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
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:
| Setting | Value | Effect |
|---|---|---|
PermitRootLogin | "no" | Root cannot log in via SSH under any circumstances |
PasswordAuthentication | false | Disables password login |
KbdInteractiveAuthentication | false | Disables challenge-response (PAM) login |
AuthenticationMethods | "publickey" | Explicitly requires public key; rejects all other methods |
PermitEmptyPasswords | false | Defence-in-depth against misconfigured accounts |
Authorised Key
A single Ed25519 key is authorised for the nixos user:
users.users.${username}.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBjCf1PpvoMshFkoyjFOYUJ6/pLexwEFqr29COJawkoB"
];Key Exchange (Post-Quantum)
KexAlgorithms = [
"mlkem768x25519-sha256"
"sntrup761x25519-sha512"
"curve25519-sha256@libssh.org"
];| Algorithm | Type | Notes |
|---|---|---|
mlkem768x25519-sha256 | Post-quantum hybrid | ML-KEM (FIPS 203) + X25519; primary KEX. Protects against harvest-now-decrypt-later attacks from future quantum computers |
sntrup761x25519-sha512 | Post-quantum hybrid | NTRU Prime + X25519; fallback post-quantum option |
curve25519-sha256@libssh.org | Classical | Fallback 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
Ciphers = [
"chacha20-poly1305@openssh.com"
"aes256-gcm@openssh.com"
"aes256-ctr"
];| Cipher | Notes |
|---|---|
chacha20-poly1305@openssh.com | Preferred; constant-time on all hardware, no AES-NI dependency |
aes256-gcm@openssh.com | Fast on Intel with AES-NI; AEAD cipher |
aes256-ctr | Fallback for older clients; requires a separate MAC |
All weak/legacy ciphers (3des-cbc, aes*-cbc, arcfour*) are excluded by not being listed.
MACs
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
MaxSessions = 2;
MaxAuthTries = 3;
clientAliveCountMax = 1;
clientAliveInterval = 60;| Setting | Value | Effect |
|---|---|---|
MaxSessions | 2 | Maximum 2 multiplexed sessions per connection |
MaxAuthTries | 3 | Disconnect after 3 failed authentication attempts |
clientAliveCountMax | 1 | Only 1 missed keepalive before disconnecting |
clientAliveInterval | 60 | Send keepalive every 60 seconds |
Idle connections are dropped after 60 seconds of unresponsiveness (60s interval * 1 missed = 60s).
Other Settings
LogLevel = "VERBOSE";
PermitTunnel = false;
TCPKeepAlive = false;
DisableForwarding = true;
allowSFTP = true;| Setting | Value | Rationale |
|---|---|---|
LogLevel | VERBOSE | Logs key fingerprints and auth details; useful for forensics |
PermitTunnel | false | Disables SSH tunnelling (VPN-over-SSH) |
TCPKeepAlive | false | Uses SSH-level keepalives instead of TCP keepalives (not spoofable) |
DisableForwarding | true | Disables all forwarding: TCP, X11, agent, streamlocal |
allowSFTP | true | SFTP 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.