Security Hardening
Overview
This chapter locks down the two main attack surfaces on a public-facing Proxmox host: the SSH port and the Proxmox web interface on port 8006. UFW handles inbound traffic filtering, and fail2ban sits in front of both services to automatically ban IPs that repeatedly fail authentication. The Proxmox built-in firewall is explicitly disabled to avoid conflicts with UFW and keep all firewall rules in one place.
Step 1 — Disable the Proxmox Built-in Firewall
Running both the Proxmox firewall and UFW simultaneously is one of those configurations that works fine until it doesn't — and when it breaks, the interaction between the two rule sets is genuinely hard to reason about. Pick one firewall and disable the other completely. UFW wins here because the rules live in plain text files you can audit, version-control, and restore after a rebuild.
Proxmox ships with its own firewall that operates independently of the OS-level firewall. Having both active simultaneously leads to unpredictable rule ordering and hard-to-debug connectivity issues. Since UFW will handle all filtering, the Proxmox firewall must be explicitly disabled.
pvesh set /cluster/firewall/options --enable 0
Verify it is off:
pvesh get /cluster/firewall/options
The output should show "enable" : 0. If the key is absent, the firewall is already disabled — the command above makes that state explicit and persistent.
Step 2 — Configure UFW
Install UFW if not already present:
apt-get install -y ufw
Set the default policies — deny all incoming, allow all outgoing:
ufw default deny incoming
ufw default allow outgoing
Allow SSH and the Proxmox web interface:
ufw allow 22/tcp
ufw allow 8006/tcp
Enable UFW:
ufw --force enable
Confirm the active rules:
ufw status verbose
The output should show Status: active with rules allowing 22/tcp and 8006/tcp and a default incoming policy of deny.
Step 3 — Create the Proxmox fail2ban Filter
fail2ban works by scanning log files for patterns matching failed authentication attempts. Proxmox logs authentication failures to /var/log/syslog via pvedaemon. The log line looks like this:
pvedaemon[253152]: authentication failure; rhost=192.168.1.1 user=root@pam msg=Authentication failure
Create /etc/fail2ban/filter.d/proxmox.conf:
[Definition]
failregex = pvedaemon\[.*\]: authentication failure; rhost=<HOST>
ignoreregex =
The <HOST> placeholder is fail2ban’s built-in token — it matches and extracts the offending IP address from the rhost= field automatically.
Test the filter against the live log before activating the jail:
fail2ban-regex /var/log/syslog /etc/fail2ban/filter.d/proxmox.conf
If there are existing failed login attempts in the log, they will appear under Lines: ... matched. Zero matches on a server with no prior login attempts is expected and correct.
Step 4 — Configure fail2ban Jails
The values here — 3 retries, 5-minute window, 1-hour ban — are a reasonable starting point for a server that isn't publicly advertised. If you're running anything with a public DNS name, expect the SSH ban list to grow quickly. You can check it at any time with fail2ban-client status sshd. Seeing bans accumulate means it's working, not that something is wrong.
Create /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 3600
findtime = 300
maxretry = 3
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
[proxmox]
enabled = true
port = 8006
filter = proxmox
logpath = /var/log/syslog
| Setting | Value | Meaning |
|---|---|---|
bantime | 3600 | Ban duration in seconds — 1 hour |
findtime | 300 | Window in seconds to count failures — 5 minutes |
maxretry | 3 | Failed attempts within the window before banning |
Restart fail2ban to apply:
systemctl restart fail2ban
Confirm both jails are active:
fail2ban-client status
Check each jail individually:
fail2ban-client status sshd
fail2ban-client status proxmox
Both should show Currently banned: 0 and Total banned: 0 on a fresh system.
No comments to display
No comments to display