Some checks failed
CI / skip-ci-check (pull_request) Successful in 7s
CI / lint-and-test (pull_request) Successful in 12s
CI / ansible-validation (pull_request) Failing after 5s
CI / secret-scanning (pull_request) Successful in 6s
CI / dependency-scan (pull_request) Successful in 8s
CI / sast-scan (pull_request) Failing after 6s
CI / license-check (pull_request) Successful in 10s
CI / vault-check (pull_request) Failing after 5s
CI / playbook-test (pull_request) Failing after 6s
CI / container-scan (pull_request) Failing after 6s
CI / sonar-analysis (pull_request) Failing after 3s
CI / workflow-summary (pull_request) Successful in 5s
Use ansible.builtin.su, spaces in caddy blockinfile, relax MD060/MD036 and line length for homelab documentation tables. Co-authored-by: Cursor <cursoragent@cursor.com>
265 lines
9.3 KiB
Markdown
265 lines
9.3 KiB
Markdown
# Security Audit Report
|
||
|
||
**Date:** 2026-05-20
|
||
**Auditor:** Automated read-only scan (`scripts/security-audit-*.sh`)
|
||
**Scope:** Proxmox nodes `pve201` (10.0.10.201) and `pve10` (10.0.10.10), all LXCs via `pct exec`, SSH deep-dive on hypervisors.
|
||
|
||
**Repo baseline** (`roles/ssh/defaults/main.yml`): `PermitRootLogin prohibit-password`, `PasswordAuthentication no`, UFW enabled.
|
||
|
||
---
|
||
|
||
## Executive summary
|
||
|
||
| Area | Critical | High | Medium |
|
||
|------|----------|------|--------|
|
||
| Hypervisors (201, 10) | 2 | 4 | 2 |
|
||
| LXCs on 201 (10 running) | 0 | 10 | 8 |
|
||
| LXCs on 10 (3 running) | 0 | 3 | 3 |
|
||
|
||
**Top priorities**
|
||
|
||
1. Harden **SSH on both Proxmox hosts** (root + passwords currently allowed).
|
||
2. Restrict **Proxmox API/UI port 8006** to admin IPs.
|
||
3. Disable **password SSH on all LXCs**; deploy keys + `make copy-ssh-keys` for inventory IPs.
|
||
4. Patch hosts with **40–105** pending apt upgrades (hypervisors worst).
|
||
5. Put **HTTP services** (8080, 8000, qBit, etc.) behind reverse proxy + TLS or bind to internal IPs.
|
||
|
||
---
|
||
|
||
## Proxmox hypervisors
|
||
|
||
### pve201 — 10.0.10.201 (`pve`)
|
||
|
||
| Resource | Status |
|
||
|----------|--------|
|
||
| OS | Debian 12, PVE 8.4.16, kernel 6.8.12-18-pve |
|
||
| RAM free | ~2.5 GB / 126 GB (**critical**) |
|
||
| Pending apt | **105** |
|
||
| UFW / fail2ban / unattended-upgrades | **None** |
|
||
|
||
#### SSH audit (dedicated)
|
||
|
||
| Setting | Current | Target |
|
||
|---------|---------|--------|
|
||
| `permitrootlogin` | **yes** | `prohibit-password` |
|
||
| `passwordauthentication` | **yes** | `no` |
|
||
| `pubkeyauthentication` | yes | yes |
|
||
| `maxauthtries` | 6 | 3–4 |
|
||
| `x11forwarding` | yes | no (on servers) |
|
||
| Root keys | 3 keys in `authorized_keys` | audit/remove unused |
|
||
|
||
#### Exposed services
|
||
|
||
| Port | Service | Risk |
|
||
|------|---------|------|
|
||
| 22 | SSH | Brute-force (no fail2ban) |
|
||
| 8006 | Proxmox API/UI | **Critical** — full cluster control |
|
||
| 3128 | spiceproxy | Medium |
|
||
| 111 | rpcbind | Low — reduce exposure |
|
||
|
||
#### Fixes (pve201)
|
||
|
||
```bash
|
||
# 1) SSH — prefer Ansible after limiting to your IP
|
||
make copy-ssh-key HOST=pve201 # if needed
|
||
# Manual quick fix on host:
|
||
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
|
||
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||
sshd -t && systemctl reload sshd
|
||
|
||
# 2) Proxmox firewall — Datacenter → Firewall → restrict 8006 to 10.0.10.0/24 or admin IP
|
||
# Or iptables on host for port 8006
|
||
|
||
# 3) fail2ban
|
||
apt install fail2ban -y
|
||
systemctl enable --now fail2ban
|
||
|
||
# 4) Auto security updates
|
||
apt install unattended-upgrades apt-listchanges -y
|
||
dpkg-reconfigure -plow unattended-upgrades
|
||
|
||
# 5) Patch
|
||
apt update && apt upgrade -y
|
||
```
|
||
|
||
**Ansible (when ready):** add `pve201` / `pve10` to a `proxmox` group play with `roles/ssh` + `roles/monitoring_server` (fail2ban).
|
||
Do **not** lock yourself out — test with second session first.
|
||
|
||
---
|
||
|
||
### pve10 — 10.0.10.10 (`PVENAS`)
|
||
|
||
| Resource | Status |
|
||
|----------|--------|
|
||
| OS | Debian 13 (trixie), PVE, kernel 6.17.13-3-pve |
|
||
| Load | **~30** on 24 CPUs (overloaded) |
|
||
| Pending apt | **92** |
|
||
| UFW / fail2ban / unattended-upgrades | **None** |
|
||
| ZFS `NAS.SP00` | **inactive** (I/O suspended) |
|
||
| PBS `PVEBUVD00` → 10.0.10.200:8007 | **unreachable** |
|
||
|
||
#### SSH audit (dedicated)
|
||
|
||
Same as pve201: `permitrootlogin yes`, `passwordauthentication yes`, 3 root authorized_keys.
|
||
|
||
#### Exposed services
|
||
|
||
| Port | Service | Risk |
|
||
|------|---------|------|
|
||
| 22 | SSH | High |
|
||
| 8006 | Proxmox API/UI | **Critical** |
|
||
| 2049, mountd, statd | NFS/RPC | High on LAN |
|
||
| 3128 | spiceproxy | Medium |
|
||
|
||
#### Fixes (pve10)
|
||
|
||
Same SSH / fail2ban / unattended-upgrades / patch steps as pve201.
|
||
|
||
Additional:
|
||
|
||
```bash
|
||
# Investigate ZFS pool
|
||
zpool status NAS.SP00
|
||
# Fix PBS connectivity or remove stale datastore from Proxmox UI
|
||
```
|
||
|
||
---
|
||
|
||
## LXCs on pve201 (via `pct exec`)
|
||
|
||
| VMID | Name | IP | Status | SSH root | Password auth | UFW | fail2ban | Upgrades | Public services |
|
||
|------|------|-----|--------|----------|---------------|-----|----------|----------|-----------------|
|
||
| 301 | vikunja-debian | 10.0.10.159 | running | without-password | **yes** | no | no | 0 | **3456**, 22 |
|
||
| 302 | qbit-debian | 10.0.10.91 | running | without-password | **yes** | no | no | 0 | **8080** (qBit), 22 |
|
||
| 303 | searchXNG-debian | 10.0.10.70 | running | without-password | **yes** | no | no | **83** | **8080**, 22 |
|
||
| 304 | wireguard-debian | 10.0.10.192 | running | without-password | **yes** | no | no | 0 | 22 |
|
||
| 305 | kuma-debian | 10.0.10.197 | **stopped** | — | — | — | — | — | replaced by LXC 218 |
|
||
| 306 | portfolio | — | **destroyed** | — | — | — | — | — | migrated → pve10 LXC **219** @ `10.0.10.106` (purged 2026-05-22) |
|
||
| 307 | jobber-delian | 10.0.10.178 | running | without-password | **yes** | no | no | **83** | **3005**, 22 |
|
||
| 308 | stirling-pdf | 10.0.10.43 | running | without-password | **yes** | no | no | 0 | **8080**, 22 |
|
||
| 9001 | pote-dev | 10.0.10.114 | **stopped** | — | — | — | — | — | — |
|
||
| 9101 | punimTagFE-dev | 10.0.10.121 | running | without-password | **yes** | **active** | no | **89** | **8000**, 111, 22 |
|
||
| 9401 | mirrormatch-dev | 10.0.10.141 | **stopped** | — | — | — | — | — | — |
|
||
|
||
**Inventory mapping:** `vikanjans` → 159, `qBittorrent` → 91, `punimTag` app → 121.
|
||
|
||
### Common LXC issues (pve201)
|
||
|
||
| Issue | Severity | Fix |
|
||
|-------|----------|-----|
|
||
| `passwordauthentication yes` on all LXCs | High | Set `PasswordAuthentication no` in `/etc/ssh/sshd_config`, reload sshd |
|
||
| No fail2ban | High | Install fail2ban or rely on Proxmox FW + LAN segmentation |
|
||
| Apps on `0.0.0.0:8080` / 8000 / 3456 | High | Bind to localhost + Caddy, or restrict via Proxmox guest firewall (`firewall=1` on net0 — enable rules) |
|
||
| 79–89 pending upgrades on several CTs | Medium | `pct exec <id> -- apt update && apt upgrade -y` |
|
||
| Stopped dev CTs (9001, 9401) | Low | Start when needed or keep stopped to reduce attack surface |
|
||
|
||
### Per-LXC fixes (pve201)
|
||
|
||
```bash
|
||
# Example: harden + patch vikunja (301) from Proxmox host
|
||
pct exec 301 -- sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||
pct exec 301 -- systemctl reload ssh
|
||
|
||
# Patch container
|
||
pct exec 303 -- bash -c 'apt update && apt upgrade -y'
|
||
|
||
# Copy your SSH key (from Mac, once password/key works)
|
||
make copy-ssh-key HOST=vikanjans # 10.0.10.159
|
||
make copy-ssh-key HOST=qBittorrent # 10.0.10.91
|
||
```
|
||
|
||
**punimTagFE-dev (9101):** Only LXC with **UFW active** — extend rules to deny inbound except 22 from admin subnet; still disable password auth.
|
||
|
||
---
|
||
|
||
## LXCs on pve10 (via `pct exec`)
|
||
|
||
| VMID | Name | IP | Status | SSH root | Password auth | UFW | fail2ban | Upgrades | Public services |
|
||
|------|------|-----|--------|----------|---------------|-----|----------|----------|-----------------|
|
||
| 210 | cal | 10.0.10.228 | running | without-password | **yes** | no | no | 0 | **3000**, 22 |
|
||
| 215 | caseware | 10.0.10.105 | running | without-password | **yes** | no | no | **40** | **80** (nginx), 22 |
|
||
| 216 | auto | 10.0.10.59 | running | without-password | **yes** | no | no | **40** | **80** (nginx), 22 |
|
||
|
||
**Inventory mapping:** `caseware` → 105, `auto` → 59.
|
||
|
||
### Fixes (pve10 LXCs)
|
||
|
||
```bash
|
||
# SSH harden caseware (215)
|
||
pct exec 215 -- sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||
pct exec 215 -- systemctl reload sshd
|
||
|
||
# Patch
|
||
pct exec 215 -- apt update && apt upgrade -y
|
||
pct exec 216 -- apt update && apt upgrade -y
|
||
|
||
# Deploy keys from Mac
|
||
make copy-ssh-key HOST=caseware
|
||
make copy-ssh-key HOST=auto
|
||
```
|
||
|
||
**HTTP port 80 on caseware/auto:** Ensure TLS termination on Caddy (inventory host `caddy` 10.0.10.50) and no plain HTTP from WAN if exposed.
|
||
|
||
---
|
||
|
||
## SSH hardening checklist (all Linux targets)
|
||
|
||
Use this order to avoid lockout:
|
||
|
||
1. Confirm your key works: `ssh -o BatchMode=yes root@<ip> true`
|
||
2. Set `PasswordAuthentication no`
|
||
3. Set `PermitRootLogin prohibit-password` (LXCs already `without-password` — equivalent for keys-only)
|
||
4. `sshd -t && systemctl reload sshd`
|
||
5. Open **second terminal** and test before closing first
|
||
6. Optional: change SSH port, `MaxAuthTries 4`, disable `X11Forwarding`
|
||
|
||
**Ansible alignment:**
|
||
|
||
```bash
|
||
# After keys on host
|
||
make dev HOST=<hostname> --tags security
|
||
# or role ssh via playbooks that include roles/ssh
|
||
```
|
||
|
||
---
|
||
|
||
## Re-run audits
|
||
|
||
```bash
|
||
# Hypervisor full audit
|
||
ssh root@10.0.10.201 'bash -s' < scripts/security-audit-remote.sh
|
||
ssh root@10.0.10.10 'bash -s' < scripts/security-audit-remote.sh
|
||
|
||
# Hypervisor SSH-only
|
||
ssh root@10.0.10.201 'bash -s' < scripts/security-audit-ssh.sh
|
||
|
||
# All LXCs on a node
|
||
ssh root@10.0.10.201 'bash -s' < scripts/security-audit-lxc-via-pve.sh
|
||
ssh root@10.0.10.10 'bash -s' < scripts/security-audit-lxc-via-pve.sh
|
||
```
|
||
|
||
---
|
||
|
||
## Tracking
|
||
|
||
| Item | Owner | Status |
|
||
|------|-------|--------|
|
||
| SSH harden pve201 | | ☐ |
|
||
| SSH harden pve10 | | ☐ |
|
||
| Restrict 8006 on both nodes | | ☐ |
|
||
| fail2ban on hypervisors | | ☐ |
|
||
| Patch pve201 / pve10 | | ☐ |
|
||
| Disable password SSH on all LXCs | | ☐ |
|
||
| `copy-ssh-keys` for inventory | | ☐ |
|
||
| TLS for :80/:8080 services | | ☐ |
|
||
| Fix ZFS NAS.SP00 on pve10 | | ☐ |
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- **[Security remediation plan](security-remediation-plan.md)** — phased fixes (critical → low) and login model
|
||
- [Security hardening guide](security.md)
|
||
- [SECURITY_HARDENING_PLAN.md](../SECURITY_HARDENING_PLAN.md)
|
||
- Role defaults: `roles/ssh/defaults/main.yml`
|