{ "meta": { "title": "Fail2Ban | ProxMenux Documentation", "description": "Install Fail2Ban with three jails tuned for Proxmox (SSH aggressive, Proxmox UI 8006, ProxMenux Monitor 8008). Includes the journald MaxLevelStore fix, custom log services for reliability, auto-detected nftables/iptables backend and SSH MaxAuthTries hardening.", "ogTitle": "Fail2Ban | ProxMenux Documentation", "ogDescription": "Brute-force protection for SSH and Proxmox web UIs, with Proxmox-specific journald and backend fixes." }, "header": { "title": "Fail2Ban", "description": "Installs Fail2Ban with a Proxmox-specific configuration: three jails (SSH aggressive, Proxmox UI on port 8006, ProxMenux Monitor on port 8008 + reverse proxy), a journald log-level fix so SSH auth events are actually stored, two journal-to-file logger services that work around a known Fail2Ban systemd-backend issue, auto-detected firewall backend and SSH MaxAuthTries hardening.", "section": "Security" }, "intro": { "title": "What this does", "body": "Installs Fail2Ban from the Debian repos and writes a complete Proxmox-tuned configuration that protects three attack surfaces (SSH, Proxmox UI, ProxMenux Monitor) out of the box. Detects an existing install and offers a manage menu (reinstall / remove) instead of re-running the installer." }, "firstLaunch": { "heading": "First-launch dialog", "body": "On a host without Fail2Ban, the script shows a confirmation dialog summarising everything it's about to install and configure. Cancel exits without changes; confirm starts the install flow.", "imageAlt": "Fail2Ban install confirmation dialog listing the three jails, journald fix and SSH hardening" }, "jails": { "heading": "The three jails", "headerJail": "Jail", "headerProtects": "Protects", "headerRetries": "Retries / Window", "headerBan": "Ban time", "rows": [ { "jail": "[sshd]", "protects": "SSH (aggressive mode — covers ddos, mode, normal)", "retries": "2 / 60 min", "ban": "9 hours" }, { "jail": "[proxmox]", "protects": "Proxmox web UI (port 8006)", "retries": "3 / 10 min", "ban": "1 hour" }, { "jail": "[proxmenux]", "protects": "ProxMenux Monitor (port 8008 + http/https reverse proxy)", "retries": "3 / 10 min", "ban": "1 hour" } ], "outro": "Global defaults from jail.local: ignoreip = 127.0.0.1/8 ::1, ignoreself = true, bantime = 86400 (24h fallback for jails that don't override it), maxretry = 2, findtime = 1800." }, "journald": { "heading": "Why the journald fix matters", "intro": "Proxmox ships /etc/systemd/journald.conf with MaxLevelStore=warning. journald drops every log message below warning before storing it. SSH and PAM emit auth failures at info / notice levels, so:", "diagram": { "sshLabel": "SSH / PAM", "sshDetail": "auth failure\n(info / notice level)", "journaldLabel": "journald", "journaldDetail": "MaxLevelStore=warning\n→ event silently dropped", "fail2banLabel": "Fail2Ban", "fail2banDetail": "sees nothing\n→ never bans anything", "arrowLabel": "default Proxmox" }, "afterDiagram": "The installer detects this and writes a drop-in at /etc/systemd/journald.conf.d/proxmenux-loglevel.conf raising both MaxLevelStore and MaxLevelSyslog to info:", "code": "[Journal]\nMaxLevelStore=info\nMaxLevelSyslog=info", "outro": "Then restarts systemd-journald. The drop-in is removed on uninstall, restoring the original Proxmox default." }, "loggers": { "heading": "Why two custom logger services", "intro1": "Fail2Ban can read directly from the systemd journal (backend = systemd), but on Proxmox this backend has known reliability issues with pvedaemon worker processes (auth events appear in the journal but Fail2Ban doesn't always pick them up) and intermittently with sshd.", "intro2": "The workaround: ProxMenux creates two tiny systemd services that journalctl -f the relevant units and append every line to a file. Fail2Ban then reads those files with the rock-solid backend = auto (file mode):", "headerService": "Service", "headerSource": "Source unit", "headerOutput": "Output file", "rows": [ { "service": "proxmox-auth-logger.service", "source": "pvedaemon.service", "output": "/var/log/proxmox-auth.log" }, { "service": "ssh-auth-logger.service", "source": "ssh.service", "output": "/var/log/ssh-auth.log" } ], "outro": "Both services are declared PartOf=fail2ban.service so they restart with Fail2Ban and stop with it. Mode 640 owned by root:adm on the log files. The third log used by the [proxmenux] jail (/var/log/proxmenux-auth.log) is written directly by the ProxMenux Monitor Flask app — no logger service needed for that one." }, "backend": { "heading": "Firewall backend auto-detection", "intro": "The installer probes the host: if nft list ruleset works, it picks nftables as the ban action. Otherwise it falls back to iptables-multiport / iptables-allports. The choice is written into jail.local:", "code": "# nftables host\nbanaction = nftables\nbanaction_allports = nftables[type=allports]\n\n# iptables host (fallback)\nbanaction = iptables-multiport\nbanaction_allports = iptables-allports" }, "hardening": { "heading": "SSH hardening: MaxAuthTries", "intro": "Lynis control SSH-7408 recommends MaxAuthTries 3 in sshd_config. With Fail2Ban's maxretry = 2 on the SSH jail, a malicious client never reaches three attempts anyway — but the explicit setting satisfies the audit and adds defence in depth (e.g. if Fail2Ban is stopped for maintenance).", "installerIntro": "The installer:", "items": [ "Reads the current MaxAuthTries value (or defaults to 6 if absent).", "Saves it to /usr/local/share/proxmenux/sshd_maxauthtries_backup.", "Edits sshd_config in-place — replaces existing line, uncomments commented line, or appends.", "Reloads sshd (reload, not restart, to keep existing sessions alive)." ], "outro": "On uninstall, the saved original value is restored and sshd is reloaded again." }, "manage": { "heading": "Manage an existing install", "intro": "If Fail2Ban is already installed when you open the menu, the script detects it and shows a manage menu instead of re-running the installer:", "headerAction": "Action", "headerWhat": "What it does", "rows": [ { "action": "Reinstall", "what": "Re-runs the full installer — rewrites all jails with the current ProxMenux defaults. Use this after a ProxMenux update bumps the recommended values." }, { "action": "Remove", "what": "Stops fail2ban and the two logger services, purges the apt package, removes all jail / filter files, deletes the journald drop-in (restoring the Proxmox default), and restores the original SSH MaxAuthTries." } ] }, "verify": { "heading": "Verify it's working", "intro": "After installation, useful commands from the host:", "code": "# Service status and version\nsystemctl status fail2ban\nfail2ban-client --version\n\n# All active jails\nfail2ban-client status\n\n# Detail on one jail\nfail2ban-client status sshd\nfail2ban-client status proxmox\nfail2ban-client status proxmenux\n\n# Currently banned IPs in a jail\nfail2ban-client status sshd | grep \"Banned IP\"\n\n# Manually unban an IP (use this if you ban yourself)\nfail2ban-client unban 192.0.2.10\n\n# Tail the auth logs Fail2Ban watches\ntail -f /var/log/ssh-auth.log\ntail -f /var/log/proxmox-auth.log\ntail -f /var/log/fail2ban.log" }, "troubleshoot": { "heading": "Troubleshooting", "neverBansTitle": "Fail2Ban runs but never bans anything", "neverBansBody": "Check the auth log files actually receive entries: tail -f /var/log/ssh-auth.log and try a wrong-password SSH attempt from a different machine. If the log stays empty, the logger service is not running: systemctl status ssh-auth-logger.service. If it's active but the log is empty, check that the journald drop-in took effect: journalctl --dump-catalog | head — events at info level should be visible.", "monitorEmptyTitle": "ProxMenux Monitor jail has no entries even after failed logins", "monitorEmptyBody": "The [proxmenux] jail reads /var/log/proxmenux-auth.log, which is written by the ProxMenux Monitor Flask app — not by a journald logger. If you don't run the Monitor, the file stays empty and the jail never fires. That's expected; the jail config is harmless. If you do run the Monitor and the log is empty, check the Flask logging config.", "selfBanTitle": "I banned myself", "selfBanIntro": "From a console / IPMI / iKVM (or another whitelisted IP):", "selfBanCode": "fail2ban-client unban '<'YOUR_IP'>'\n\n# To prevent it next time, edit /etc/fail2ban/jail.local and add your IP:\nignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.42\n\n# Then reload\nfail2ban-client reload", "aptFailTitle": "apt-get fails: ''Unable to locate package fail2ban''", "aptFailBody": "The host is missing the Debian repos (common on barebones Proxmox installs). The installer detects this and writes /etc/apt/sources.list.d/debian.sources with the right codename (bookworm / trixie) before retrying. If it still fails, check /etc/os-release for VERSION_CODENAME and confirm the repo URL is reachable.", "lockoutTitle": "SSH locks me out after install", "lockoutBody": "The installer sets MaxAuthTries=3. If your password manager / agent retries multiple keys, you may exceed that on a single connection attempt. Limit the keys offered: ssh -o IdentitiesOnly=yes -i ~/.ssh/specific_key user@host. Or temporarily raise MaxAuthTries in sshd_config while you debug." }, "files": { "heading": "Files written", "code": "/etc/fail2ban/jail.local # global defaults + [sshd]\n/etc/fail2ban/jail.d/proxmox.conf # [proxmox]\n/etc/fail2ban/jail.d/proxmenux.conf # [proxmenux]\n/etc/fail2ban/filter.d/proxmox.conf # auth-failure regex for pvedaemon\n/etc/fail2ban/filter.d/proxmenux.conf # auth-failure regex for Monitor\n/etc/systemd/system/proxmox-auth-logger.service # journal → file (pvedaemon)\n/etc/systemd/system/ssh-auth-logger.service # journal → file (sshd)\n/etc/systemd/journald.conf.d/proxmenux-loglevel.conf # MaxLevelStore=info\n/etc/ssh/sshd_config # MaxAuthTries=3 (in-place edit)\n/var/log/proxmox-auth.log # written by logger service\n/var/log/ssh-auth.log # written by logger service\n/var/log/proxmenux-auth.log # written by Monitor Flask app\n/usr/local/share/proxmenux/sshd_maxauthtries_backup # for restore on uninstall" }, "related": { "heading": "Related", "monitorLabel": "ProxMenux Monitor → Security tab", "monitorTail": " — same install reachable from the dashboard, plus live jail status, banned IPs and per-jail retry / ban-time tuning from the browser.", "lynisLabel": "Lynis", "lynisTail": " — run a security audit before/after to confirm the SSH-7408 control is satisfied.", "securityLabel": "Security overview", "securityTail": " — back to the section overview." } }