Security & Vulnerability Gaps
Known security findings, mitigations in place, and areas for improvement. Cross-references the full audit in homelab-notes/deep-dive.md.
Active Findings
SG-001 — qBittorrent CSRF bypass required for reverse proxy
Status: Mitigated Affected services: seedbox.eva-00.network, normal.eva-00.network Date identified: 2026-03-30
Issue: qBittorrent 5.x CSRF protection fires before AuthSubnetWhitelist evaluation. When oauth2-proxy forwards authenticated requests, the Origin/Referer headers don't match what qBittorrent expects, causing a 401 even though the client IP is whitelisted.
Mitigation: CSRFProtection, ClickjackingProtection, and HostHeaderValidation are disabled in qBittorrent's config. These checks are redundant because:
- All external access goes through oauth2-proxy (PocketID SSO) on LXC 119 (infra-apps)
AuthSubnetWhitelist=192.168.1.118/32, 192.168.1.119/32restricts unauthenticated access to qbitwebui (LXC 118) and oauth2-proxy (LXC 119) only- No qBittorrent ports are exposed to the internet
Residual risk: Low. Only processes on LXC 118 (tools) and LXC 119 (infra-apps) can reach qBittorrent without auth. These are dedicated single-purpose LXCs with limited attack surface.
References:
- services/seedbox/fix-webui-auth.py — the script that applies all settings
- docs/services/pocketid/oauth2-proxy-integration.md — step 7 documents the bypass
- deep-dive.md Section 2 — Security Best Practices — general reverse proxy security guidance
SG-002 — qbitwebui API exposed on localhost without authentication
Status: Accepted Affected services: qbit.eva-00.network Date identified: 2026-03-30
Issue: qbitwebui runs with DISABLE_AUTH=true on LXC 118 (tools). The API allows listing, adding, and deleting qBittorrent instances without credentials.
Mitigation: External access goes through oauth2-proxy on LXC 119 (PocketID SSO required). The qbitwebui port is only reachable from the LAN.
Residual risk: Low. Shell access to LXC 118 is the prerequisite. Instance credentials are AES-256-GCM encrypted in the SQLite database, but the encryption key is in the .env file on the same host.
SG-003 — Flat network — no segmentation
Status: Open Affected services: All Date identified: 2026-03-24
Issue: All LXCs and the Proxmox host share a single subnet (192.168.1.0/24). Any compromised container can reach any other service directly.
Impact: A compromised service (e.g., a vulnerable web app) could scan and attack other services on the same subnet, access Vault directly, or pivot to Proxmox.
Recommendation: Implement VLAN segmentation. Start with isolating Vault (high-value target) and internet-facing LXCs onto separate VLANs with firewall rules.
Reference: deep-dive.md Section 7 — Network Hardening — full VLAN strategy and zero-trust options
SG-004 — No intrusion detection/prevention
Status: Open Affected services: All internet-facing services Date identified: 2026-03-24
Issue: No IDS/IPS is deployed. Brute force attacks on oauth2-proxy, PocketID, or SSH are not detected or blocked. Caddy serves requests without rate limiting.
Recommendation: Deploy CrowdSec with the Caddy bouncer module. This provides community-shared blocklists, brute force detection, and automated blocking.
Reference: deep-dive.md Section 11 — CrowdSec + Caddy Implementation Guide
SG-005 — No security headers on Caddy
Status: Open Affected services: All .eva-00.network Date identified:* 2026-03-24
Issue: Caddy does not set security headers (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy). This leaves services vulnerable to clickjacking, MIME sniffing, and other browser-based attacks.
Recommendation: Add a global (security_headers) snippet to the Caddyfile.
Reference: deep-dive.md Section 7.2 — Caddy Security Headers — exact Caddyfile config provided
SG-006 — Hardcoded LXC password in playbooks
Status: Open Affected services: vault.yml, filedump.yml Date identified: 2026-03-24
Issue: LXC creation uses --password bigboydata, exposed in Git history. SSH key-only access is configured, so the password is a secondary risk, but it's still a bad practice.
Recommendation: Generate a random password at creation time and discard it. SSH keys are already the primary access method.
Reference: deep-dive.md Section 1.1 — Hardcoded LXC Password
SG-007 — Shell/JSON injection in Ansible playbooks
Status: Open Affected services: forgejo.yml, n8n.yml Date identified: 2026-03-24
Issue: Vault secrets injected into shell commands and JSON strings without escaping. A secret containing ', ", or \ could break or inject commands.
Recommendation: Use | quote for shell variables and | to_json for JSON embedding.
Reference: deep-dive.md Sections 1.2, 1.3, 1.4 — specific file paths and fix patterns
SG-008 — No Vault audit logging
Status: Open Affected services: Vault (LXC 106) Date identified: 2026-03-24
Issue: Vault does not have audit logging enabled. There is no record of who accessed which secret, when. If credentials are leaked, there is no trail to investigate.
Recommendation: Enable Vault's file audit device and ship logs to Loki via Alloy.
Reference: deep-dive.md Section 2.7 — Audit Logging
SG-009 — Containers run as root
Status: Open Affected services: Most Docker services across all LXCs Date identified: 2026-03-24
Issue: Most containers run as root inside the container. A container escape vulnerability could grant root access to the Docker host.
Recommendation: Add user: "1000:1000" and security_opt: [no-new-privileges:true] where the service supports it. Apply incrementally — not all services support non-root.
Reference: deep-dive.md Section 2.3 — Container Security Hardening
SG-010 — No image vulnerability scanning
Status: Open Affected services: All Docker services Date identified: 2026-03-24
Issue: Docker images are pulled and deployed without CVE scanning. A compromised or vulnerable upstream image could introduce exploits.
Recommendation: Add Trivy scanning to deploy workflows. Fail on HIGH/CRITICAL CVEs.
Reference: deep-dive.md Section 2.4 — Vulnerability Scanning
Closed Findings
SG-C01 — qBittorrent auth bypass was 0.0.0.0/0
Status: Fixed (2026-03-30)
Issue: fix-webui-auth.py set AuthSubnetWhitelist=0.0.0.0/0, disabling auth for all IPs. Any device on the LAN (or anything that could route to LXC 111) had full unauthenticated access to qBittorrent.
Fix: Tightened to 192.168.1.118/32, 192.168.1.119/32 (qbitwebui + oauth2-proxy only). Combined with CSRF/clickjacking/host-validation disables (SG-001) to maintain reverse proxy compatibility.