guest@6admin.io:~$

SSH Hardening: the 2017 Trilogy Reread in 2026

In October 2017, an SSH hardening article goes by on my timeline. Generic analysis, naive countermeasures. I answer in three comments: disabling root, changing the port, key authentication. Nine years later, those three comments serve me as an inventory: what held, what changed and what was out of scope but deserved a mention.

First, the principle that made me react: comparing OpenSSH to a simple administration interface is reductive. SSH is the bastions to your IS, the SSH VPNs, the agentless orchestration (Ansible, Puppet Bolt) that runs on top of it. It is the administrative spine of most Unix fleets. Harden it badly and you harden the whole IS badly.

1. The root account: close what you can, frame what remains

Disabling direct root login over SSH reduces part of the attack surface. But some business imperatives require such access and I regret it. When root access must exist, OpenSSH provides limiting mechanisms:

  • command= in authorized_keys to allow a single command, with a well-written wrapper limiting lolbins the way you would in a sudoers file.
  • from= to restrict the source, with good segmentation on the client side through ~/.ssh/config and ~/.ssh/config.d/.
  • TTY allocation limits, X11 forwarding limits and the rest of the toolbox.

authorized_keys is the section of the man page people never read, yet it is the foundation of many hand-made bastions done right. AuthorizedKeysCommand and AuthorizedKeysCommandUser additionally let you delegate key distribution, which makes centralized management possible without a fragile central box.

And the anecdote that justified my tone back then: I often saw servers with the root account “disabled”, access accounts of the admin:123456 kind and a sudoers set to NOPASSWD:ALL. The compliance checkbox is ticked, the server is wide open. Under a real attack these countermeasures only buy time, if that.

2. The port: the debate that isn’t one

Changing the standard port: aside from automated scans and firewalling bypass, it only slows a real attack down. My 2017 position: leave the daemon on 22 and use port knocking or SPA. I even wrote a port knocking variant based only on TCP flags on usually unfiltered ports, varying with a time-based parameter.

[edit: 2026] Ten years later I keep the diagnosis (changing the port does nothing against a serious attacker) but the treatment changed: WireGuard silently ignores every unauthenticated packet and produces the same invisibility effect, without an extra operational layer. The full argument is in Security through obscurity is not security . Port knocking, when I keep one, is a break-glass mechanism before having to crawl to the provider’s KVMoIP/IPMI console. A plan B, not an architecture.

For a server that really must not be exposed: the daemon listens only on the loopback interface and you go through a tunnel. The port question then becomes the entry point question and that is the bastion question.

3. Keys: ed25519 and everything else

The 2017 comment fit in one sentence: yes, systematically, with the best crypto of the moment (ed25519) and restrictions on the available ciphers, server side as well as client side. The config details live in hardening SSH and modern SSH . The rest:

  • Human user: strong passphrase mandatory.
  • Machine to machine: command restriction mandatory (command=), otherwise the key is a password that gets copied around.
  • Agent forwarding and X11 forwarding: disabled by default, enabled case by case.

None of this aged. What aged is the client side and that comes next.

4. What 2017 didn’t see: the client and the supply chain [edit: 2026]

Half of the 2017 SSH discussion was server-side. In 2026, the weak link is often the client.

First case: the Windows workstation. The native OpenSSH client has existed for years and a good old ~/.ssh/config does the job: aliases, ProxyJump, Match host. Sessions then live in a text file you can read, template and distribute via a Git repo or a GPO, rather than in the registry. You avoid the .ppk and its puttygen conversions and above all you step out of a distribution chain that already failed: PuTTY trojanized by UNC4034 in 2022, malvertising and SEO poisoning campaigns on “download putty” queries. The SSH client is software you install: installing it from the right source is part of hardening.

The native install has holes, let us be precise: no ControlMaster or ControlPersist on Windows so no multiplexing, no serial console (PuTTY keeps the edge for resuming the COM port), no ssh-copy-id out of the box, X11 forwarding requires a third-party X server. These are known, framed holes, not reasons to go back to a trojanizable supply chain.

5. The bastion: a single entry point, not a magician [edit: 2026]

The bastion gives you a single entry point to your machines, whether for your contractors or your employees. What I learned to phrase precisely in 2025, after a question like “can the bastion prevent a contractor from doing X”:

  • No. If the contractors hold the administrative privileges on the machine and even without them, via privilege escalation, then they can do what they want. That is not the bastion’s job.
  • What protects you is elsewhere: a systematic least-privilege policy and network isolation. If only the bastion holds the firewall rules toward the remote administration of a VLAN’s machines and the machines of that VLAN don’t have them between themselves, then lateral movement from machine to machine is theoretically impossible. Only from the bastion to the machines the user is authorized for.

Lateral movement is prevented by architecture, not by the entry point. The bastion concentrates logs and access, it replaces neither.

6. The compromise nobody puts in the guides

Also in 2017: limiting a key to an IP, tcpwrappers, denyhosts, hosts.allow/hosts.deny, filtering with iptables or upstream, but that may hurt users on the move. It is often necessary to make compromises between security and comfort of use and administration.

A guide that doesn’t mention this compromise is selling a configuration that users will dismantle. The SSH hardening that survives three months in production is the one that integrates mobility from version one.

7. The final wall: the physical threat [edit: 2026]

One last point, which I formulated in 2025 about biometric authentication: just take the fingerprint, or cut the fingers off. In the case of a physical intrusion where your servers and equipment are the target, you have to think about that, as much as about the psychological and physical constraints of extracting a secret. A simple axe or one battering ram blow and it’s over.

SSH hardening protects against a remote attacker on port 22. It does not protect against physical access and saying so is part of the discipline. That is why measured boot mechanisms, disk encryption with a detachable secret and incident response planning exist and why they don’t appear in the same guides.

Recap [edit: 2026]

20172026
Root: close it, frame it with command=/from=Same. Always check there is no admin:123456 behind it
Port: moving it is useless, knocking/SPA as fallbackSame diagnosis, treatment: WireGuard does it better natively
Keys: ed25519, passphrase, command=, TTY/X11Same and the client matters as much as the server
Bastion: single entry point+ least privilege and network isolation for lateral movement
Not mentionedClient supply chain (trojanized PuTTY, .ppk, distribution via repo/GPO)
Not mentionedPhysical threat: an axe or a ram and the config no longer counts

[edit: 2026] The Ansible implementation of all this: the ssh and ssh-users roles.