Files
OpenVPN-Monitoring-Simple/DOCS/Changes/2026-09-30_Security_Hardening.md
iclaoudezinandClaude Sonnet 5.5 9b2882d5f4 Docs: concise README, split deployment guides, add change records
- README: short overview, quick start, config table and links.
- DOCS/General: Deployment_Docker.md and Deployment_Native.md (system
  services, HTTPS, host hardening); refresh Index.md.
- DOCS/Changes: security hardening, admin username change and egress
  via Hysteria2 with results and verification.
- Drop mentions of the built-in admin/password account.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 12:12:09 +00:00

3.0 KiB

Security hardening (2026-09-30)

Scope: a native (OpenRC) deployment of this suite on an internet-facing host. Findings from a review of exposed services, the fixes and their verification.

Findings and results

# Severity Finding Fix Result
1 Critical SSH accepted passwords for root (PasswordAuthentication yes, cloud-init drop-in overrode the main config); the host was already being brute-forced /etc/ssh/sshd_config.d/00-hardening.conf: PasswordAuthentication no, KbdInteractiveAuthentication no, PermitRootLogin prohibit-password, MaxAuthTries 3, LoginGraceTime 30 key login works; password login → Permission denied (publickey)
2 High Path traversal in Profiler: username was an unvalidated string used in client-config/<username>.ovpn and as an easyrsa argument, service runs as root pattern ^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$ in schemas.py; validate_username() in services/pki.py; realpath containment checks in routers/profiles.py and services/generator.py ../../tmp/pwn, a/b, .., -x → 422, no file created; valid profile create/revoke works
3 High 2FA bypass: the temporary token issued after the password step was accepted by every protected route token_required (Flask) and verify_token (Profiler) reject tokens with is_2fa_pending; only /api/auth/verify-2fa uses them temp token → 401 on /api/v1/user/me, /profiles-api/config, change-username; full token → OK
4 Medium enable_2fa logged the OTP and TOTP secret log line reduced to "Attempting 2FA activation" no secrets in logs
5 Medium CORS * with credentials on both APIs allowed origin restricted to the panel origin; Profiler methods/headers narrowed foreign Origin gets no Access-Control-Allow-Origin
6 Medium No brute-force throttling outside the app rate limit fail2ban jails sshd, sshd-ddos, ovpmon-login (401/429/503 on POST /api/auth/login); admin IP in ignoreip jails active, bans observed for SSH scanners
7 Medium Panel served over plain HTTP Nginx TLS on the panel port (TLS 1.2/1.3, HSTS, nosniff, X-Frame-Options, no-store, login limit_req 5 r/min, HTTP→HTTPS redirect via error_page 497) HTTPS 200, TLS 1.1 rejected, rate limit returns 503

Checked, no issue: JWT algorithm pinned to HS256, random 32-byte secret; SQL f-strings only use internal table/column names; backends bound to 127.0.0.1; Nginx workers unprivileged.

Residual risks

  • Self-signed certificate: verify the fingerprint on first visit; use a CA-issued certificate when a domain exists.
  • The APIs run as root; split privileged OpenVPN/PKI operations into a separate helper.
  • Enable 2FA for the admin account.
  • Changes were applied in place on the host; keep them in the repository (see the commit "Harden auth and API").

Rollback

Backups were taken on the host before each change: sshd_config, ovpmon.conf (Nginx), application files in /root/app-bak/, previous UI build /var/www/ovpmon.bak.