I locked myself out of a VPS at 11 PM last year because I’d hardened the SSH config without testing it from a second session first. The server was a $6/month box running a few side projects, so the stakes were low enough that a support ticket and a few hours of downtime was the full cost of the mistake. That experience pushed me to actually document what a production-ready sshd_config looks like and, more importantly, the order in which to apply changes safely.
SSH is the main administrative entry point for most Linux servers. Getting the configuration wrong doesn’t just create security risk—it can also lock you out entirely. These are the settings I now apply to every new server, with notes on why each one matters and what breaks if you misconfigure it.
The First Three Settings That Matter Most
If you only change three things from a stock OpenSSH configuration, make them these:
Disable root login with passwords. Set PermitRootLogin prohibit-password. This allows key-based root access for scripts that genuinely need it while blocking password brute-force against the most privileged account on the machine. If you have no scripts using root SSH keys, set it to no entirely. The default in many distributions is still yes, which is a problem.
Disable password authentication entirely. Set PasswordAuthentication no. This requires that you have SSH keys working before you flip this switch—do not change this without testing key-based login in a second terminal window first. With password auth off, credential stuffing attacks against your server become useless. Automated scanners will keep hammering port 22, but they’ll get nowhere.
Move off port 22 if you want quieter logs. This is not a security measure—anyone scanning your IP range will find your SSH port regardless of what number you use—but changing to a non-standard port dramatically reduces the noise in your auth logs, which makes real anomalies easier to spot. I use something in the 2200-9999 range. Update your firewall rules before restarting.
Cryptographic Settings Worth Locking Down
The default OpenSSH ciphers and key exchange algorithms in modern distributions are already reasonable, but explicitly configuring them serves two purposes: it documents your security posture and it prevents future OpenSSH updates from silently adding weaker algorithms for compatibility reasons.
For ciphers, I use: Ciphers [email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr. The GCM modes are authenticated encryption and faster on hardware with AES-NI support. The CTR modes are there for compatibility with slightly older clients.
For key exchange, I use: KexAlgorithms curve25519-sha256,[email protected],ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group14-sha256. This drops diffie-hellman-group1-sha1 entirely, which uses a 768-bit prime that’s no longer considered safe. If you’re connecting from anything that requires group1, you have a different problem.
For MACs: MACs [email protected],[email protected],hmac-sha2-256,hmac-sha2-512. The etm (encrypt-then-MAC) variants are more secure and should be preferred where client compatibility allows.
One practical note: after changing cryptographic settings, test from every type of client you use—your main workstation, any CI/CD pipelines, monitoring agents, deployment scripts—before assuming you’re done. I’ve broken automated deployments by dropping an algorithm that a third-party tool was still using.
Access Control: Limiting Who Can Connect at All
OpenSSH has AllowUsers and AllowGroups directives that work as a whitelist at the SSH level, independent of Linux filesystem permissions. I use AllowGroups sshusers and add only the specific accounts that need SSH access to that group. This means a compromised application account can’t be used as an SSH entry point even if an attacker knows the password or gets a key onto the machine.
Combined with this: set MaxAuthTries 3 and LoginGraceTime 30. The auth tries limit cuts down on how many password guesses an attacker gets before the connection is dropped. The grace time limit (in seconds) prevents long-lived unauthenticated connections from tying up server resources.
ClientAliveInterval 300 and ClientAliveCountMax 2 together will terminate idle sessions after about 10 minutes of inactivity. On servers that handle sensitive data, leaving authenticated sessions sitting open indefinitely is a real risk—not from remote attackers but from someone with physical or console access to the client machine.
For servers where I’m doing infrastructure work, I also enable LogLevel VERBOSE. This logs the key fingerprint used for each login, not just the username. When you’re auditing which SSH keys are actually being used across a fleet, fingerprint-level logging is the only way to correlate key activity with specific key files.
A Safe Sequence for Applying These Changes
The way to not lock yourself out:
- Open a second SSH session to the server before touching the config. Keep it open throughout.
- Edit
/etc/ssh/sshd_configwith your changes. - Run
sshd -tto test the config for syntax errors. Fix anything it reports before continuing. - Restart SSH:
systemctl restart sshd(notstopthenstart—existing sessions survive a restart). - In a third terminal (not either of the two existing sessions), open a new connection. Verify it works with your new settings.
- Only after step 5 succeeds, close your backup session from step 1.
The sshd -t test step catches syntax errors and unknown directives. It does not catch logic errors—like disabling password auth before your keys are actually installed on the server—so the test-from-a-new-session step in point 5 is non-negotiable.
If you’re managing multiple servers, tools like Ansible make applying a standard sshd_config template across a fleet straightforward. The post on browser fingerprinting defenses touches on how layering too many protection measures can backfire—the same principle applies here: a coherent, minimal config applied consistently is more reliable than a maximally complex one applied inconsistently.
What to Actually Audit on an Existing Server
If you’re hardening a server that’s already been running, not a fresh install, there’s a prior step: audit what’s already in /etc/ssh/authorized_keys and ~/.ssh/authorized_keys for each user. Every key in those files is a credential with permanent access until explicitly removed.
Run for user in $(cut -f1 -d: /etc/passwd); do echo "$user:"; grep -v '^#' /home/$user/.ssh/authorized_keys 2>/dev/null || true; done to see all authorized keys across user accounts. On servers that have been running for a while, it’s common to find keys from former team members, old deployment systems, or test accounts that were never cleaned up.
Also check for anything in /etc/ssh/authorized_keys/%u if AuthorizedKeysFile has been customized. Organizations that moved keys to a central location sometimes left the per-user files in place as well, creating duplicate credential paths.
For homelab setups where you’re also running self-hosted services, the post on homelab hardware and self-hosted services covers the infrastructure side—the same boxes that need SSH hardening are often running Docker, Pi-hole, and other exposed services worth securing together.
Tools and Reference Material
The SSH.com sshd_config reference is the most useful single-page reference for option explanations. Mozilla’s OpenSSH configuration generator at infosec.mozilla.org/guidelines/openssh produces a config block you can drop into your sshd_config based on your OpenSSH version and security/compatibility tradeoff.
For going deeper on SSH security and Linux server hardening more broadly, these are worth having on hand. These are affiliate links—I may earn a small commission from qualifying purchases at no extra cost to you.
- Linux server security and hardening books on Amazon — covers SSH alongside firewall configuration, SELinux, and audit logging
- SSH Mastery by Michael W. Lucas on Amazon — the most thorough practical treatment of OpenSSH configuration and key management available
- YubiKey hardware security keys on Amazon — for adding hardware-backed authentication as a second factor alongside SSH keys
- Managed network switches for homelab VLAN segmentation on Amazon — network isolation is the layer below SSH hardening for serious setups
If you’re running SSH-exposed infrastructure and want to stay current on new vulnerabilities and configuration guidance, follow Alpha Signal on Telegram for security field notes as they come up.
📧 Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.
Leave a Reply