Skip to content

SSH Hardening

The server runs OpenSSH with a hardened configuration focused on post-quantum cryptography, strict authentication, and minimal session exposure.

Source: server/modules/openssh.nix

Authentication

nix
settings = {
  PermitRootLogin = "no";
  PermitEmptyPasswords = false;
  PasswordAuthentication = false;
  AuthenticationMethods = "publickey";
  KbdInteractiveAuthentication = false;
};

Only public key authentication is allowed. Password-based login is completely disabled at every level:

SettingValueEffect
PermitRootLogin"no"Root cannot log in via SSH under any circumstances
PasswordAuthenticationfalseDisables password login
KbdInteractiveAuthenticationfalseDisables challenge-response (PAM) login
AuthenticationMethods"publickey"Explicitly requires public key; rejects all other methods
PermitEmptyPasswordsfalseDefence-in-depth against misconfigured accounts

Authorised Key

A single Ed25519 key is authorised for the nixos user:

nix
users.users.${username}.openssh.authorizedKeys.keys = [
  "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBjCf1PpvoMshFkoyjFOYUJ6/pLexwEFqr29COJawkoB"
];

Key Exchange (Post-Quantum)

nix
KexAlgorithms = [
  "mlkem768x25519-sha256"
  "sntrup761x25519-sha512"
  "curve25519-sha256@libssh.org"
];
AlgorithmTypeNotes
mlkem768x25519-sha256Post-quantum hybridML-KEM (FIPS 203) + X25519; primary KEX. Protects against harvest-now-decrypt-later attacks from future quantum computers
sntrup761x25519-sha512Post-quantum hybridNTRU Prime + X25519; fallback post-quantum option
curve25519-sha256@libssh.orgClassicalFallback for clients that don't support post-quantum KEX

Post-Quantum Priority

The post-quantum algorithms are listed first, so any client that supports them will negotiate a quantum-resistant key exchange. The classical fallback exists only for compatibility with older SSH clients.

Ciphers

nix
Ciphers = [
  "chacha20-poly1305@openssh.com"
  "aes256-gcm@openssh.com"
  "aes256-ctr"
];
CipherNotes
chacha20-poly1305@openssh.comPreferred; constant-time on all hardware, no AES-NI dependency
aes256-gcm@openssh.comFast on Intel with AES-NI; AEAD cipher
aes256-ctrFallback for older clients; requires a separate MAC

All weak/legacy ciphers (3des-cbc, aes*-cbc, arcfour*) are excluded by not being listed.

MACs

nix
Macs = [
  "hmac-sha2-512-etm@openssh.com"
  "hmac-sha2-512"
];

Only SHA-512 MACs are allowed. The etm (Encrypt-then-MAC) variant is preferred as it provides stronger security guarantees than the default MAC-then-Encrypt construction.

Session Limits

nix
MaxSessions = 2;
MaxAuthTries = 3;
clientAliveCountMax = 1;
clientAliveInterval = 60;
SettingValueEffect
MaxSessions2Maximum 2 multiplexed sessions per connection
MaxAuthTries3Disconnect after 3 failed authentication attempts
clientAliveCountMax1Only 1 missed keepalive before disconnecting
clientAliveInterval60Send keepalive every 60 seconds

Idle connections are dropped after 60 seconds of unresponsiveness (60s interval * 1 missed = 60s).

Other Settings

nix
LogLevel = "VERBOSE";
PermitTunnel = false;
TCPKeepAlive = false;
DisableForwarding = true;
allowSFTP = true;
SettingValueRationale
LogLevelVERBOSELogs key fingerprints and auth details; useful for forensics
PermitTunnelfalseDisables SSH tunnelling (VPN-over-SSH)
TCPKeepAlivefalseUses SSH-level keepalives instead of TCP keepalives (not spoofable)
DisableForwardingtrueDisables all forwarding: TCP, X11, agent, streamlocal
allowSFTPtrueSFTP is allowed for file transfer (used for config management)

TCPKeepAlive = false

TCP keepalives can be spoofed to keep dead sessions alive. SSH-level keepalives (clientAliveInterval) are preferred because they run inside the encrypted channel.

Remote Unlock SSH

The server also runs an SSH server in the initrd for remote LUKS unlock. See Disk Layout & Impermanence for details. That initrd SSH instance uses the same host key but has separate authorised keys configuration defined in server/modules/disko.nix.