Proxmox Security Hardening: SSH Keys, Firewall, and Locking Down a New Cluster

Essential security configuration for a new Proxmox cluster — SSH key-based authentication, disabling password login, configuring the built-in firewall, securing the web UI, and the audit steps to verify nothing is exposed that shouldn't be.

A fresh Proxmox install is accessible from your entire LAN with a password. That’s convenient for getting started and a security liability for anything beyond that. This post covers the hardening steps that should happen before you start deploying workloads — SSH keys, the Proxmox firewall, certificate management, and the checks that verify your changes worked.

Proxmox Security Hardening — SSH Keys, Firewall & Locking Down a Cluster — video walkthrough
Proxmox Security Hardening — SSH Keys, Firewall & Locking Down a Cluster — video walkthrough

This is part of the Building a Homelab series. Your cluster should already be formed — see Forming a Proxmox Cluster first.


Why a homelab needs security hardening

“It’s just on my home network” is a reasonable starting point, but it’s not the whole picture:

  • Any device on your home network — phones, smart TVs, game consoles — can potentially reach your Proxmox web UI and attempt authentication
  • If Tailscale is installed, your cluster is reachable from anywhere you have a Tailscale client — which means a stolen phone is a path to your cluster
  • Bad habits built on an “it’s just a homelab” mindset carry forward to production environments

The hardening in this post takes about 30 minutes and significantly raises the bar for unauthorized access without making legitimate administration harder. If you want an independent checklist to cross-reference, cr0x.net’s Proxmox security checklist and Virtualization Howto’s PVE 9 hardening guide cover the same ground and are worth reading alongside this one.


Task 1: Set up SSH key authentication

Password-based SSH is vulnerable to brute force attacks. SSH keys are cryptographically much stronger — a correctly generated key pair is practically impossible to brute-force.

SSH Key Authentication — How It Works① Your Laptopssh-keygen -t ed25519private keystays herepublic key.pub filessh-copy-id(one time, password)② Proxmox Node~/.ssh/authorized_keyspublic key stored hereserver remembers your key③ Every login after that:private keysigns a challengeno passwordinstant, cryptographicpublic keyverifies the signatureAccess grantedThe private key never leaves your laptop. The server only ever sees the public key, which is useless without the private key.
1Generate an SSH key pair on your laptop/desktop3 min

On your laptop (the machine you’ll administer the cluster from):

Your laptop — generate SSH key pair

# Generate an Ed25519 key (modern, fast, secure)
ssh-keygen -t ed25519 -C "homelab-admin"

# When prompted:
# - File: press Enter to accept default (~/.ssh/id_ed25519)
# - Passphrase: enter a passphrase (strongly recommended)
#   A passphrase protects the key if your laptop is stolen

This creates two files:

  • ~/.ssh/id_ed25519 — your private key (never share this, never leave it on a server)
  • ~/.ssh/id_ed25519.pub — your public key (safe to share — goes on servers)
2Copy the public key to all Proxmox nodes5 min
Your laptop — copy key to all nodes

ssh-copy-id root@10.0.0.70   # pvelab01
ssh-copy-id root@10.0.0.71   # pvelab02
ssh-copy-id root@10.0.0.72   # pvelab03
ssh-copy-id root@10.0.0.73   # pvelab04

Each ssh-copy-id command prompts for the root password (the last time you’ll need it for SSH). It appends your public key to ~/.ssh/authorized_keys on the node.

Test that key auth works:

Your laptop — verify key-based login

ssh root@10.0.0.70 "echo 'Key auth working on pvelab01'"
# Should succeed without asking for a password
3Disable password-based SSH authentication5 min

Once you’ve verified key-based login works, disable password auth on all nodes:

pvelab01 — disable SSH password auth

sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

# Verify the change
grep "PasswordAuthentication" /etc/ssh/sshd_config

# Reload SSH daemon (does not disconnect existing sessions)
systemctl reload sshd

Run this on all four nodes. After this, only SSH key authentication works — password guessing is impossible via SSH.

Do not close your current SSH session yet

Stay connected to pvelab01 in your current terminal while testing the change. Open a new terminal window and verify you can still SSH in with key auth before closing the old session. If you get locked out, you’ll need console access (Proxmox web terminal or physical keyboard).

4Disable root SSH login (optional, recommended)3 min

For maximum security, disable direct root SSH access. You’d SSH in as your admin user and sudo/su to root only when needed:

pvelab01 — disable root SSH login

# First, make sure your admin user is set up (from the install guide)
# Then disable root SSH:
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config

systemctl reload sshd
Note

The Proxmox web UI still uses root credentials for its internal communication — this doesn’t break the web UI. It only affects SSH connections.


Task 2: Configure the Proxmox firewall

The Proxmox built-in firewall operates at three levels: cluster (Datacenter), node, and VM/CT. It uses iptables/nftables under the hood, but is managed through the web UI and API.

1Create IPSETs for trusted networks5 min

Before enabling the firewall, define your trusted networks as named groups. This makes rules readable and easier to update.

In the Proxmox web UI: Datacenter → Firewall → IPSet → Create.

Set name CIDR to add Purpose
lablan 10.0.0.0/24 Your cluster subnet
homelan 192.168.1.0/24 Your home network (adjust to match yours)
tailscale 100.64.0.0/10 Tailscale address space
2Add the core firewall allow rules5 min

In Datacenter → Firewall → Rules → Add, create these rules in order:

# Direction Action Source Proto Ports Comment
1 in ACCEPT +lablan any any All traffic from cluster subnet
2 in ACCEPT +homelan tcp 22, 8006 SSH + web UI from home network
3 in ACCEPT +tailscale any any Tailscale peer access
Warning

Rules are evaluated in order. Add the most permissive (lab LAN) rule first. If you later need to block specific cluster IPs, add more specific rules above the general one.

3Enable the datacenter firewall with DROP input policy2 min

Datacenter → Firewall → Options:

  • Firewall: Yes
  • Input policy: DROP
  • Output policy: ACCEPT

Click Save.

Test immediately

Before closing the web UI, verify you can still reach it (refresh the page). Keep your SSH session open. If the page loads and SSH still works, your rules are correct.

If you lose access: SSH to any node and run pvesh set /cluster/firewall/options --enable 0 to disable the firewall from the command line.

4Enable node-level security features3 min

Select any node → Firewall → Options. Enable:

  • SMURFS: Yes — blocks SMURF amplification attacks
  • TCP flags filter: Yes — blocks malformed/crafted TCP packets

These are simple checkboxes that add additional iptables rules for common attack vectors. Enable them on all four nodes.


Task 3: Secure the web UI

1Replace the self-signed certificate (optional)15 min

By default, Proxmox uses a self-signed certificate. Browsers warn about it and connections aren’t authenticated. For a homelab, this is acceptable — the certificate still provides encryption, just not server identity verification.

If you want a trusted certificate (no browser warning), you have two options:

Option A: Let’s Encrypt via ACME (requires a public domain)

In Node → Certificates → ACME, configure your domain. This requires your Proxmox web UI to be reachable from the internet (or use DNS-01 challenge with a domain registrar API). Not recommended for a homelab unless you have a specific reason.

Option B: Add the self-signed cert to your browser’s trust store

Export the current cert from any node and import it into your browser/OS as trusted. This eliminates the warning without needing a public domain.

pvelab01 — view the current certificate

# View the Proxmox certificate details
openssl x509 -in /etc/pve/pve-ssl.pem -text -noout | grep -E "Subject|DNS|Validity|Not After"
2Enable 2FA for Proxmox web UI logins5 min

Proxmox supports TOTP (authenticator app) and WebAuthn (hardware security key) as second factors.

To enable TOTP for your admin user:

  1. Log into the Proxmox web UI
  2. Click your username in the top-right corner → My Account
  3. Under Two Factor Auth, click Add
  4. Select TOTP
  5. Scan the QR code with Google Authenticator, Bitwarden Authenticator, Aegis (recommended for Android — open source), or similar
  6. Enter a test code to confirm it’s working
  7. Click Save

From this point, logging in requires both your password and the current TOTP code. Even if someone gets your password, they can’t log into the web UI without your phone.

3Create an API token for automated access5 min

If you’re using any automation (scripts, the Flutter app, Ansible) to access Proxmox, use an API token instead of the root password. API tokens can be scoped to specific roles and revoked without changing the root password.

pvelab01 — create an API token for automation

# Create a token for your admin user
pveum user token add youradmin@pam automation-token --privsep=1

# The token secret is shown once — save it securely
# Format: youradmin@pam!automation-token=<uuid>

Grant the token only the roles it needs:

pvelab01 — assign minimal permissions to the token

# PVEAuditor = read-only access (monitoring scripts, dashboards)
pveum aclmod / -token youradmin@pam!automation-token -role PVEAuditor

# PVEAdmin = full access (if your automation creates/manages containers)
# pveum aclmod / -token youradmin@pam!automation-token -role PVEAdmin

Task 4: Harden the underlying Debian system

Proxmox runs on Debian. The OS-level hardening matters as much as the Proxmox-specific settings.

1Keep the system updated5 min
pvelab01 — update all packages

apt update && apt dist-upgrade -y

Run this on all nodes. Consider setting up unattended security updates:

pvelab01 — enable unattended security updates

apt install -y unattended-upgrades
cat > /etc/apt/apt.conf.d/50unattended-upgrades << 'EOF'
Unattended-Upgrade::Allowed-Origins {
  "Debian:bookworm-security";
};
Unattended-Upgrade::Automatic-Reboot "false";
EOF

dpkg-reconfigure -plow unattended-upgrades
2Install fail2ban to block SSH brute force5 min

Even with password auth disabled, failed key attempts can flood your logs and waste resources. fail2ban automatically bans IPs that make too many failed authentication attempts:

pvelab01 — install and configure fail2ban

apt install -y fail2ban

cat > /etc/fail2ban/jail.d/sshd.conf << 'EOF'
[sshd]
enabled = true
port = 22
filter = sshd
maxretry = 5
bantime = 3600
findtime = 600
EOF

systemctl enable --now fail2ban

With this config, an IP making 5 failed SSH attempts within 10 minutes is blocked for 1 hour.

3Disable unused services5 min

Proxmox runs several services by default. Review and disable anything you don’t use:

pvelab01 — list running services

systemctl list-units --state=running --type=service

Services you probably don’t need on a homelab:

  • postfix — local mail server. Unless you’re routing alert emails through Proxmox directly (most people use Grafana/Alertmanager for this), disable it:
Disable postfix if not needed

systemctl disable --now postfix

Don’t disable Proxmox’s own services (pvedaemon, pveproxy, pvestatd, pve-cluster, corosync) — those are required.


Task 5: Security audit — verify your hardening worked

1Run a port scan from another machine on your LAN5 min
From your laptop — scan a Proxmox node

# Install nmap if needed: sudo apt install nmap
nmap -sS -O 10.0.0.70

Expected open ports on a hardened Proxmox node:

  • 22 — SSH (key auth only)
  • 8006 — Proxmox web UI (HTTPS)
  • 111, 2049 — NFS (only if NFS is enabled)
  • 3128 — Spice proxy (only if VM console access is enabled)

Ports that should NOT be open unless you specifically configured them:

  • 80 (plain HTTP)
  • 3306 (MySQL)
  • 5432 (PostgreSQL)
  • Any high port not corresponding to a service you installed

If you see unexpected open ports, identify the service (ss -tlnp | grep <port>) and disable it.

2Verify password auth is actually disabled2 min
From your laptop — attempt password auth (should fail)

# This should fail with "Permission denied (publickey)"
ssh -o PasswordAuthentication=yes -o PubkeyAuthentication=no root@10.0.0.70

If you see a password prompt, password auth is still enabled. Go back and recheck the /etc/ssh/sshd_config change and systemctl reload sshd.

3Verify firewall is blocking unexpected traffic3 min

From a device NOT in your homelan or lablan IPSETs (e.g., a device on a guest WiFi network):

From an untrusted network — attempt access

# Web UI access should time out (not "connection refused" — DROPPED)
curl -k --connect-timeout 5 https://10.0.0.70:8006
# Expected: curl: (28) Connection timed out

# SSH should also time out
ssh -o ConnectTimeout=5 root@10.0.0.70
# Expected: ssh: connect to host 10.0.0.70 port 22: Connection timed out

“Connection timed out” means the firewall is silently dropping the packets — correct behavior. “Connection refused” means the port is open but nothing is listening — the service is down, not the firewall. “Connected” means the firewall isn’t working.


Ongoing security practices

A one-time hardening pass degrades over time. Build these habits:

  • Monthly: apt update && apt dist-upgrade -y on all nodes
  • After any incident or staff change: Rotate API tokens, review authorized_keys
  • When adding new services: Review what ports they expose and add appropriate firewall rules
  • Before exposing anything to the internet: Re-run the port scan, review all firewall rules, ensure TLS is configured

Summary of what this hardening achieves

Attack vector Before After
SSH brute force Possible (password auth) Impossible (key auth only + fail2ban)
Web UI from untrusted network Open to any LAN device Restricted to homelan + lablan + Tailscale
Root access via weak password Possible Impossible via SSH (key auth); web UI requires password + TOTP
Open unnecessary ports Default Debian services may be exposed Audited and unused services disabled
Outdated packages Manual Automatic security updates enabled

With this configuration in place, your cluster is meaningfully more secure than the default while remaining fully accessible from your usual devices and locations.


Next steps

With a secure, running cluster, it’s time to start deploying workloads. A good first container is a monitoring stack — Prometheus + Grafana — so you have visibility into the cluster before adding anything else.

→ See: Node Exporter + pve-exporter: Complete Proxmox Metrics in Grafana

Also consider:

This post contains Amazon affiliate links (tag: buildahomelab-20). I earn a small commission on qualifying purchases at no extra cost to you.