Proxmox SSH Hardening

Step-by-step SSH hardening procedure for Proxmox LXC containers and VMs: disable root login, configure key-based auth, deploy fail2ban, and lock down the firewall.

Prerequisites

Before starting, confirm the following:

  • You have root or sudo access to the target Proxmox host, LXC container, or VM
  • You have a local SSH key pair generated (~/.ssh/id_ed25519 and ~/.ssh/id_ed25519.pub)
  • You have console access to the target via the Proxmox web UI (fallback if SSH locks you out)
  • The target is running a Debian-based distribution (Ubuntu 22.04/24.04 or Debian 12)
  • You have tested that your SSH key works before disabling password auth

Warning: Misconfiguring SSH can lock you out of the system. Always keep the Proxmox console session open as a fallback until every step is verified.

Step 1: Create a Service Account

Never use root for routine SSH access. Create a dedicated service account.

# On the target host/container
sudo adduser svc-admin --disabled-password --gecos "Service Admin"

# Add to sudo group
sudo usermod -aG sudo svc-admin

# Verify sudo works
su - svc-admin
sudo whoami
# Should output: root

For LXC containers, if the container was created with root-only access, you may need to run these commands from the Proxmox host:

# From the Proxmox host
pct exec <CTID> -- adduser svc-admin --disabled-password --gecos "Service Admin"
pct exec <CTID> -- usermod -aG sudo svc-admin

Step 2: Configure SSH Key Authentication

# On the target, as the new service account
su - svc-admin
mkdir -p ~/.ssh
chmod 700 ~/.ssh

From your local machine, copy your public key:

# From your local machine
ssh-copy-id -i ~/.ssh/id_ed25519.pub svc-admin@<target-ip>

If ssh-copy-id is not available or the target is an LXC container without direct SSH yet:

# Manually copy the key content
# On your local machine, copy the output of:
cat ~/.ssh/id_ed25519.pub

# On the target, as svc-admin:
echo "ssh-ed25519 AAAA... your-key-comment" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Verify: Open a new terminal and confirm key-based login works before proceeding.

ssh svc-admin@<target-ip>
# Should log in without a password prompt

Stop here if key-based login does not work. Do not proceed to disable password auth until this is confirmed.

Step 3: Disable Root SSH and Password Authentication

Edit the SSH daemon configuration:

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup-$(date +%Y%m%d)
sudo nano /etc/ssh/sshd_config

Set the following directives (uncomment and modify as needed):

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30
AllowUsers svc-admin

If you need to allow additional users, add them to the AllowUsers line separated by spaces.

Validate the configuration before restarting:

sudo sshd -t
# Should produce no output if config is valid

Restart SSH:

sudo systemctl restart sshd

Verify: In a new terminal (do not close your current session):

# Should work
ssh svc-admin@<target-ip>

# Should be rejected
ssh root@<target-ip>
# Expected: Permission denied (publickey)

Step 4: Set Up fail2ban

fail2ban monitors SSH logs and bans IPs after repeated failed attempts.

sudo apt update && sudo apt install -y fail2ban

Create a local configuration (never edit the main config directly):

sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
backend = systemd

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF

Start and enable fail2ban:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Verify:

sudo fail2ban-client status sshd
# Should show the jail is active with 0 currently banned (unless there is existing traffic)

Step 5: Configure Firewall Rules

For Proxmox VMs (using UFW)

sudo apt install -y ufw

# Default deny incoming, allow outgoing
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH from your management network only
sudo ufw allow from 10.0.0.0/24 to any port 22 proto tcp comment "SSH from management network"

# If you need to allow SSH from WireGuard subnet
sudo ufw allow from 10.10.0.0/24 to any port 22 proto tcp comment "SSH from WireGuard"

# Enable the firewall
sudo ufw enable
sudo ufw status verbose

For Proxmox LXC Containers (using Proxmox firewall)

LXC containers can use the Proxmox built-in firewall instead of UFW. Configure via the Proxmox web UI or CLI:

# From the Proxmox host
# Enable firewall on the container
pct set <CTID> -firewall 1

# Add rules via the Proxmox API or web UI:
# Allow SSH from management network
# Direction: IN, Action: ACCEPT, Source: 10.0.0.0/24, Dest port: 22, Protocol: tcp

Alternatively, create the firewall rules file directly:

# On the Proxmox host
cat > /etc/pve/firewall/<CTID>.fw << 'EOF'
[RULES]
IN ACCEPT -source 10.0.0.0/24 -p tcp -dport 22 -log nolog
IN DROP -p tcp -dport 22 -log nolog
EOF

Verify:

# From an allowed network
ssh svc-admin@<target-ip>
# Should succeed

# The firewall should drop connections from non-allowed networks

Step 6: Optional Hardening

Change the SSH port

Not security through obscurity -- it reduces log noise from automated scanners.

# In /etc/ssh/sshd_config
Port 2222

# Update fail2ban jail
# In /etc/fail2ban/jail.local, change port = ssh to port = 2222

# Update firewall rules to reflect the new port
sudo ufw delete allow from 10.0.0.0/24 to any port 22 proto tcp
sudo ufw allow from 10.0.0.0/24 to any port 2222 proto tcp comment "SSH non-standard port"

sudo systemctl restart sshd
sudo systemctl restart fail2ban

Disable SSH agent forwarding

# In /etc/ssh/sshd_config
AllowAgentForwarding no
AllowTcpForwarding no

Rollback

If SSH access is lost:

  1. Use the Proxmox console -- Access the VM or container via the Proxmox web UI console (no SSH required)
  2. Restore the SSH config backup:
sudo cp /etc/ssh/sshd_config.backup-YYYYMMDD /etc/ssh/sshd_config
sudo systemctl restart sshd
  1. If fail2ban banned your IP:
sudo fail2ban-client set sshd unbanip <your-ip>
  1. If UFW is blocking you:
# From the Proxmox console
sudo ufw disable
# Fix the rules, then re-enable
  1. If the service account is misconfigured:
# From the Proxmox console, log in as root
# Re-enable root SSH temporarily
sed -i 's/PermitRootLogin no/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd
# Fix the service account, then re-harden

Maintenance Notes

  • Audit /var/log/auth.log periodically for failed login patterns
  • Review fail2ban-client status sshd weekly for ban activity
  • Rotate SSH keys annually -- update authorized_keys on all hardened hosts
  • When creating new LXC containers or VMs, apply this procedure before any service deployment
  • Document each hardened host in the infrastructure inventory with the SSH port, allowed networks, and service account name