Pinning an explicit list of SSH algorithms feels like the responsible thing to do. It is also
how you remove a protection you had for free. A hand-written KexAlgorithms line here quietly
dropped post-quantum key exchange on a host that supported it, and nothing failed. The only
signal was a warning from the SSH client that nobody reads. The same session produced a second lesson: the
obvious way to test a MACs change never consults the setting at all.
The goal was ordinary. A scanner had flagged weak MAC algorithms, so the fix was an explicit
MACs line, and while in there it seemed sensible to pin KexAlgorithms too. The list
came from a hardening guide. It looked strict and modern.
It omitted [email protected]. OpenSSH 9.6 on that host offered it by default,
the pinned list replaced the default entirely, and key exchange fell back to classical curve25519. No error.
No log line on the server. Every connection continued to work exactly as before.
Modern OpenSSH clients do tell you. It looks like this, and it scrolls past above the banner:
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
Take it seriously. "Store now, decrypt later" is not a hypothetical for a host with a long life. Traffic captured today is decryptable the day the maths changes, and a key exchange is exactly the part of the session that matters for that.
OpenSSH lets you modify the default list rather than overwrite it, and this is almost always what you actually want:
# remove specific weak algorithms, keep everything else including future additions
KexAlgorithms -diffie-hellman-group1-sha1,diffie-hellman-group14-sha1
# or put one first without discarding the rest
KexAlgorithms ^[email protected]
The - prefix subtracts from the default. The ^ prefix moves entries to the front.
Both survive an OpenSSH upgrade adding something new and good, which a hardcoded list does not. Pin the
literal list only when a compliance document demands that exact string, and then diff it against
ssh -Q kex on every upgrade.
Never trust the config file. Ask the connection:
ssh -vv <host> 2>&1 | grep -i "kex: algorithm"
# kex: algorithm: [email protected]
# and see what this build even supports
ssh -Q kex
Here is the second trap, and it invalidates most people's verification of a MACs change.
You set a strict MACs line. You test it by connecting with a weak MAC forced, expecting a
rejection. It connects. You conclude the setting did not apply.
MACs list.
[email protected] carries its own integrity, so when it is negotiated the MAC
directive is simply not used. Your test connected because MACs were irrelevant to that session, not because
your config failed. Force a non-AEAD cipher and the directive comes back into play:
# correct test. Expect: no matching MAC found
ssh -o Ciphers=aes256-ctr -o MACs=hmac-sha1 <host>
# control. A permitted MAC on the same cipher should still connect
ssh -o Ciphers=aes256-ctr -o MACs=hmac-sha2-512 <host>
Run both. One test that fails proves a rejection exists. The pair proves you rejected the right thing.
Every item here is the same mistake wearing different clothes. A security change that only ever makes things stricter is easy to verify, because failure is loud. A security change that replaces a set of defaults can silently remove something, and the removal is invisible unless you go looking for what is no longer there.
So after any algorithm change, ask what the connection negotiated rather than what the file says. The fleet-wide version of this fix, applied across 22 hosts, is in the SSH MAC hardening guide.
KexAlgorithms alone is a perfectly defensible position and strictly better than a stale
pinned list.