SSH Server

Posted on 6 2026

SSH is the primary management interface for every server and container in this network. Getting the configuration right once, in the base container template, means every subsequent container inherits a hardened baseline without repetition.

This page covers the SSH server configuration applied to the Proxmox host and the base container template, updated for OpenSSH 9.6p1 as shipped with Ubuntu 24.04 LTS.

Algorithm selection

The source material covers algorithm selection in detail. The principles remain the same but some specifics have changed with OpenSSH 9.6p1 on Ubuntu 24.04.

Host key algorithms

Only Ed25519 and RSA-SHA2 variants are recommended. ECDSA using NIST P-curves is excluded. DSA and SHA-1-based algorithms are not offered.

Check what the installed version supports:

ssh -Q key

Recommended set for the server configuration:

ssh-ed25519
ssh-ed25519-cert-v01@openssh.com
sk-ssh-ed25519@openssh.com
sk-ssh-ed25519-cert-v01@openssh.com
rsa-sha2-512
rsa-sha2-512-cert-v01@openssh.com
rsa-sha2-256
rsa-sha2-256-cert-v01@openssh.com

Key exchange algorithms

OpenSSH 9.x enables sntrup761x25519-sha512@openssh.com by default: a post-quantum hybrid key exchange. This is worth keeping in the configuration as it provides quantum-resistant key exchange for clients that support it, while falling back gracefully to curve25519-sha256 for clients that do not.

ssh -Q kex

Recommended set:

sntrup761x25519-sha512@openssh.com
curve25519-sha256
curve25519-sha256@libssh.org
diffie-hellman-group16-sha512
diffie-hellman-group18-sha512
diffie-hellman-group-exchange-sha256

Symmetric ciphers

ssh -Q cipher

Preferred set:

chacha20-poly1305@openssh.com
aes256-gcm@openssh.com
aes128-gcm@openssh.com
aes256-ctr
aes192-ctr
aes128-ctr

Message authentication codes

Encrypt-then-MAC (ETM) variants only. Plain MAC variants excluded.

ssh -Q mac

Preferred set:

hmac-sha2-512-etm@openssh.com
hmac-sha2-256-etm@openssh.com
umac-128-etm@openssh.com

Server host keys

Regenerate the server host keys after the entropy section is confirmed and the base container is clean. The template cleanup already removes host keys. They are regenerated on first boot for each container.

For the Proxmox host, regenerate explicitly:

sudo rm /etc/ssh/ssh_host_*
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
sudo ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
sudo systemctl restart ssh

Only Ed25519 and RSA host keys are generated. ECDSA and DSA keys are not created, which prevents them from being offered even if a client requests them.

SSH server configuration

The complete server configuration goes in /etc/ssh/sshd_config.d/hardening.conf. Using a drop-in file rather than editing the main sshd_config survives package upgrades cleanly.

sudo tee /etc/ssh/sshd_config.d/hardening.conf << 'EOF'
# ============================================================
# SSH server hardening configuration
# Applies on top of /etc/ssh/sshd_config
# Ubuntu 24.04 / OpenSSH 9.6p1
# ============================================================

# ============================================================
# Network and protocol
# ============================================================

# Non-standard port reduces automated scanner noise in logs
# Not a security feature - just keeps logs readable
Port 63508

# Protocol version 2 only (already the default, stated explicitly)
Protocol 2

# ============================================================
# Server authentication
# ============================================================

HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com

# ============================================================
# Client and user authentication
# ============================================================

# Root login prohibited: use a regular user with sudo
PermitRootLogin no

# Public key authentication only
AuthenticationMethods publickey

# Disable all password-based authentication
PasswordAuthentication no

# KbdInteractiveAuthentication replaces ChallengeResponseAuthentication
# in OpenSSH 8.7+
KbdInteractiveAuthentication no

# Enable PAM for session management but not authentication
UsePAM yes

# Login grace time: disconnect if not authenticated within 30 seconds
LoginGraceTime 30

# Maximum authentication attempts per connection
MaxAuthTries 3

# Maximum simultaneous unauthenticated connections
MaxStartups 10:30:60

# ============================================================
# Access control
# ============================================================

# Only members of the sshlogin group may connect
AllowGroups sshlogin

# ============================================================
# Cipher suite selection
# ============================================================

KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256

Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

# ============================================================
# GPG agent forwarding
# ============================================================

# Remove stale socket files before creating forwarded sockets
# Required for GPG agent forwarding as covered in the desktop section
StreamLocalBindUnlink yes

# ============================================================
# Miscellaneous
# ============================================================

# Disable X11 forwarding
X11Forwarding no

# Pass locale environment variables from client
AcceptEnv LANG LC_*

# SFTP subsystem
Subsystem sftp /usr/lib/openssh/sftp-server

# Print last login information
PrintLastLog yes

# Log level for auditing
LogLevel VERBOSE

EOF

Allowed user group

Create the sshlogin group and add your user account:

sudo groupadd sshlogin
sudo adduser $USER sshlogin

In containers where a service-specific user account connects via SSH (for example, a borg user for backup operations), add that account to the sshlogin group as well:

sudo adduser borg sshlogin

Restart SSH

Before restarting, validate the configuration:

sudo sshd -t

No output means no errors. If errors appear, fix them before restarting.

Open a second SSH session to the server before restarting from the first, so you can recover if the configuration has an error:

sudo systemctl restart ssh

Verify the service started correctly:

sudo systemctl status ssh

System-wide SSH client configuration

The server also acts as an SSH client when connecting to other servers (backup destinations, remote management). Configure system-wide client defaults in /etc/ssh/ssh_config.d/hardening.conf:

sudo tee /etc/ssh/ssh_config.d/hardening.conf << 'EOF'
# System-wide SSH client hardening
# Ubuntu 24.04 / OpenSSH 9.6p1

Host *
    # Strong algorithms only
    HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,sk-ssh-ed25519@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256

    KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256

    Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

    MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

    # Use DNS for host key verification where SSHFP records exist
    VerifyHostKeyDNS yes

    # Connection multiplexing
    ControlMaster auto
    ControlPath /tmp/ssh-%r@%h:%p
    ControlPersist 5m

    # Keep connections alive
    ServerAliveInterval 60
    ServerAliveCountMax 3

EOF

Note: UseRoaming no appeared in the source material. This option was removed in OpenSSH 7.8 and should not appear in modern configurations. It is not included here.

SSHFP DNS records

Publishing SSH host key fingerprints in DNS allows clients to verify server identity without manually accepting host keys, provided the domain is DNSSEC-secured.

Generate SSHFP records for the server:

ssh-keygen -r server.yourdomain.net. -f /etc/ssh/ssh_host_ed25519_key.pub
ssh-keygen -r server.yourdomain.net. -f /etc/ssh/ssh_host_rsa_key.pub

Add the SHA-256 records to the DNS zone. The SHA-1 records (type 1 1) should not be published.

Verify from the desktop once DNS is configured:

ssh -o VerifyHostKeyDNS=yes server.yourdomain.net

PAM SSH agent authentication for passwordless sudo

The source material covers libpam-ssh-agent-auth for allowing sudo authentication via the forwarded SSH agent. This is the same mechanism as on the desktop, allowing administrative operations without typing a password, using the Yubikey-backed key from the desktop session.

Install the PAM module:

sudo apt install -y libpam-ssh-agent-auth

Create /etc/pam.d/ssh-agent-auth:

auth sufficient pam_ssh_agent_auth.so file=/etc/security/authorized_keys

Note: the source material has a typo (/etc/secuurity/authorized_keys). The correct path is /etc/security/authorized_keys.

Copy the desktop’s public keys to this file:

sudo mkdir -p /etc/security
sudo tee /etc/security/authorized_keys << 'EOF'
# Authorised keys for PAM SSH agent authentication
# Add public keys of authorised administrators here
ssh-ed25519 AAAA... you@desktop
EOF

sudo chmod 0644 /etc/security/authorized_keys

Configure sudoers to keep SSH_AUTH_SOCK in the environment:

sudo visudo

Add:

Defaults env_keep += "SSH_AUTH_SOCK"

Edit /etc/pam.d/sudo to include ssh-agent-auth before common-auth:

#%PAM-1.0

@include ssh-agent-auth
@include common-auth
@include common-account
@include common-session-noninteractive

Test from the desktop with agent forwarding active:

ssh -A server.yourdomain.net
sudo whoami

Should return root without prompting for a password.

Auditing the SSH configuration

The ssh-audit tool checks the server configuration against current recommendations:

# Install on the desktop
pip install ssh-audit

# Or run via Docker
docker run -it -p 2222:2222 positronsecurity/ssh-audit server.yourdomain.net:63508

The online version at https://www.ssh-audit.com/ can audit the server if it is accessible from the internet.

Never restart the SSH daemon from a remote session without a second open connection to the server. If the configuration has an error, the daemon fails to start and you lose access. The sshd -t validation step catches most errors but having a backup connection is the safety net.