Ubuntu · Network

Use only strong ciphers, MACs and key exchange algorithms in the SSH server

When it fails: The SSH server accepts weak or outdated cryptographic algorithms. Weak algorithms let an attacker who records or intercepts SSH sessions decrypt or tamper with them, now or later.

CB-UBU-NET-015mediumactiveautomatedautomated fix, lockout riskprofile core

Description

The OpenSSH server negotiates three families of algorithms with each client, set by Ciphers, MACs and KexAlgorithms in sshd_config. This control requires every algorithm the server offers to be in an allow list:

  • ciphers: ChaCha20-Poly1305, AES-GCM, AES-CTR;
  • MACs: HMAC-SHA2-256/512, preferably in encrypt-then-MAC (-etm) mode;
  • key exchange: hybrid post-quantum (mlkem768x25519-sha256, sntrup761x25519-sha512), Curve25519, NIST ECDH, and Diffie-Hellman groups of at least 2048 bits with SHA-2.

The first key exchange algorithm offered must be a hybrid post-quantum one.

Ubuntu ships OpenSSH 8.2 (20.04), 8.9 (22.04), 9.6 (24.04) and 10.2 (26.04). All of them exclude weak ciphers but still offer SHA-1 and UMAC MACs. Only 26.04 puts a post-quantum key exchange first; 22.04 and 24.04 offer [email protected] after Curve25519, and 20.04 has none (its [email protected] is an older, withdrawn variant), so the post-quantum check does not apply to 20.04. The fix writes the three lists in /etc/ssh/sshd_config.d/00-cirsec.conf, read before the cloud image files (50-cloud-init.conf, 60-cloudimg-settings.conf) since sshd keeps the first value it reads, keeping only the algorithms the installed OpenSSH supports (ssh -Q): mlkem768x25519-sha256 is only set on 26.04. On 24.04 and later, sshd is started by ssh.socket; sshd -T fails with a missing /run/sshd until a first connection has started ssh.service, so the checks report an error until then, and the fix creates the directory before testing the configuration.

Rationale

CBC ciphers, RC4, 3DES, SHA-1 and MD5 based MACs, 64-bit tag UMAC and SHA-1 Diffie-Hellman are broken or too weak for long-term protection; UMAC is kept out to stay with the widely reviewed HMAC-SHA2 constructions. Sessions recorded today can be decrypted later by a quantum computer unless the key exchange is post-quantum, which is why it must be preferred.

Impact

Old clients and network equipment that only support removed algorithms cannot connect anymore (for example hmac-sha1 or diffie-hellman-group14-sha1). Check the clients, automation tools and SFTP libraries in use before applying.

Applies when

Detected: when: OpenSSH server is installed A scanner reports the control not applicable when one is false.

Setting

Location/etc/ssh/sshd_config.d/00-cirsec.conf (`Ciphers`, `MACs`, `KexAlgorithms`)
DefaultOpenSSH defaults, including hmac-sha1 and umac-64 MACs
RecommendedOnly the allowed algorithms, post-quantum key exchange first

Audit

Run sshd -T | grep -E '^(ciphers|macs|kexalgorithms) ' and check that:

  1. ciphers are among [email protected], [email protected], [email protected], aes256-ctr, aes192-ctr, aes128-ctr;
  2. MACs are among [email protected], [email protected], hmac-sha2-512, hmac-sha2-256;
  3. key exchange algorithms are among mlkem768x25519-sha256, sntrup761x25519-sha512 (with or without @openssh.com), curve25519-sha256 (with or without @libssh.org), ecdh-sha2-nistp256/384/521, diffie-hellman-group-exchange-sha256, diffie-hellman-group14-sha256, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, and the first one is mlkem768x25519-sha256 or sntrup761x25519-sha512.

Automated: every check must pass. Scanners report each check by its id.

ciphers

Effective Ciphers list.

key: ciphers
operator: match
value: ^((chacha20-poly1305@openssh\.com|aes(128|256)-gcm@openssh\.com|aes(128|192|256)-ctr)(,|$))+$

macs

Effective MACs list.

key: macs
operator: match
value: ^(hmac-sha2-(256|512)(-etm@openssh\.com)?(,|$))+$

kex

Effective KexAlgorithms list.

key: kexalgorithms
operator: match
value: ^((mlkem768x25519-sha256|sntrup761x25519-sha512(@openssh\.com)?|curve25519-sha256(@libssh\.org)?|ecdh-sha2-nistp(256|384|521)|diffie-hellman-group-exchange-sha256|diffie-hellman-group14-sha256|diffie-hellman-group(16|18)-sha512)(,|$))+$

kex-post-quantum

First key exchange algorithm is a hybrid post-quantum one.

key: kexalgorithms
operator: match
value: ^(mlkem768x25519-sha256|sntrup761x25519-sha512(@openssh\.com)?)(,|$)

Remediation

Create /etc/ssh/sshd_config.d/00-cirsec.conf (mode 0600) with the lists below, removing the algorithms that ssh -Q cipher, ssh -Q mac or ssh -Q kex does not print:

Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr
MACs [email protected],[email protected],hmac-sha2-512,hmac-sha2-256
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected],curve25519-sha256,[email protected],ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256

Check with sshd -t, then run systemctl reload ssh (on 24.04 and later, when ssh.service is not running yet, the next connection starts it with the new settings). Keep an open session until a new connection succeeds.

Restartservice: ssh
Disruptionlow
Lockout riskyes: can cut off administrative access. Off by default in generated scripts and playbooks, opt in through a tailoring file.

Rollback

Remove the three lines from /etc/ssh/sshd_config.d/00-cirsec.conf and run systemctl reload ssh.

algorithms

# CB-UBU-NET-015: set strong SSH server algorithms, keeping only those this OpenSSH version supports.
PATH="$PATH:/usr/sbin"
# sshd -T and -t need /run/sshd, which only exists once ssh.service has run (ssh.socket on 24.04 and later).
[ -d /run/sshd ] || mkdir -m 0755 /run/sshd
f=/etc/ssh/sshd_config.d/00-cirsec.conf
grep -Eqi '^[[:space:]]*Include[[:space:]]+/etc/ssh/sshd_config.d/\*\.conf' /etc/ssh/sshd_config || {
  echo '/etc/ssh/sshd_config does not include sshd_config.d/*.conf' >&2
  exit 1
}
# keep TYPE LIST: the algorithms of LIST that "ssh -Q TYPE" knows, comma separated, in LIST order.
keep() {
  sup=$(ssh -Q "$1") || return 1
  out=
  for a in $(printf '%s' "$2" | tr ',' ' '); do
    printf '%s\n' "$sup" | grep -Fxq -- "$a" && out="${out:+$out,}$a"
  done
  [ -n "$out" ] && printf '%s' "$out"
}
nokeep() {
  echo "ssh -Q $1 failed or knows none of the recommended algorithms" >&2
  exit 1
}
c=$(keep cipher [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr) || nokeep cipher
m=$(keep mac [email protected],[email protected],hmac-sha2-512,hmac-sha2-256) || nokeep mac
k=$(keep kex mlkem768x25519-sha256,sntrup761x25519-sha512,[email protected],curve25519-sha256,[email protected],ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256) || nokeep kex
mkdir -p /etc/ssh/sshd_config.d
cb_backup "$f"
touch "$f"
chmod 600 "$f"
sed -i '/^[[:space:]]*\(ciphers\|macs\|kexalgorithms\)[[:space:]]/Id' "$f"
printf 'ciphers %s\nmacs %s\nkexalgorithms %s\n' "$c" "$m" "$k" >>"$f"
if ! sshd -t; then
  cb_restore "$f"
  echo 'sshd -t rejected the change, reverted' >&2
  exit 1
fi

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
NIST SP 800-53 Rev. 55.1.1SC-8partial
NIST SP 800-53 Rev. 55.1.1SC-13partial

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic, BIOS boot. MACs with UMAC and SHA-1 reported, post-quantum check not applicable; fix.sh writes the lists filtered with ssh -Q; a UMAC client is refused; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic, BIOS boot. MACs and post-quantum order reported; fix.sh puts [email protected] first; a UMAC client is refused; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic, BIOS boot. Checks report an error before the first SSH connection (no /run/sshd), then MACs fail; fix.sh works on a fresh boot; a UMAC client is refused; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic, BIOS boot. MACs reported, mlkem768x25519-sha256 already first; fix.sh writes the lists filtered with ssh -Q; a UMAC client is refused; SSH by key of a non-root user accepted on localhost and port 22 reachable from the lab host after the fix and after reboot.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr