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

As released in v0.1.0 (beta). Current state.

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 -p -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