Skip to main content

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
SettingValueMeaning
bantime3600Ban duration in seconds — 1 hour
findtime300Window in seconds to count failures — 5 minutes
maxretry3Failed 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.