- 011. Pourquoi durcir OpenSSH en 2026 et pré-requis
- 022. Configuration sshd_config durcie
- 033. FIDO2 SSH : ed25519-sk, verify-required, clés résidentes, Touch ID
- 044. PAM : pam_faillock, TOTP, OATH, Yubico
- 055. auditd : règles SSH, rotation, forward Wazuh
- 066. fail2ban : jails sshd, sshd-ddos, action ipset
- 077. Bastion / jumphost : ProxyJump et SSH CA
- 088. Ce qu'on supprime
- 099. Snippet Ansible pour déployer la conf à un fleet
- 1010. Tester : ssh-audit, lynis, OpenVAS
#1. Pourquoi durcir OpenSSH en 2026 et pré-requis
OpenSSH reste en 2026 la porte d'entrée privilégiée vers les fleets Linux : VM cloud, bastions on-prem, conteneurs LXC, équipements réseau. Les statistiques publiques d'observatoires de bots (Shadowserver, GreyNoise, AbuseIPDB) montrent qu'un service sshd exposé sur Internet absorbe entre 5 000 et 80 000 tentatives d'authentification par jour selon le préfixe IP. Les campagnes de credential stuffing visent désormais en priorité les clés faibles, les versions vulnérables (CVE-2023-38408 sur ssh-agent, CVE-2024-6387 alias regreSSHion sur sshd < 9.8) et les configurations PAM laxistes.
OpenSSH 9.x a apporté plusieurs ruptures importantes : suppression définitive de ssh-rsa SHA-1 dans le négoce par défaut depuis 9.0, désactivation de scp historique au profit de SFTP, support natif des clés FIDO2 résidentes (-O resident), et durcissement des KEX avec sntrup761x25519-sha512 (post-quantique hybride) activé par défaut depuis 9.0. La 9.6 a corrigé la faille Terrapin (CVE-2023-48795) en introduisant la "strict KEX". La 9.8 a corrigé regreSSHion. Toute fleet figée en OpenSSH 8.x cumule donc un retard de sécurité significatif.
Ce guide cible les sysadmins, DevOps et SRE qui exploitent un parc de serveurs Linux (Debian 12, Ubuntu 22.04 / 24.04, RHEL 9, Rocky 9, AlmaLinux 9). Pré-requis :
- OpenSSH 9.6 minimum côté serveur (9.8 recommandée pour regreSSHion)
- Un démon PAM fonctionnel (
/etc/pam.d/sshd) auditdinstallé et démarréfail2ban>= 1.0 (ousshguard, oucrowdsecselon préférence)- Pour FIDO2 :
libfido2>= 1.13 et une clé matérielle (YubiKey 5, SoloKey, NitroKey, OnlyKey, Titan) - Un canal de bascule : console série, IPMI/iDRAC/iLO, ou accès cloud "out of band". Toute manipulation de
sshd_configdoit être effectuée avec une session ouverte de secours pour éviter le lockout.
Les contrôles ISO 27001:2022 directement adressés sont A.8.5 (secure authentication), A.8.16 (monitoring activities) et A.8.20 (networks security). Pour une mise en conformité globale, voir /services/iso-27001.
#2. Configuration sshd_config durcie
Le fichier /etc/ssh/sshd_config.d/00-hardening.conf (préféré à l'édition directe de sshd_config sur les distributions modernes qui utilisent Include /etc/ssh/sshd_config.d/*.conf). Toute directive ci-dessous est commentée pour expliquer le choix.
# /etc/ssh/sshd_config.d/00-hardening.conf
# Durcissement OpenSSH 9.x — base 2026
# --- Réseau et écoute ---
Port 2222 # Réduit le bruit des bots qui scannent le 22 ; n'est pas une mesure de sécurité en soi
AddressFamily inet # IPv4 seul si IPv6 non utilisé ; sinon "any"
ListenAddress 10.42.0.5 # Bind explicite sur l'IP de management
Protocol 2 # SSHv1 mort depuis 2006, mais on l'écrit explicitement
# --- Versions et bannière ---
VersionAddendum none # Pas de fingerprinting facilité
Banner /etc/ssh/banner.txt # Avertissement légal (mention RGPD + journalisation)
DebianBanner no # Sur Debian/Ubuntu : masque la sous-version
# --- Authentification ---
PermitRootLogin no # Root n'a aucune raison de se logger directement
PasswordAuthentication no # Clés uniquement
PermitEmptyPasswords no # Ceinture + bretelles
ChallengeResponseAuthentication no # Désactive l'auth keyboard-interactive sauf via PAM ciblé
KbdInteractiveAuthentication no # Synonyme moderne de la directive ci-dessus
UsePAM yes # Indispensable pour faillock, TOTP, etc.
AuthenticationMethods publickey,keyboard-interactive:pam # 2FA matériel + TOTP PAM
PubkeyAuthentication yes
HostbasedAuthentication no
IgnoreRhosts yes
IgnoreUserKnownHosts yes # Empêche un user d'auto-trustifier des hôtes
# --- Algorithmes (KEX, cipher, MAC, host keys) ---
KexAlgorithms [email protected],curve25519-sha256,[email protected]
HostKeyAlgorithms ssh-ed25519,[email protected],[email protected],rsa-sha2-512,[email protected]
PubkeyAcceptedAlgorithms ssh-ed25519,[email protected],[email protected],rsa-sha2-512,[email protected]
Ciphers [email protected],[email protected],[email protected]
MACs [email protected],[email protected],[email protected]
# --- Clés d'hôte ---
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key # Conservée pour rétro-compat clients legacy ; à retirer si le parc est homogène
# --- Sessions ---
ClientAliveInterval 300 # Ping toutes les 5 min
ClientAliveCountMax 2 # Déconnecte après 10 min d'inactivité réseau
LoginGraceTime 30 # 30s pour s'authentifier, sinon coupe (mitige slowloris SSH)
MaxAuthTries 3 # 3 tentatives par session avant coupure
MaxSessions 4 # Limite les sessions multiplexées par connexion
MaxStartups 10:30:60 # Throttle des connexions concurrentes en cours d'auth
# --- Forwardings et features ---
AllowAgentForwarding no # On n'autorise que sur les bastions, via Match
AllowTcpForwarding no # Idem
GatewayPorts no
PermitTunnel no
X11Forwarding no # X11 = surface d'attaque, dépréciée
PrintMotd no # MotD géré par PAM
TCPKeepAlive no # On préfère ClientAlive (chiffré)
Compression no # Mitige CRIME-like sur SSH
PermitUserEnvironment no # Empêche un user de surcharger l'env via ~/.ssh/environment
PermitUserRC no
# --- Subsystems ---
Subsystem sftp internal-sftp -f AUTHPRIV -l INFO # SFTP intégré, journalisé
# --- Restriction d'accès ---
AllowGroups ssh-users sre admins
DenyUsers root toor admin guest
# --- Logging ---
SyslogFacility AUTHPRIV
LogLevel VERBOSE # VERBOSE log les fingerprints des clés utilisées (utile pour audit)
# --- Match blocs ---
Match Group bastion-jumphost
AllowTcpForwarding yes
PermitOpen 10.0.0.0/8:22 192.168.0.0/16:22
AllowAgentForwarding no
ForceCommand /usr/local/bin/ssh-bastion-shell
Match Address 10.42.0.0/24
AuthenticationMethods publickey
Validation et rechargement :
sshd -t -f /etc/ssh/sshd_config # Test de syntaxe
sshd -T | grep -Ei 'kex|cipher|mac' # Affiche la conf effective
systemctl reload sshd # Reload sans couper les sessions actives
LogLevel VERBOSE est essentiel pour la corrélation : il loggue le fingerprint SHA-256 de la clé publique présentée à chaque connexion réussie, ce qui permet de tracer précisément quelle paire de clés a été utilisée même si plusieurs clés sont autorisées pour un même compte.
#3. FIDO2 SSH : ed25519-sk, verify-required, clés résidentes, Touch ID
OpenSSH 8.2 a introduit les types de clés ecdsa-sk et ed25519-sk qui délèguent la signature à un authentificateur FIDO2 matériel. La clé privée ne quitte jamais le token, ce qui élimine la classe entière des vols de clés par exfiltration de fichier ou compromission d'agent.
#Génération d'une clé FIDO2
# Clé non-résidente (par défaut) : un handle est stocké côté client
ssh-keygen -t ed25519-sk -O verify-required -O application=ssh:prod -C "alice@laptop"
# Clé résidente (stockée dans la mémoire flash du token, portable)
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:prod
Options clés :
-O verify-requiredexige le PIN du token à chaque signature, en plus de la présence physique (touch). Sans cette option, seul le touch est requis : confort accru mais moindre garantie en cas de vol.-O residentpermet de récupérer la clé sur n'importe quel poste avecssh-keygen -K. Pratique pour un workflow multi-machines, mais limite le nombre de slots du token (25 sur YubiKey 5 typiquement).-O application=ssh:prodsegmente l'usage par "scope" : la même clé physique peut héberger des slots distincts pourssh:prod,ssh:lab,ssh:perso.
#Récupération depuis le token
cd ~/.ssh
ssh-keygen -K # Crée id_ed25519_sk_rk + id_ed25519_sk_rk.pub depuis les clés résidentes du token branché
#Côté serveur
Le format de ~/.ssh/authorized_keys est inchangé : la clé publique commence par [email protected]. Pour exiger côté serveur la vérification utilisateur (PIN), on ajoute l'option verify-required directement dans authorized_keys :
verify-required sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29t... alice@laptop
Combinée à PubkeyAuthOptions verify-required dans sshd_config, cette directive impose la vérification utilisateur même si la clé a été générée sans -O verify-required.
#Touch ID sur macOS
Sur macOS, secretive ou ssh-tpm-agent exposent le Secure Enclave comme un agent SSH. Sur Linux, tpm2-pkcs11 ou ssh-tpm-agent fournissent l'équivalent via un TPM 2.0. Ce sont des compléments aux clés FIDO2 matérielles, à privilégier pour les comptes nominatifs.
#Migration progressive
Pour un parc, la migration vers FIDO2 se fait par cohorte :
- Distribuer les tokens, former à
ssh-keygen -t ed25519-sk - Pousser les nouvelles clés publiques via la gestion de configuration (Ansible, Salt, Puppet) en mode additif
- Période de coexistence (30 jours) avec ancien et nouveau jeu de clés
- Retrait des anciennes clés ed25519 logicielles
- Activation finale de
PubkeyAcceptedAlgorithmsrestreint à[email protected],[email protected]
#4. PAM : pam_faillock, TOTP, OATH, Yubico
PAM (Pluggable Authentication Modules) traite la deuxième couche d'authentification : verrouillage anti-bruteforce, OTP, contrôle d'accès horaire. Sur Debian/Ubuntu : /etc/pam.d/sshd. Sur RHEL : /etc/pam.d/sshd qui inclut system-auth et password-auth.
#pam_faillock (anti-bruteforce local)
pam_faillock (remplaçant moderne de pam_tally2) verrouille un compte après N échecs sur une fenêtre glissante. Configuration dans /etc/security/faillock.conf :
deny = 5 # Verrouille après 5 échecs
fail_interval = 900 # Fenêtre de 15 minutes
unlock_time = 1800 # Déverrouillage auto après 30 min
even_deny_root = no # Évite de se locker root
silent # Ne révèle pas le nombre de tentatives restantes
Activation dans /etc/pam.d/sshd (ou via authselect sur RHEL) :
auth required pam_faillock.so preauth
auth sufficient pam_unix.so nullok
auth [default=die] pam_faillock.so authfail
account required pam_faillock.so
Inspection : faillock --user alice affiche les tentatives. Reset : faillock --user alice --reset.
#TOTP avec pam_google_authenticator
apt install libpam-google-authenticator
sudo -u alice google-authenticator -t -d -f -r 3 -R 30 -W
Options : -t TOTP, -d désactive la réutilisation, -r 3 -R 30 rate-limite à 3 tentatives par 30s, -W autorise une fenêtre étendue de tolérance horaire.
Dans /etc/pam.d/sshd, ajouter en tête :
auth required pam_google_authenticator.so nullok
Le flag nullok permet une migration progressive (utilisateurs sans TOTP encore configuré). Une fois la migration achevée, le retirer.
#OATH HOTP/TOTP
pam_oath est l'alternative open source plus standardisée (RFC 4226 / 6238). Configuration via /etc/users.oath :
HOTP/T30 alice - 3132333435363738393031323334353637383930
Dans /etc/pam.d/sshd :
auth requisite pam_oath.so usersfile=/etc/users.oath window=10 digits=6
#Yubico OTP avec pam_yubico
Pour exploiter le mode OTP (long string générée par toucher la YubiKey, validée contre l'API YubiCloud ou un serveur YubiKey Validation Server self-hosted) :
auth required pam_yubico.so id=16 key=LONGAPIKEY authfile=/etc/yubikey_mappings urllist=https://yubico.example.org/wsapi/2.0/verify
/etc/yubikey_mappings contient les associations alice:ccccccaabbbb. Self-hosting du Yubico Validation Server : voir le projet yubico/yubikey-val sur GitHub (PHP, MariaDB).
#AuthenticationMethods côté sshd
Pour combiner clé publique (FIDO2) ET TOTP PAM :
AuthenticationMethods publickey,keyboard-interactive:pam
L'utilisateur s'authentifie d'abord par sa clé matérielle (touch + PIN), puis fournit son TOTP. Defense in depth.
#5. auditd : règles SSH, rotation, forward Wazuh
auditd capture les événements kernel via le sous-système audit. Pour SSH, on cible :
- Modifications des fichiers de conf et clés
- Appels exec de
sshd - Événements de connexion/déconnexion (
USER_LOGIN,USER_AUTH,CRED_ACQ)
#Règles /etc/audit/rules.d/50-ssh.rules
# Modification de la configuration SSH
-w /etc/ssh/sshd_config -p wa -k ssh_config
-w /etc/ssh/sshd_config.d/ -p wa -k ssh_config
-w /etc/ssh/ssh_config -p wa -k ssh_config
# Modification des clés d'hôte
-w /etc/ssh/ssh_host_ed25519_key -p wa -k ssh_hostkey
-w /etc/ssh/ssh_host_rsa_key -p wa -k ssh_hostkey
# authorized_keys de tous les users (via /home et /root)
-w /root/.ssh/ -p wa -k ssh_authkeys
-w /home -p wa -k ssh_authkeys
# PAM
-w /etc/pam.d/sshd -p wa -k pam_ssh
-w /etc/security/faillock.conf -p wa -k pam_ssh
# Exec de sshd (utile pour repérer les sshd "shadow")
-a always,exit -F arch=b64 -S execve -F path=/usr/sbin/sshd -k ssh_exec
Recharger : augenrules --load && systemctl restart auditd.
#Recherches utiles
ausearch -k ssh_config -ts today
ausearch -k ssh_authkeys -ts today --format text
aureport --auth --summary -ts week
#Rotation et rétention
/etc/audit/auditd.conf :
max_log_file = 200 # MB
num_logs = 10
max_log_file_action = ROTATE
space_left = 500
space_left_action = SYSLOG
disk_full_action = HALT # Sur systèmes critiques uniquement
Pour des serveurs sensibles, disk_full_action = HALT est cohérent avec une politique "no log = no service" (alignée avec ANSSI ANSSI-PA-076). Pour des charges moins sensibles, SUSPEND suffit.
#Forward vers Wazuh / SIEM
Wazuh ingère nativement les logs auditd via son agent (<localfile><log_format>audit</log_format>). Pour un SIEM générique, auditd peut écrire en JSON via audisp-json (ou plugin audisp-syslog vers rsyslog → Loki / Elasticsearch / OpenSearch).
Exemple de règle Wazuh pour détecter une modification non-attendue de authorized_keys :
<rule id="100201" level="10">
<if_sid>80700</if_sid>
<field name="audit.key">ssh_authkeys</field>
<description>Modification d'authorized_keys hors fenêtre de change</description>
</rule>
#6. fail2ban : jails sshd, sshd-ddos, action ipset
fail2ban parse les logs (/var/log/auth.log ou journald) et bannit dynamiquement les IP fautives via iptables, nftables ou ipset.
#Jail principale /etc/fail2ban/jail.d/sshd.local
[DEFAULT]
backend = systemd
banaction = nftables-multiport
banaction_allports = nftables-allports
ignoreip = 127.0.0.1/8 ::1 10.42.0.0/24
findtime = 10m
bantime = 1h
bantime.increment = true
bantime.factor = 4
bantime.maxtime = 14d
[sshd]
enabled = true
port = 2222
filter = sshd
maxretry = 3
findtime = 10m
bantime = 2h
[sshd-ddos]
enabled = true
port = 2222
filter = sshd-ddos
maxretry = 5
findtime = 1m
bantime = 24h
bantime.increment = true avec factor = 4 augmente exponentiellement la durée de ban à chaque récidive : 2h, 8h, 32h, 5 jours, plafonné à 14 jours par maxtime. Une IP qui revient inlassablement finit donc bloquée sur le très long terme.
#Action ipset (haute volumétrie)
Sur un bastion exposé, iptables avec une règle par IP devient inefficace au-delà de quelques milliers d'entrées. ipset stocke les IP dans une structure de hash performante :
banaction = nftables-allports[type=set, blocktype=drop]
Ou en iptables :
banaction = iptables-ipset-proto6-allports
Avec ipset list f2b-sshd | wc -l pour visualiser la taille du set.
#Whitelist via DNSBL / threat feed
ignoreip peut référencer un fichier rechargé périodiquement, alimenté par un script qui agrège des whitelist internes (IP de bastions, monitoring, CI/CD).
#Métriques
fail2ban-client status sshd retourne le nombre d'IP bannies. À exporter vers Prometheus via fail2ban-prometheus-exporter pour graphes Grafana et alerting.
#7. Bastion / jumphost : ProxyJump et SSH CA
L'architecture cible élimine les accès SSH directs aux serveurs de production. Tous les flux passent par un bastion dédié, avec authentification certificat SSH (et non plus clés statiques).
#ProxyJump côté client
# ~/.ssh/config
Host bastion-prod
HostName bastion.prod.internal
User alice
Port 2222
IdentityFile ~/.ssh/id_ed25519_sk_rk
IdentitiesOnly yes
Host *.prod.internal
ProxyJump bastion-prod
User alice
IdentityFile ~/.ssh/id_ed25519_sk_rk
ProxyJump (alias -J) crée un tunnel TCP via le bastion sans jamais y exposer la clé privée du client (contrairement à ProxyCommand historique avec agent forwarding).
#SSH Certificate Authority
OpenSSH supporte une vraie PKI SSH depuis 5.4 (2010). Le principe :
- Une CA SSH (paire de clés ed25519, conservée hors-ligne ou dans un HSM)
- La CA signe des certificats utilisateur de courte durée (8h typiquement)
- Les serveurs sont configurés avec
TrustedUserCAKeys /etc/ssh/ca.pubet acceptent tout certificat valide signé par cette CA - La CA signe également les clés d'hôte des serveurs, le client connaît la CA via
~/.ssh/known_hosts(@cert-authority *.prod.internal ssh-ed25519 AAAA...)
#Génération de la CA
ssh-keygen -t ed25519 -f /secure/ssh-ca-user -C "ssh-ca-user-2026"
ssh-keygen -t ed25519 -f /secure/ssh-ca-host -C "ssh-ca-host-2026"
Les clés privées de CA sont à protéger : HSM (pkcs11), Vault PKI Secret Engine, ou stockage offline avec procédure d'astreinte.
#Signature d'un certificat user (8h)
ssh-keygen -s /secure/ssh-ca-user \
-I "alice-2026-05-03T1430" \
-n alice,sre,prod-readonly \
-V +8h \
-O clear \
-O permit-pty \
-O source-address=10.42.0.0/24 \
~/.ssh/id_ed25519_sk_rk.pub
-Iidentité du certificat (loggée côté serveur)-nprincipals : les comptes Unix sur lesquels ce certificat est valide-V +8hvalidité de 8 heures-O source-addressrestriction réseau
Le certificat généré (id_ed25519_sk_rk-cert.pub) est posé à côté de la clé publique. SSH le présente automatiquement.
#Côté serveur
# /etc/ssh/sshd_config.d/00-hardening.conf
TrustedUserCAKeys /etc/ssh/ca-user.pub
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
RevokedKeys /etc/ssh/revoked-keys
RevokedKeys est la KRL (Key Revocation List), générée par ssh-keygen -kf /etc/ssh/revoked-keys -u -s /secure/ssh-ca-user.pub après ajout d'identités révoquées.
#Automatisation : Vault, Smallstep CA, Teleport
En production, la signature manuelle est remplacée par :
- HashiCorp Vault SSH Secret Engine : authentification de l'utilisateur sur Vault (OIDC / LDAP), Vault signe à la volée un certificat de courte durée
- smallstep
step-ca: open source, avec provisioner OIDC, ACME, JWK - Teleport : surcouche complète (audit session recording inclus)
Tous trois exposent une CLI ou une API qui réduit la TTL effective des secrets longue durée à zéro.
#Audit
LogLevel VERBOSE côté serveur loggue :
Accepted publickey for alice from 10.42.0.5 port 51234 ssh2: ED25519-CERT SHA256:... ID alice-2026-05-03T1430 (serial 0) CA ED25519 SHA256:...
L'identité du certificat (alice-2026-05-03T1430) est unique et corrélable avec les logs d'émission de la CA. Tracabilité complète sans gestion individuelle des authorized_keys.
#8. Ce qu'on supprime
Le hardening passe autant par ce qu'on retire que par ce qu'on ajoute. Liste explicite des éléments à bannir :
PasswordAuthentication yes: zéro raison de le maintenir en 2026, tout poste utilisateur peut générer une paire de clés en 30 secondesPermitRootLogin yesouprohibit-password: jamais. Sudo nominatif, avec NOPASSWD limité aux commandes strictement nécessairesX11Forwarding yes: surface d'attaque (CVE historiques sur le forwarding X11), inutile sauf cas industriel précis- Algorithmes faibles :
- Ciphers CBC :
aes128-cbc,aes256-cbc,3des-cbc— vulnérables à des attaques de type Terrapin et oracle padding - RC4 :
arcfour,arcfour128,arcfour256— cassés - MAC SHA-1 :
hmac-sha1,hmac-sha1-96— collision possible - MAC non-ETM : préférer systématiquement les variantes
*[email protected](Encrypt-then-MAC)
- Ciphers CBC :
- KEX faibles :
diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,gss-*(sauf usage Kerberos explicite) - Host key algorithms :
ssh-rsa(SHA-1),ssh-dss— interdire au profit dessh-ed25519etrsa-sha2-512 AllowAgentForwarding yespartout : à n'activer que sur les bastions, et idéalement remplacer par ProxyJumpPermitTunnel yes: sauf VPN SSH expliciteUseDNS yes: ajoute de la latence et des dépendances DNS au login, sans valeur sécurité réelle- Comptes inutilisés avec clés : auditer
~/.ssh/authorized_keysde tous les comptes (find /home /root -name authorized_keys -ls) et purger les clés orphelines
Vérification post-modif :
ssh -Q kex # Liste les KEX supportés par le binaire client
ssh -Q cipher # Liste les ciphers supportés
sshd -T | grep -E '^(kex|cipher|mac|host|pubkey)algorithms' # Conf effective serveur
#9. Snippet Ansible pour déployer la conf à un fleet
Rôle Ansible minimaliste pour pousser la conf hardening sur un parc. Structure roles/ssh-hardening/ :
# roles/ssh-hardening/tasks/main.yml
---
- name: Vérifier la version OpenSSH minimale
ansible.builtin.command: ssh -V
register: ssh_version
changed_when: false
failed_when: false
- name: Echec si OpenSSH < 9.6
ansible.builtin.fail:
msg: "OpenSSH {{ ssh_version.stderr }} trop ancien, 9.6+ requis"
when: ssh_version.stderr is not search('OpenSSH_9\.([6-9]|1[0-9])')
- name: Déposer la conf hardening
ansible.builtin.template:
src: 00-hardening.conf.j2
dest: /etc/ssh/sshd_config.d/00-hardening.conf
owner: root
group: root
mode: "0600"
validate: "/usr/sbin/sshd -t -f %s"
notify: reload sshd
- name: Déposer la bannière légale
ansible.builtin.copy:
src: banner.txt
dest: /etc/ssh/banner.txt
owner: root
group: root
mode: "0644"
- name: Déployer la CA SSH user trustée
ansible.builtin.copy:
src: "files/ca-user-{{ ssh_ca_generation }}.pub"
dest: /etc/ssh/ca-user.pub
owner: root
group: root
mode: "0644"
notify: reload sshd
- name: Pousser les règles auditd SSH
ansible.builtin.copy:
src: 50-ssh.rules
dest: /etc/audit/rules.d/50-ssh.rules
owner: root
group: root
mode: "0640"
notify: reload auditd
- name: Pousser la jail fail2ban sshd
ansible.builtin.template:
src: jail-sshd.local.j2
dest: /etc/fail2ban/jail.d/sshd.local
owner: root
group: root
mode: "0644"
notify: reload fail2ban
- name: faillock.conf
ansible.builtin.template:
src: faillock.conf.j2
dest: /etc/security/faillock.conf
owner: root
group: root
mode: "0644"
- name: Configurer pam.d/sshd avec pam_faillock + pam_google_authenticator
ansible.builtin.template:
src: pam-sshd.j2
dest: /etc/pam.d/sshd
owner: root
group: root
mode: "0644"
backup: true
- name: Group ssh-users existe
ansible.builtin.group:
name: ssh-users
state: present
- name: Smoke test post-deploy
ansible.builtin.command: sshd -T
changed_when: false
register: sshd_effective
- name: Asserter que PasswordAuthentication est off
ansible.builtin.assert:
that:
- "'passwordauthentication no' in sshd_effective.stdout"
- "'permitrootlogin no' in sshd_effective.stdout"
- "'kbdinteractiveauthentication no' in sshd_effective.stdout or 'authenticationmethods publickey,keyboard-interactive:pam' in sshd_effective.stdout"
# roles/ssh-hardening/handlers/main.yml
---
- name: reload sshd
ansible.builtin.systemd:
name: sshd
state: reloaded
- name: reload auditd
ansible.builtin.command: augenrules --load
changed_when: true
- name: reload fail2ban
ansible.builtin.systemd:
name: fail2ban
state: restarted
Stratégie de déploiement :
# playbook deploy-ssh-hardening.yml
- hosts: linux_fleet
serial: "10%" # Par vagues de 10% du fleet
max_fail_percentage: 0 # Stop dès le premier échec
any_errors_fatal: true
pre_tasks:
- name: Vérifier qu'une session de secours existe
ansible.builtin.assert:
that: ansible_user is defined
roles:
- ssh-hardening
L'option serial: "10%" couplée à max_fail_percentage: 0 bloque le déploiement à la première erreur, évitant un lockout massif. En complément, un job cron de healthcheck (ssh depuis un nœud bastion vers chaque cible, avec timeout 10s) permet une détection rapide.
#10. Tester : ssh-audit, lynis, OpenVAS
#ssh-audit
L'outil de référence (jtesta/ssh-audit sur GitHub, Python). Usage :
pip install ssh-audit
ssh-audit -L # Mode listing des conf hardening de référence
ssh-audit bastion.prod.internal:2222
ssh-audit --policy=hardened_openssh_d2024_01 bastion.prod.internal:2222
Un score [pass] sur tous les algorithmes et un grade A signalent une conf cohérente. Les warnings typiques portent sur la conservation de RSA pour rétro-compat, à arbitrer.
#Lynis
Audit système global (CISOFY). Section SSH spécifique :
lynis audit system --tests-from-group ssh
Lynis vérifie ~30 directives sshd_config et émet des recommandations alignées sur les benchmarks CIS.
#OpenVAS / GVM
Pour un scan vulnérabilité réseau, OpenVAS (Greenbone) couvre les CVE OpenSSH connues (regreSSHion, Terrapin, etc.) et signale les versions en retard. Lancement via la CLI :
gvm-cli socket --xml '<create_target><name>fleet-prod</name><hosts>10.42.0.0/24</hosts></create_target>'
#CIS Benchmark
Pour une vérification industrielle, le CIS Distribution Independent Linux Benchmark v2.0 contient une section 5.x SSH avec ~40 contrôles. L'outil cis-cat-pro ou des rôles communautaires (dev-sec.ssh-hardening sur Ansible Galaxy) automatisent l'évaluation.
#Test FIDO2 manuel
ssh -v [email protected] 2>&1 | grep -E '(Authenticator|sk-)'
Doit afficher :
debug1: Server accepts key: ... ED25519-SK SHA256:...
debug1: Authenticator requires user verification
#Validation continue
L'ensemble de ces outils gagne à être intégré en CI :
# .gitlab-ci.yml ou GitHub Actions
ssh-audit:
stage: verify
script:
- ssh-audit --policy=hardened_openssh_d2024_01 ${TARGET}:${PORT}
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
Un job hebdomadaire planifié contre l'ensemble de la fleet détecte rapidement toute régression de configuration (ex : un déploiement mal qualifié qui réintroduit un cipher CBC). Les résultats peuvent être exportés vers le SIEM via webhook ou agrégés dans Wazuh comme événements custom pour cartographier la dette de durcissement à l'échelle du parc.
Cet article vous parle ? On accompagne PME, ESN et éditeurs SaaS dans leur conformité ISO 27001 / NIS2 — Lead Auditor certifié, tarifs publics, 100 % open source. Découvrir notre SOC managé open source →
Cet article vous parle ?
On accompagne PME, ESN et éditeurs SaaS dans leur conformité ISO 27001 / NIS2 — Lead Auditor certifié, tarifs publics, 100 % open source.