
Securing SSH with post-quantum algorithms
Why post-quantum, and why now
The usual objection — "quantum computers are far off" — ignores a very concrete, present-day threat: harvest now, decrypt later. An attacker captures encrypted traffic today, stores it, and waits twenty years for quantum technology to decrypt it retroactively.
For a home NAS the risk stays limited. But OpenSSH has supported post-quantum algorithms since 9.x and Debian 13 ships a recent enough version, so the good habits may as well start now.
Host keys
By default Debian generates ECDSA, Ed25519 and RSA. Drop ECDSA and DSA (obsolete) and keep only Ed25519 and RSA 4096:
# Remove unwanted host keys
rm -f /etc/ssh/ssh_host_ecdsa_key*
rm -f /etc/ssh/ssh_host_dsa_key*
# Regenerate the RSA key at 4096 bits if needed
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
Then be explicit in sshd_config:
# /etc/ssh/sshd_config - Host keys
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
Ed25519 first — compact, fast, Curve25519-based — with RSA 4096 as backup for out-of-date clients.
Post-quantum key exchanges
OpenSSH offers hybrid algorithms combining a classical exchange (X25519) with a post-quantum one. If the post-quantum algorithm turns out weak, classical security still holds — and vice versa.
# /etc/ssh/sshd_config - Key exchange
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,diffie-hellman-group16-sha512
- mlkem768x25519-sha256: hybrid ML-KEM 768 (NIST FIPS 203 standard, formerly CRYSTALS-Kyber) + X25519. The most recent option and the current recommendation.
- sntrup761x25519-sha512@openssh.com: hybrid NTRU Prime 761 + X25519, in OpenSSH for longer, an excellent fallback.
- curve25519-sha256: classical exchange, for clients without post-quantum support.
- diffie-hellman-group16-sha512: classical DH as a last resort.
Ciphers and MACs
# /etc/ssh/sshd_config - Ciphers
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
ChaCha20-Poly1305 by default, fast and well studied; the AES-GCM options for machines with AES-NI acceleration, including the Intel N95.
Hardened authentication
SSH keys only, no passwords:
# /etc/ssh/sshd_config - Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
MaxSessions 5
LoginGraceTime 30
PermitRootLogin no forces logging in as nasadmin then using sudo, MaxAuthTries 3 limits brute-forcing, and LoginGraceTime 30 narrows the authentication window to thirty seconds, which discourages bots quickly.
Warning banner
Often overlooked, yet mandatory in some jurisdictions to be able to prosecute an intrusion attempt:
# /etc/ssh/sshd_config
Banner /etc/ssh/banner.txt
# /etc/ssh/banner.txt
*************************************************************
WARNING: Unauthorized access to this system is prohibited.
All connections are monitored and recorded.
By connecting, you agree to comply with applicable policies.
*************************************************************
Ansible automation
All of it is deployed through an Ansible role built on the devsec.hardening collection, which applies hundreds of CIS- and ANSSI-based rules:
# playbook.yml - excerpt
- name: Harden SSH
hosts: nas
roles:
- role: devsec.hardening.ssh_hardening
vars:
ssh_kex:
- mlkem768x25519-sha256
- sntrup761x25519-sha512@openssh.com
- curve25519-sha256
- diffie-hellman-group16-sha512
ssh_host_key_algorithms:
- ssh-ed25519
- rsa-sha2-512
- rsa-sha2-256
ssh_ciphers:
- chacha20-poly1305@openssh.com
- aes256-gcm@openssh.com
- aes128-gcm@openssh.com
ssh_allow_users: nasadmin
ssh_max_auth_retries: 3
The playbook is idempotent: running it fifty times changes nothing.
Client side and verification
An Ed25519 key on the client, and a check of supported algorithms:
# Generate an Ed25519 key
ssh-keygen -t ed25519 -C "user@workstation"
# Check supported algorithms
ssh -Q kex
If mlkem768x25519-sha256 appears in the output, the client is compatible. After deployment, validate the actual negotiation:
ssh -vv nasadmin@192.168.1.50 2>&1 | grep "kex:"
# Expected result:
# debug1: kex: algorithm: mlkem768x25519-sha256
Configuring SSH for post-quantum is neither complex nor costly in performance: with OpenSSH 9.x and Debian 13 everything is available, you just set the right parameters and let Ansible do the rest.
Related articles