Handbook sections▾
Foundations
Understanding the threats to a Linux hostWhy we hardenThe defense principles behind every ruleEffective configuration: the truth no file holdsThe standards Pavois maps toSOCLE control ID modelEvidence & exportsHow the A-E grade is computedWhat a PASS proves: the qualified verdictTrust model for the evidence bundleIs SOCLE just your own norm? (governance & the circularity question)What Pavois covers, and what it doesn'tDomains
Hardening SSHHardening PAMMandatory Access Control (SELinux / AppArmor)Hardening the host firewallHardening sudoFile Permissions & OwnershipMount & filesystem hardeningHardening kernel modulesHardening the kernel & network with sysctlAudit logging with auditdHardening logging with journald and rsyslogHardening systemd servicesPackage hygieneHardening the bootloader (GRUB)Synchronizing timeLogin banners and MOTDControlling cron and at accessHardening the GNOME desktop (dconf)Tooling
Undo a hardening run: restore pointsOperating Pavois: privileges, air-gap, timing, exceptionsRun Pavois in CI (GitHub Actions)Feature status: delivered, partial, roadmapGovernance: licence, versioning, provenance, securityHardening SSH
Last reviewed
SSH is the front door to a server. Harden the daemon so a remote foothold can't become full control, and verify the effective config, not the file.
The threat: the front door, brute-forced and backdoored
SSH is usually the only remote-admin path to a server, and it is exposed to the network. That makes it the prime target: continuous brute-force and credential-stuffing, abuse of permissive settings (direct root login, password auth, TCP tunneling), and outright supply-chain attacks on the daemon itself. The 2024 XZ Utils backdoor (CVE-2024-3094) was built specifically to subvert sshd authentication, and regreSSHion (CVE-2024-6387) showed a pre-auth RCE in sshd itself. A weak SSH configuration turns a single stolen credential into full system control.
Why harden it
The blast radius is the whole machine. One brute-forced root password or one leaked key, and the attacker is the admin. Hardening SSH shrinks the attack surface and forces every connection through accountable, least-privileged paths, raising both the cost of an attack and the chance of detecting it.
What Pavois audits: the effective sshd -T config
Pavois reads the effective configuration with sshd -T, the daemon's resolved settings, so it evaluates the Includes and drop-ins under /etc/ssh/sshd_config.d/ exactly as sshd applies them, where a file-based scan (OVAL/oscap) would miss them. Across its SSH rules it checks: root login disabled, password and empty-password auth off, strong Ciphers/MACs/KexAlgorithms, MaxAuthTries, LoginGraceTime, idle timeout, the forwarding options, PermitUserEnvironment, the warning banner and LogLevel. This is the differentiator: a control reading /etc/ssh/sshd_config alone is fooled by a drop-in that flips the value back.
The essential directives
| Directive | Hardened value | Why |
|---|---|---|
PermitRootLogin |
no |
no direct root; named account then sudo, attributable |
PasswordAuthentication |
no |
keys only (set this only after a key works) |
KbdInteractiveAuthentication |
no |
closes keyboard/PAM interactive auth |
PermitEmptyPasswords |
no |
refuse any account with no password |
AllowUsers / AllowGroups |
explicit list | allowlist the accounts that may log in |
MaxAuthTries |
3 |
fewer guesses per connection (default 6) |
LoginGraceTime |
30 |
short auth window, fewer pending connections |
X11Forwarding / AllowTcpForwarding / AllowAgentForwarding |
no |
drop unused pivot/tunnel vectors |
ClientAliveInterval / ClientAliveCountMax |
300 / 2 |
close idle sessions |
LogLevel |
VERBOSE |
log key fingerprints for accountability |
Modern ciphers, MACs and key exchange
Keep only modern algorithms and drop the legacy ones. Ban: *-cbc, arcfour*, blowfish, 3des-cbc; diffie-hellman-group1-sha1, *-group14-sha1, *-sha1; hmac-md5*, hmac-sha1* and every non-ETM MAC; ssh-dss. Prefer (converges Mozilla, ssh-audit and ANSSI): KexAlgorithms with sntrup761x25519-sha512@openssh.com (post-quantum, default since OpenSSH 9.0) and curve25519-sha256; Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com; MACs in -etm only; HostKeyAlgorithms ssh-ed25519,rsa-sha2-512; RequiredRSASize 3072. Check your binary actually supports an algorithm before pasting it: ssh -Q kex ; ssh -Q cipher ; ssh -Q mac. ssh-audit grades the live result.
How do you apply it without locking yourself out?
This is where people cut their own access. The four-step reflex, never skipped:
- Validate syntax:
sshd -t(it does not test access logic). - Read the effective config:
sshd -T | grep -iE 'permitroot|passwordauth|allowusers|ciphers'. - Reload, do not restart:
systemctl reload ssh(sshdon RHEL) keeps your current session alive. - Test in a second session while keeping the first open; on a remote host, keep a KVM/serial console as backup.
sshd -t only checks syntax: you can have a green sshd -t and still lock yourself out (a bad AllowUsers, or PasswordAuthentication no with no key deployed). Hence sshd -T and the second session.
The drop-in trap
On Debian 12, Ubuntu and RHEL 9, /etc/ssh/sshd_config starts with Include /etc/ssh/sshd_config.d/*.conf, and for most directives SSH keeps the first value seen. So a file in sshd_config.d/ (read first via the Include) overrides what you write lower in the main file. The classic trap: on a cloud image, 50-cloud-init.conf often re-enables PasswordAuthentication yes and cancels your hardening. Two rules: put your settings in /etc/ssh/sshd_config.d/00-hardening.conf (the 00- prefix wins lexical order), and always verify with sshd -T, never by re-reading sshd_config.
Pitfalls
- Cloud images re-enable password auth via
50-cloud-init.conf. Override it with00-hardening.confand confirm withsshd -T. Matchblocks change the effective value per context: check them withsshd -T -C user=...,host=...,addr=....reload, notrestart: a blindrestartplus a bad rule equals an unreachable server.PasswordAuthentication nobefore a key is deployed locks everyone out.- A post-quantum KexAlgorithm on OpenSSH < 9.0 makes
sshd -tfail; remove it on old daemons.
FAQ
Why does Pavois use sshd -T instead of reading sshd_config? Because sshd -T is the resolved config the daemon enforces, including every Include and drop-in; a file scan misses an override and reports a false pass.
I hardened sshd_config but sshd -T still shows the weak value. Why? A drop-in under sshd_config.d/ (often 50-cloud-init.conf) is read first and wins. Put your settings in 00-hardening.conf.
Restart or reload? reload. It applies the new config without killing your current session, so a mistake does not lock you out immediately.