Vous êtes en pleine modification en production. Vous lancez une commande de plus, attendez la sortie… et le terminal se fige. Quelques secondes plus tard : « Broken pipe » ou « Connection reset ». Votre session SSH ne s’est pas « arrêtée » par hasard. Quelque chose sur le chemin a décidé que votre flux TCP silencieux ressemblait à du poids mort.
Ubuntu 24.04 n’est pas spécialement maudit ici, mais il est assez moderne (paramètres OpenSSH par défaut, systemd, NAT omniprésent, pare-feu agressifs) pour que de vieux conseils comme « set TCPKeepAlive yes » tombent souvent à plat. Soyons pratiques : quoi vérifier, quoi modifier et ce qu’il vaut mieux ne pas toucher si vous voulez des sessions SSH qui survivent aux réseaux d’entreprise, aux load balancers cloud et aux routeurs domestiques qui se prennent pour du matériel professionnel.
Comment les sessions SSH meurent vraiment
SSH n’est « que du TCP » avec beaucoup de cryptographie et un peu d’espoir. Quand votre session saute, l’un des événements suivants s’est produit :
- Timeout d’inactivité au milieu : un pare-feu/NAT/load balancer décide qu’un flux TCP inactif est expendable et oublie la mapping.
- Instabilité du chemin : roaming Wi‑Fi, renégociation VPN, basculement cellulaire, incident ISP. La connexion TCP ne peut pas se rétablir.
- Une des parties abandonne : le client ou le serveur considère le pair comme mort et ferme la socket.
- Interruption par ressource ou politique : sshd redémarre, un hôte reboot, un agent de sécurité tue les sessions longues, ou des politiques PAM/systemd/logind terminent des sessions.
- Problèmes PMTU/fragmentation : des paquets sont mis en black‑hole, les keepalives n’aboutissent pas, les retransmissions s’accumulent puis l’application expire.
« Keepalive » n’est pas une seule fonctionnalité. C’est une famille de timers et sondes à différents niveaux :
- Keepalives au niveau protocole SSH (OpenSSH) : messages chiffrés au niveau applicatif qui voyagent comme du vrai trafic et peuvent détecter un pair mort.
- Keepalives TCP (noyau) : sondes TCP non chiffrées qui peuvent ou non traverser les NAT/pare-feu comme vous l’imaginez.
- Expiration des sessions NAT/pare-feu : l’équipement intermédiaire décide combien de temps l’« inactivité » est autorisée.
Quand quelqu’un dit « SSH se coupe au hasard », traduisez par : « quelque chose met fin à mon flux inactif plus tôt que prévu ». Votre travail est d’identifier quelle entité et ensuite choisir la solution la moins chère et la plus sûre pour garder le flux non inactif ou le récupérer rapidement.
Un dernier rappel : les keepalives ne remplacent pas un flux de travail résilient. Ils réduisent la douleur. Ils ne rendent pas TCP immortel.
Faits intéressants et contexte historique (trivia utile)
- SSH est né en réponse aux connexions distantes non sécurisées. Les premiers accès distants reposaient sur des protocoles en clair ; l’essor de SSH a autant concerné la santé mentale que la sécurité.
- OpenSSH n’a pas inventé les keepalives ; il les a professionnalisés. Le mécanisme de keepalive au niveau protocole consiste essentiellement à « envoyer quelque chose de légitime et attendre la preuve que le pair est vivant ».
- Les valeurs par défaut des keepalives TCP sont notoirement inutiles pour les humains. De nombreux systèmes utilisaient historiquement ~2 heures avant de commencer les sondes, ce qui est parfait pour des sockets de base de données longue durée et catastrophique pour une pause café.
- Les appareils NAT ne sont pas des spectateurs neutres. Ils maintiennent un état par flux, et cet état expire. Le matériel grand public est souvent plus agressif que les pare-feu d’entreprise.
- « Broken pipe » est votre shell qui rapporte SIGPIPE. La socket sous-jacente est morte ; votre prochaine écriture échoue ; votre application terminale vous l’annonce à sa façon dramatique.
- Les pare-feu stateful ont popularisé les « timeouts d’inactivité » comme valve de sécurité. Suivre tous les flux indéfiniment coûte de la mémoire ; les timeouts sont un ramasse‑miettes grossier.
- Certains load balancers ont des timeouts distincts pour TCP et HTTP. Si vous faites transiter SSH par un appareil optimisé pour HTTP, il peut traiter votre flux TCP longue durée comme un meuble suspect.
- Le multiplexage SSH (ControlMaster) peut masquer des coupures. Vos « nouvelles » sessions peuvent être de nouveaux canaux sur une vieille socket TCP qui est sur le point de mourir.
- IPv6 change la donne des middleboxes, mais ne l’élimine pas. Moins de NAT ne signifie pas absence de filtrage stateful, et les réseaux d’entreprise appliquent toujours des timeouts aux flux inactifs.
Playbook de diagnostic rapide
Premier point : décider si le serveur ou le réseau a tué la session
- Côté client, reproduisez avec un logging verbeux et regardez le timing et qui a initié la fermeture.
- Côté serveur, corrélez les logs sshd autour de l’horodatage de la déconnexion.
- Si rien n’apparaît dans les logs, supposez qu’un middlebox a supprimé l’état et que votre paquet suivant a atteint le vide.
Deuxième point : identifier « timeout d’inactivité » vs « instabilité de chemin »
- Si ça meurt après une fenêtre d’inactivité prévisible (10 min, 30 min, 60 min), c’est une politique de timeout quelque part.
- Si ça tombe durant un mouvement (VPN on/off, roaming Wi‑Fi), c’est de l’instabilité du chemin ; les keepalives ne résoudront pas totalement le problème, mais ils peuvent raccourcir le délai de détection.
Troisième point : choisir la solution la moins chère
- ServerAlive* côté client si vous avez juste besoin de stabiliser vos propres sessions et ne pouvez pas changer les serveurs.
- ClientAlive* côté serveur si vous administrez les serveurs et voulez un comportement cohérent pour tout le monde.
- Keepalive TCP du noyau si vous avez aussi besoin que d’autres applications non‑SSH restent stables, ou si vos probes SSH sont bloquées (rare mais réel).
Quatrième point : confirmer la frontière de timeout du chemin
Ne devinez pas. Mesurez. Si le réseau tue les flux inactifs ~900 secondes, envoyer des keepalives toutes les 60 secondes suffit ; toutes les 5 secondes, c’est du spectacle avec du surcoût.
Tâches pratiques : 12+ vérifications réelles avec commandes, signification des sorties, décisions
Ces étapes sont volontairement « à la manière d’un opérateur » : exécutez une commande, interprétez-la, prenez une décision. Gardez les horodatages. Ne vous fiez pas aux impressions.
Task 1: Capture d’une session qui échoue avec la verbosité SSH
cr0x@server:~$ ssh -vvv ops@db-prod-1
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024
debug1: Server host key: ssh-ed25519 SHA256:...
debug1: Authenticating to db-prod-1:22 as 'ops'
...
debug1: client_input_channel_req: channel 0 rtype keepalive@openssh.com reply 1
...
Write failed: Broken pipe
Ce que cela signifie : Si vous voyez des messages réguliers keepalive@openssh.com et que la session tombe malgré tout, votre intervalle de keepalive peut être trop long pour le chemin, ou le chemin est instable (pas seulement de l’expiration d’inactivité).
Décision : Si les drops surviennent sans logs de keepalive, activez ServerAliveInterval côté client. Si des keepalives existent mais que les coupures persistent, réduisez l’intervalle et enquêtez sur les événements réseau (VPN/Wi‑Fi) et les logs serveur.
Task 2: Vérifier si vous utilisez le multiplexage SSH (et si vous faites confiance par erreur à une socket périmée)
cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'controlmaster|controlpath|controlpersist'
controlmaster auto
controlpath /home/cr0x/.ssh/cm-%r@%h:%p
controlpersist 10m
Ce que cela signifie : Vos connexions SSH « nouvelles » peuvent réutiliser une connexion TCP sous-jacente. Si ce flux TCP est tué silencieusement, vous verrez des échecs bizarres dans plusieurs onglets.
Décision : Pour les réseaux instables, envisagez de réduire ControlPersist ou de désactiver le multiplexage pour cet hôte afin de rendre les échecs plus évidents et de réduire l’ampleur des incidents.
Task 3: Voir les raisons de déconnexion côté serveur dans les logs (journalctl)
cr0x@server:~$ sudo journalctl -u ssh -S "2025-12-29 09:00" -U "2025-12-29 10:00" --no-pager | tail -n 20
Dec 29 09:41:12 db-prod-1 sshd[21877]: Connection closed by 10.20.4.18 port 53122 [preauth]
Dec 29 09:41:18 db-prod-1 sshd[21903]: Received disconnect from 10.20.4.18 port 53144:11: disconnected by user
Dec 29 09:52:07 db-prod-1 sshd[22011]: Timeout, client not responding from 10.20.4.18 port 53410
Dec 29 09:52:07 db-prod-1 sshd[22011]: Disconnecting: Timeout, client not responding
Ce que cela signifie : « Connection closed by » peut venir du client, ou du client réagissant à un chemin mort. « Timeout, client not responding » indique que le keepalive côté serveur (ClientAlive*) s’est déclenché ou qu’il y a un chemin client mort.
Décision : Si le serveur met les clients en timeout, ajustez ClientAliveInterval/ClientAliveCountMax. Si le client a « fermé », concentrez-vous sur les middleboxes et les paramètres côté client.
Task 4: Vérifier la configuration effective de sshd (ne pas faire confiance au fichier)
cr0x@server:~$ sudo sshd -T | egrep -i 'clientalive|tcpkeepalive|kex|loglevel'
clientaliveinterval 0
clientalivecountmax 3
tcpkeepalive yes
loglevel INFO
Ce que cela signifie : ClientAliveInterval 0 signifie que le serveur n’enverra pas de keepalives au niveau SSH. Beaucoup supposent que TCPKeepAlive yes suffit. Ce n’est souvent pas le cas.
Décision : Si vous administrez le serveur, définissez ClientAliveInterval sur une valeur sensée (exemples plus bas). Si vous ne le pouvez pas, utilisez ServerAliveInterval côté client.
Task 5: Confirmer la configuration effective SSH du client (comprend les blocs Match)
cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'serveralive|tcpkeepalive|ipqos'
serveraliveinterval 0
serveralivecountmax 3
tcpkeepalive yes
ipqos lowdelay throughput
Ce que cela signifie : Aucun keepalive côté client n’est activé. Si le réseau coupe les flux inactifs, vous êtes en danger.
Décision : Ajoutez ServerAliveInterval globalement ou par hôte (et un ServerAliveCountMax raisonnable).
Task 6: Mesurer la fenêtre d’échec avec un test d’inactivité contrôlé
cr0x@server:~$ date; ssh -o ServerAliveInterval=0 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 3600'
Mon Dec 29 10:02:01 UTC 2025
connected
Write failed: Broken pipe
Ce que cela signifie : Sans keepalives, la session meurt pendant le sleep d’une heure. Notez l’horodatage de la coupure ; répétez plusieurs fois et vous verrez généralement une fenêtre serrée (par ex. ~15 minutes).
Décision : Si le seuil est cohérent, réglez les keepalives pour qu’ils interviennent bien avant cette limite.
Task 7: Répéter avec un keepalive SSH agressif pour valider l’hypothèse
cr0x@server:~$ date; ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 3600; echo done'
Mon Dec 29 10:10:44 UTC 2025
connected
done
Ce que cela signifie : Si cela tient, votre problème est quasi-certainement une perte d’état d’inactivité dans le chemin réseau.
Décision : Choisissez une valeur moins agressive mais sûre (30 s parfois; 60 s souvent; 5 s est généralement du spectacle).
Task 8: Inspecter les valeurs par défaut des keepalives TCP (serveur)
cr0x@server:~$ sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
Ce que cela signifie : Première sonde après 2 heures. Ce n’est pas un « keepalive » utile ; c’est de l’archéologie.
Décision : Si vous avez besoin de keepalives au niveau noyau pour plusieurs services, baissez ces valeurs — mais faites‑le consciemment et documentez l’impact.
Task 9: Vérifier si la session TCP montre des retransmissions ou des blocages (ss)
cr0x@server:~$ ss -tinp '( sport = :22 )' | head -n 12
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 10.10.8.21:22 10.20.4.18:53144 users:(("sshd",pid=21903,fd=4))
cubic wscale:7,7 rto:204 rtt:0.356/0.112 ato:40 mss:1448 pmtu:1500 rcvmss:1392 advmss:1448 cwnd:10 bytes_sent:104925 bytes_acked:104901 bytes_received:20631 segs_out:161 segs_in:149 send 325Mbit/s lastsnd:2800 lastrcv:2800 lastack:2800
Ce que cela signifie : lastsnd/lastrcv en millisecondes indique le temps depuis le dernier trafic. Si vous voyez retrans monter ou rto exploser, vous avez un problème de qualité de chemin, pas seulement d’inactivité.
Décision : Si les retransmissions augmentent près de la déconnexion, investiguez MTU/VPN/Wi‑Fi. Les keepalives ne sauveront pas un chemin black‑holé.
Task 10: Chercher des problèmes MTU/PMTU (ping avec DF)
cr0x@server:~$ ping -M do -s 1472 -c 3 db-prod-1
PING db-prod-1 (10.10.8.21) 1472(1500) bytes of data.
1472 bytes from 10.10.8.21: icmp_seq=1 ttl=63 time=0.512 ms
1472 bytes from 10.10.8.21: icmp_seq=2 ttl=63 time=0.487 ms
1472 bytes from 10.10.8.21: icmp_seq=3 ttl=63 time=0.499 ms
--- db-prod-1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2040ms
Ce que cela signifie : Cela suggère qu’un chemin 1500 octets fonctionne. Si ceci échoue mais que des tailles plus petites réussissent, vous pourriez avoir du PMTU blackholing (commun avec certains VPNs ou tunnels mal configurés).
Décision : Si PMTU est suspect, corrigez le réseau/MTU ; ne maquillez pas le problème avec des keepalives.
Task 11: Confirmer si un pare-feu sur le serveur fait quelque chose de « utile »
cr0x@server:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN 10.20.0.0/16
Ce que cela signifie : UFW ne va généralement pas couper des connexions établies au hasard, mais un logging intensif ou des règles nftables additionnelles peuvent le faire. C’est une vérification de sanity, pas une preuve irréfutable.
Décision : Si vous voyez des rate-limits, une saturation du tracking des connexions ou des règles « recent » agressives, fouillez les compteurs nftables et l’utilisation de conntrack.
Task 12: Vérifier la pression sur conntrack (l’épuisement de la table d’état peut ressembler à des coupures aléatoires)
cr0x@server:~$ sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 198432
net.netfilter.nf_conntrack_max = 262144
Ce que cela signifie : Si nf_conntrack_count est proche de max, des flux établis peuvent être évincés ou de nouveaux flux échouer. Symptômes SSH : coupures intermittentes ou impossibilité de se reconnecter.
Décision : Si vous êtes proche du plafond, corrigez la charge (trop de flux courts), augmentez la taille de la table ou déplacez le filtrage stateful vers un appareil conçu pour cela.
Task 13: Vérifier si sshd est redémarré (et tue les sessions)
cr0x@server:~$ systemctl status ssh --no-pager
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-12-29 08:01:12 UTC; 2h 15min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 1123 (sshd)
Tasks: 1 (limit: 18920)
Memory: 6.9M
CPU: 1.421s
CGroup: /system.slice/ssh.service
└─1123 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
Ce que cela signifie : Si l’uptime est court et correspond aux coupures, vous cherchez la mauvaise chose : un redémarrage tue les sessions. Gestion de configuration, mises à jour automatiques ou watchdog peuvent être en cause.
Décision : Si des redémarrages corrèlent, corrigez d’abord le comportement de redémarrage. Les keepalives ne vont pas défier un redémarrage de démon.
Task 14: Vérifier la version OpenSSH et la politique crypto (rare, mais pertinent pour des cas de renégociation)
cr0x@server:~$ ssh -V
OpenSSH_9.6p1 Ubuntu-3ubuntu13, OpenSSL 3.0.13 30 Jan 2024
Ce que cela signifie : Ubuntu 24.04 embarque un OpenSSH moderne. C’est bien. Cela signifie aussi que d’anciennes hypothèses d’interopérabilité peuvent échouer dans des environnements limites.
Décision : Si seuls des clients anciens tombent, testez avec des clients mis à jour ou ajustez les algorithmes hôte-spécifiques, mais ne fragilisez pas la sécurité globale pour un antique.
Task 15: Prouver que c’est de l’« inactivité » en générant un trafic à faible impact pendant une session
cr0x@server:~$ ssh ops@db-prod-1 'while true; do date +"%T"; sleep 120; done'
10:31:02
10:33:02
10:35:02
Ce que cela signifie : Si cela tient indéfiniment alors que votre session interactive meurt quand vous arrêtez de taper, vous avez confirmé que c’est un problème de timeout d’inactivité, pas d’instabilité générale.
Décision : Corrigez le comportement d’inactivité avec des keepalives SSH ou des changements de politique réseau ; ne perdez pas de temps à chasser la charge CPU, sauf si les logs l’indiquent.
Boutons keepalive qui comptent vraiment (client, serveur, TCP)
Côté client : ServerAliveInterval et ServerAliveCountMax
C’est la solution la plus efficace et la moins politique parce qu’elle ne nécessite l’accès qu’à votre propre machine. Elle envoie périodiquement un message au niveau SSH sur le canal chiffré. Si le serveur (ou le chemin) est mort, le client le remarquera après quelques réponses manquantes et se déconnectera. C’est une fonctionnalité : elle échoue rapidement au lieu de rester bloquée une demi‑heure.
Comment ça marche :
ServerAliveIntervaldéfinit la fréquence (en secondes) d’envoi d’une requête keepalive par le client lorsque aucune donnée n’a été reçue.ServerAliveCountMaxdéfinit le nombre de keepalives sans réponse avant d’abandonner.
À quoi ça sert :
- Maintenir l’état NAT/pare-feu chaud (parce que c’est du vrai trafic).
- Détecter rapidement les sessions mortes et vous rendre un prompt auquel vous pouvez vous reconnecter.
Ce que ce n’est pas : Cela ne préservera pas l’état de votre terminal lors d’un changement d’IP. Si vous faites du roaming, envisagez un outil conçu pour ça (voir FAQ). Les keepalives SSH sont une ceinture de sécurité, pas de la téléportation.
Côté serveur : ClientAliveInterval et ClientAliveCountMax
Si vous administrez les serveurs, les keepalives côté serveur concernent la politique et l’hygiène. Vous dites au serveur de sonder les clients inactifs et d’évincer les sessions mortes. Cela aide à :
- Nettoyer les sessions zombies quand les clients disparaissent derrière des réseaux défaillants.
- Maintenir l’état à travers les middleboxes (même avantage que côté client, mais initié depuis l’autre extrémité).
Il y a un compromis : trop agressif, et vous tuerez des sessions valides sur des chemins à haute latence ou temporairement congestionnés. Trop laxiste, et vous retombez sur des blocages mystérieux.
Keepalives TCP du noyau : TCPKeepAlive et sysctls
OpenSSH propose TCPKeepAlive (client et serveur). Quand activé, il laisse le noyau envoyer des sondes TCP selon les timers du noyau.
Voici le piège : les valeurs par défaut des keepalives noyau sont généralement trop lentes, et certains NAT/pare‑feu traitent les sondes TCP différemment du vrai trafic applicatif. De plus, les keepalives TCP peuvent maintenir une mapping cassée « à moitié vivante » assez longtemps pour vous embrouiller. Les keepalives au niveau SSH sont en général plus clairs et plus configurables par hôte.
Quand je recommande réellement d’ajuster les keepalives noyau :
- Vous avez plusieurs services TCP longue durée (pas seulement SSH) qui souffrent des mêmes drops d’inactivité.
- Vous ne pouvez pas garantir l’application de configs SSH (fleet de clients, laptops non gérés).
- Vous standardisez le comportement dans un environnement contrôlé (bastions, jump hosts).
Économie des timeouts : choisir les intervalles selon le middlebox le plus strict
Si un pare-feu expire un TCP inactif à 10 minutes, un keepalive à 5 minutes fonctionne. Un keepalive à 30 secondes fonctionne aussi, mais c’est du bavardage inutile. Votre cible est « confortablement sous le plus court timeout d’inactivité que vous ne pouvez pas contrôler ».
Règle empirique qui tient dans la réalité : 30–60 secondes est généralement sûr pour des réseaux hostiles. 120 secondes est acceptable dans des datacenters bien gérés. Tout ce qui est en dessous de 15 secondes est typiquement du bricolage par superstition.
Blague #1 : Si votre intervalle keepalive SSH est de 1 seconde, vous ne gardez pas la session en vie — vous donnez juste du travail au pare‑feu.
Une citation pour rester honnête
Idée paraphrasée (Werner Vogels, contexte fiabilité/exploitation) : « Tout échoue ; concevez en supposant que l’échec est normal. »
C’est le bon état d’esprit. Les keepalives ne préviennent pas l’échec ; ils rendent l’échec prévisible et détectable.
Paramètres recommandés et opinionnés pour Ubuntu 24.04
Si vous voulez une réponse unique qui marche pour la plupart des humains sur la plupart des réseaux : activez les keepalives au niveau SSH côté client, et éventuellement côté serveur pour le nettoyage. Gardez TCPKeepAlive activé mais n’en faites pas la base de votre stratégie.
Côté client : définir le keepalive dans ~/.ssh/config
Utilisez une valeur par défaut globale et faites des overrides pour les réseaux fragiles ou les liens à haute latence.
cr0x@server:~$ cat > ~/.ssh/config <<'EOF'
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
Host *.corp
ServerAliveInterval 30
ServerAliveCountMax 3
EOF
Ce que cela signifie : Toutes les minutes, le client envoie un keepalive ; s’il manque 3 réponses (environ 3 minutes), il se déconnecte. Sur les hôtes corporatifs, c’est toutes les 30 secondes.
Décision : Si vous observez encore des drops autour d’un timeout connu (par ex. 5 minutes), réduisez l’intervalle à 20–30 secondes pour cet environnement. Si vous voyez des coupures en déplacement/roaming, arrêtez d’essayer de forcer le comportement avec des keepalives et utilisez une approche adaptée au roaming (FAQ).
Côté serveur : définir le keepalive dans /etc/ssh/sshd_config.d/
Sur Ubuntu 24.04, préférez les drop-ins plutôt que modifier le fichier monolithique. Gardez la configuration lisible et réversible.
cr0x@server:~$ sudo tee /etc/ssh/sshd_config.d/50-keepalive.conf > /dev/null <<'EOF'
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yes
EOF
cr0x@server:~$ sudo sshd -t
cr0x@server:~$ sudo systemctl restart ssh
cr0x@server:~$ sudo systemctl status ssh --no-pager | head -n 8
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-12-29 11:02:12 UTC; 4s ago
Docs: man:sshd(8)
man:sshd_config(5)
Ce que cela signifie : Le serveur sondes les clients inactifs. Si le client ne répond pas en ~3 minutes, sshd coupe la session. C’est généralement ce que vous voulez pour les connexions mortes.
Décision : Si vous avez des liens à haute latence ou intermittents (satellite, VPN surchargé), augmentez ClientAliveCountMax ou l’intervalle pour éviter des faux positifs.
Réglage des keepalives TCP du noyau (à utiliser avec parcimonie)
Si vous devez ajuster les valeurs par défaut du noyau, faites-le explicitement et de façon persistante :
cr0x@server:~$ sudo tee /etc/sysctl.d/60-tcp-keepalive.conf > /dev/null <<'EOF'
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
EOF
cr0x@server:~$ sudo sysctl --system | tail -n 6
* Applying /etc/sysctl.d/60-tcp-keepalive.conf ...
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
Ce que cela signifie : Les sondes commencent après 5 minutes d’inactivité, puis toutes les 30 secondes, jusqu’à 5 sondes. C’est ~7,5 minutes pour déclarer la socket morte au niveau TCP.
Décision : Ne faites cela que si vous comprenez l’impact sur toutes les connexions TCP de l’hôte. Sur des systèmes chargés, cela peut augmenter notablement le trafic de keepalive (toujours faible, mais « faible » à grande échelle devient visible).
Ce qu’il faut éviter
- Ne définissez pas des intervalles keepalive globaux à 5 secondes. Vous générerez un trafic de fond constant, maintiendrez l’état chaud dans chaque middlebox et échouerez toujours lors de vrais changements de chemin.
- Ne comptez pas uniquement sur
TCPKeepAlive yesavec les sysctls par défaut. Deux heures n’est pas une stratégie de keepalive ; c’est une histoire du soir. - Ne « réparez » pas les drops en désactivant le rekeying chiffré ou en affaiblissant la crypto. La stabilité de session n’est pas une excuse pour remonter au milieu des années 2000.
Trois mini-récits d’entreprise depuis le terrain
1) L’incident causé par une mauvaise hypothèse : « Le pare-feu est stateful, donc il nous gardera en mémoire »
Une équipe exploitait des jump hosts pour que les ingénieurs accèdent aux bases de production. Les sessions se « coupaient » au hasard — surtout pendant les réponses d’incident, bien sûr. Les gens ont blâmé les jump hosts. Quelqu’un a même patché le noyau « au cas où », ce qui est une façon impressionnante de perdre du temps.
L’hypothèse erronée était discrète et mortelle : « Pare-feu stateful signifie qu’il conserve l’état jusqu’à la fermeture de la connexion. » En pratique, le pare-feu avait un timeout d’inactivité pour les flux TCP établis. Si le canal SSH était silencieux — lecture de logs dans un autre onglet, réflexion, attente — les mappings s’épuisaient.
Ils ont essayé d’augmenter les limites côté serveur, d’élever les descripteurs de fichiers, de tweaker le logging sshd. Rien n’a changé. L’indice clé était le motif horloger : les déconnexions s’alignaient autour d’une fenêtre d’inactivité cohérente. Une fois qu’ils ont testé un sleep 3600 contrôlé avec et sans ServerAliveInterval, la solution est devenue embarrassante de simplicité.
La correction n’a pas été héroïque : définir ServerAliveInterval 30 côté client sur les jump hosts (et recommander la même chose aux laptops), plus ClientAliveInterval 60 côté serveur pour nettoyer les zombies. Le postmortem fut court et légèrement humiliant, ce qui est le meilleur genre.
2) L’optimisation qui s’est retournée contre eux : multiplexage partout
Une autre organisation a standardisé le multiplexage SSH parce que cela rend les connexions répétées rapides. ControlMaster + ControlPersist est génial quand on lance beaucoup de commandes courtes (automatisation, vérifications de fleet). Le gain de performance est réel.
Puis l’équipe VPN a déployé un nouveau client de tunnel « intelligent » qui renégociait périodiquement les routes. La connexion TCP sous-jacente se bloquait ou était orpheline. Avec le multiplexage, les ingénieurs ne voyaient pas immédiatement « la connexion est tombée ». Ils voyaient des échecs aléatoires : scp bloqué, nouvelles sessions ssh échouant instantanément, terminaux acceptant la frappe sans produire de sortie.
L’optimisation a transformé une socket défaillante en une dépendance partagée. Une connexion de contrôle morte pouvait casser dix onglets. Le mode de défaillance semblait chaotique parce que les gens ouvraient des « nouvelles sessions » qui n’étaient pas du tout nouvelles.
La correction finale fut nuancée : garder le multiplexage pour l’automatisation et les réseaux internes stables, mais le désactiver (ou garder ControlPersist court) pour les hôtes accédés via des VPN instables. Ils ont aussi resserré ServerAliveInterval pour que la socket de contrôle meure vite lors d’un hic VPN, permettant aux nouvelles connexions de créer une socket fraîche.
3) La pratique ennuyeuse mais correcte qui a sauvé la mise : standards explicites de keepalive
Un établissement financier avait une politique : tous les bastions et postes d’administration incluent une configuration SSH de base avec keepalives, et les serveurs appliquent une politique correspondante. Ce n’était pas glamour. C’était écrit. C’était déployé de façon cohérente.
Un jour, un changement réseau a introduit un timeout d’inactivité plus court sur une série de pare-feu de segmentation. Les conséquences furent presque nulles. Les ingénieurs ont remarqué quelques sessions qui sautaient plus vite qu’avant (parce que la détection via keepalive était plus rapide), mais la rage liée aux déconnexions « aléatoires » n’a jamais eu lieu.
Ce qui les a sauvés, c’était l’alignement ennuyeux : le client envoie des keepalives toutes les 60 secondes ; le serveur expire les clients morts en quelques minutes ; les sysctls TCP keepalive étaient réglés à des valeurs raisonnables sur les bastions. Quand le réseau est devenu plus strict, leurs sessions généraient encore assez de trafic périodique pour rester « non inactives ».
Blague #2 : Le système le plus fiable est celui que vous pouvez expliquer à un auditeur sans pleurer.
Erreurs courantes (symptôme → cause → correction)
1) Symptom: « Broken pipe » après ~10–30 minutes d’inactivité
Cause racine : Timeout d’inactivité dans un NAT/pare-feu/load balancer. Le mapping expire ; le paquet suivant est reset ou black‑holé.
Correction : Définir ServerAliveInterval à 30–60 et ServerAliveCountMax à 3. Si vous administrez aussi les serveurs, mettez ClientAliveInterval de la même manière.
2) Symptom: la session se fige (pas de sortie), puis finit par se déconnecter
Cause racine : Blackhole du chemin ou perte asymétrique ; TCP n’apprend pas immédiatement que le pair est injoignable. Aucun trafic applicatif n’apparaît pour révéler la panne.
Correction : Activez les keepalives SSH pour que SSH détecte l’absence de réponse et déchire la session. Si cela coïncide avec du mouvement VPN/Wi‑Fi, considérez cela comme une instabilité de chemin et envisagez un outil de roaming.
3) Symptom: plusieurs terminaux meurent ensemble ; nouvelles commandes SSH échouent instantanément
Cause racine : Multiplexage ControlMaster réutilisant une socket de contrôle morte, ou une connexion TCP sous-jacente partagée par de nombreuses sessions.
Correction : Réduire ControlPersist, désactiver le multiplexage pour cet hôte, et s’assurer que les keepalives sont activés pour que la socket de contrôle meure vite.
4) Symptom: les logs serveur indiquent « Timeout, client not responding »
Cause racine : ClientAliveInterval côté serveur est activé et se déclenche, ou le chemin réseau bloque les réponses assez longtemps pour atteindre le seuil.
Correction : Conserver ClientAliveInterval mais définir un ClientAliveCountMax réaliste (souvent 3). Si les clients sont sur des liens à haute latence, augmentez le compte ou l’intervalle.
5) Symptom: les déconnexions coïncident avec des redémarrages sshd ou des mises à jour automatiques
Cause racine : Les redémarrages du service tuent les sessions. Ce n’est pas un problème de keepalive.
Correction : Contrôler la cadence des redémarrages, utiliser des fenêtres de maintenance, ou stabiliser les bastions. Vérifier avec journalctl et systemctl status.
6) Symptom: seuls les gros outputs (ou scp) échouent ; la saisie interactive fonctionne
Cause racine : Problèmes PMTU/fragmentation ou MTU de tunnel cassé. Les petits paquets passent ; les plus grands sont black‑holés.
Correction : Valider avec des pings DF et corriger l’MTU. Les keepalives ne régleront pas les blackholes de paquets.
7) Symptom: les reconnexions échouent de manière intermittente ; SSH ne s’établit parfois pas
Cause racine : Épuisement de la table conntrack sur un pare-feu ou l’hôte ; ou règles de rate-limiting.
Correction : Vérifier nf_conntrack_count, examiner les politiques de pare‑feu, ajuster la taille de conntrack et réduire les modèles de traffic abusifs ailleurs.
8) Symptom: fonctionne depuis un réseau, échoue depuis un autre
Cause racine : Politiques de middlebox différentes : pare-feu d’entreprise, portail captif d’hôtel, CGNAT ISP.
Correction : Utiliser des blocs de configuration SSH par hôte ou par réseau. Si besoin, envisagez un transport alternatif (VPN/bastion) plutôt que de lutter contre chaque réseau captif.
Listes de contrôle / plan étape par étape
Étape par étape : stabiliser vos propres sessions SSH (client uniquement)
-
Mesurer la fenêtre d’échec. Lancez un test inactif sans keepalive et observez quand il tombe.
cr0x@server:~$ ssh -o ServerAliveInterval=0 ops@db-prod-1 'echo connected; sleep 1800' connected Write failed: Broken pipeDécision : Si cela tombe autour d’un temps cohérent, vous traitez un timeout d’inactivité.
-
Valider avec un keepalive temporaire.
cr0x@server:~$ ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=3 ops@db-prod-1 'echo connected; sleep 1800; echo survived' connected survivedDécision : Si cela tient, intégrez la configuration dans
~/.ssh/config. -
Appliquer la config globalement, et override si nécessaire.
cr0x@server:~$ ssh -G db-prod-1 | egrep -i 'serveraliveinterval|serveralivecountmax' serveraliveinterval 60 serveralivecountmax 3Décision : Confirmer que les valeurs effectives sont celles attendues (les blocs Match peuvent vous surprendre).
-
Si vous faites du roaming, cessez d’attendre que TCP survive aux changements d’IP. Utilisez un outil conçu pour le roaming ou acceptez les reconnexions et utilisez tmux/screen côté serveur (FAQ).
Étape par étape : standardiser le comportement sur les serveurs (approche flotte)
-
Vérifier la config effective de sshd.
cr0x@server:~$ sudo sshd -T | egrep -i 'clientalive|tcpkeepalive' clientaliveinterval 0 clientalivecountmax 3 tcpkeepalive yesDécision : Si
clientaliveintervalest 0, vous ne sondez pas les clients inactifs. -
Ajouter une politique de keepalive en drop-in.
cr0x@server:~$ sudo tee /etc/ssh/sshd_config.d/50-keepalive.conf > /dev/null <<'EOF' ClientAliveInterval 60 ClientAliveCountMax 3 TCPKeepAlive yes EOF -
Valider et redémarrer.
cr0x@server:~$ sudo sshd -t cr0x@server:~$ sudo systemctl restart sshDécision : Si
sshd -téchoue, ne redémarrez pas ; corrigez la syntaxe d’abord. -
Observer les changements de logs après déploiement.
cr0x@server:~$ sudo journalctl -u ssh -S "now-1h" --no-pager | egrep -i 'timeout|disconnect|closed' | tail -n 20 Dec 29 11:21:07 db-prod-1 sshd[24102]: Timeout, client not responding from 10.20.4.18 port 54119 Dec 29 11:21:07 db-prod-1 sshd[24102]: Disconnecting: Timeout, client not respondingDécision : Si les timeouts augmentent de façon inattendue, vos valeurs d’intervalle/compte sont peut‑être trop agressives pour votre environnement.
Étape par étape : décider si vous devez tuner les keepalives TCP du noyau
- Vérifier les sysctls actuels (
tcp_keepalive_timeen particulier). - Inventorier qui partage l’hôte. Baisser les keepalives impacte tous les sockets TCP (bases, agents, exporters).
- Modifier via /etc/sysctl.d/ et mesurer le trafic/comportement.
- Revenir rapidement en arrière si des effets secondaires inattendus apparaissent (certaines applications implémentent déjà leurs propres keepalives).
FAQ
1) Dois‑je utiliser ServerAliveInterval ou TCPKeepAlive ?
Commencez par ServerAliveInterval. C’est au niveau SSH, configurable par hôte, et tend à maintenir l’état NAT de façon fiable. Gardez TCPKeepAlive yes mais ne comptez pas sur les valeurs noyau par défaut.
2) Quelles valeurs devrais‑je définir pour les keepalives ?
Commencez par ServerAliveInterval 60 et ServerAliveCountMax 3 côté client. Pour des réseaux stricts, utilisez 30 secondes. Côté serveur, ClientAliveInterval 60 et ClientAliveCountMax 3 est une base sensée.
3) Pourquoi ma session meurt alors que je suis en train de taper ?
Ce n’est généralement pas un timeout d’inactivité. Recherchez la perte de paquets, la renégociation de routes VPN, le roaming Wi‑Fi ou des problèmes MTU. Vérifiez ss -tinp pour des retransmissions et lancez des pings DF pour tester l’MTU.
4) ClientAliveInterval déconnecte‑t‑il les utilisateurs ?
Ça peut arriver si votre réglage est trop agressif. Il est conçu pour expulser les sessions mortes, pas punir les liens lents. Si vous avez des déconnexions erronées, augmentez ClientAliveCountMax ou l’intervalle.
5) Les keepalives posent‑ils un risque pour la sécurité ?
Pas intrinsèquement. Ils envoient un trafic authentifié minimal. La vraie question de sécurité est de savoir si vous maintenez des sessions en vie qui devraient être fermées pour des raisons de politique. Si votre organisation exige une déconnexion d’inactivité, les keepalives peuvent entrer en conflit avec cette politique.
6) Je suis derrière un proxy/pare-feu d’entreprise qui tue SSH. Les keepalives aideront‑ils ?
Seulement si les connexions SSH sont autorisées mais que les flux inactifs sont supprimés. Si le réseau bloque activement SSH ou effectue une inspection profonde qui détruit les sessions longue durée, vous aurez besoin d’un bastion/VPN sanctionné.
7) Dois‑je tuner les net.ipv4.tcp_keepalive_* sur Ubuntu 24.04 ?
Seulement si vous avez un besoin système global. Pour SSH seul, préférez les keepalives OpenSSH. Si vous touchez aux keepalives noyau, documentez‑le et comprenez que cela affecte toutes les connexions TCP.
8) Ma session SSH se coupe quand je ferme le capot du laptop. Est‑ce lié aux keepalives ?
Pas vraiment. Votre laptop peut suspendre le réseau ; la connexion TCP devient obsolète. Les keepalives peuvent détecter la session obsolète plus vite, mais ils ne peuvent pas empêcher une NIC suspendue de couper la communication.
9) Et tmux ou screen ?
Faites‑le. Les multiplexeurs de terminal côté serveur ne préviennent pas les déconnexions, mais ils évitent la perte de travail. Associez tmux à des keepalives et vous obtenez à la fois moins de coupures et moins de douleur quand elles surviennent.
10) Existe‑t‑il un meilleur outil que SSH pour les réseaux en roaming ?
Oui : des outils de session conçus pour le roaming existent spécifiquement pour survivre aux changements d’IP et à la connectivité intermittente. Les keepalives aident SSH, mais ils ne changent pas les fondamentaux de TCP.
Prochaines étapes à faire aujourd’hui
Voici le chemin industriel hors du « SSH se coupe au hasard » :
- Prouvez le mode de défaillance. Mesurez s’il s’agit d’un timeout d’inactivité ou d’une instabilité de chemin en lançant un test
sleepet en activant des logs verbeux. - Activez les keepalives SSH côté client. Mettez
ServerAliveInterval 60(30sur réseaux hostiles) etServerAliveCountMax 3. - Si vous administrez des serveurs, ajoutez des keepalives côté serveur. Utilisez un fichier drop‑in avec
ClientAliveInterval 60etClientAliveCountMax 3pour nettoyer les sessions mortes. - Ce n’est qu’ensuite que vous pouvez envisager de tuner les keepalives noyau. C’est plus vaste, parfois nécessaire, souvent surutilisé.
- Réduisez les surprises. Si le multiplexage transforme une socket morte en de nombreuses sessions cassées, ciblez-le finement.
Si vous suivez ces étapes dans l’ordre, vous cesserez de traiter les déconnexions SSH comme la météo et commencerez à les voir pour ce qu’elles sont : une politique de timeout rencontrant un flux TCP inactif. Réparable. Prévisible. Parfois toujours pénible — à cause des réseaux — mais au moins plus rationnel et moins mystique.