# 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 -- 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@ 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= --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`