1. Accueil
  2. Expertise
  3. SSH indisponible après un reboot

SSH indisponible après un reboot

La panne qui dormait depuis des mois : un durcissement volontaire, couplé sans le savoir au cycle de vie du VPN, révélé par le premier redémarrage.

PrivéFiableSécurisé3 min de lecture
En 10 secondes

Après un reboot de maintenance, SSH ne répondait plus : sshd, volontairement restreint à l'adresse du VPN maillé, démarrait avant que cette interface n'existe. Diagnostic par console out-of-band, correctif noyau d'une ligne (ip_nonlocal_bind), et un test de non-régression qui reproduit les conditions du boot. La restriction de SSH au réseau privé reste inchangée.

Contexte / besoin

Le serveur de production auto-hébergé (sites clients, reverse proxy, services internes) devait passer une maintenance planifiée : mises à jour de sécurité et redémarrage, le premier depuis des mois, avec un noyau en attente et une mémoire saturée par le long uptime. Au reboot, tous les services conteneurisés repartent seuls (politiques de redémarrage Docker). SSH, lui, refuse toute connexion.

Diagnostic

Trois observations circonscrivent le problème avant même d’ouvrir une console :

  • le refus est immédiat (« Connection refused », sans timeout) : la machine est jointe, mais rien n’écoute sur le port SSH ;
  • les sites sont en ligne : Docker et le réseau vont bien ;
  • le pair répond sur le maillage Tailscale : le démon VPN tourne. Le mort, c’est sshd seul.

Restait la console KVM out-of-band du serveur (et son clavier interprété en QWERTY, exercice d’humilité en soi). Verdict de systemctl status ssh, adresse expurgée :

sshd: error: Bind to <ip-vpn> failed: Cannot assign requested address.
sshd: fatal: Cannot bind any address.

Cause racine : sshd_config contient un ListenAddress restreint à l’adresse du VPN maillé. C’est un choix de durcissement délibéré (réseau zero-trust) : SSH n’est joignable que depuis le réseau privé. Or au boot, sshd démarre avant que Tailscale n’ait monté son interface : l’adresse n’existe pas encore, le bind échoue, sshd épuise ses tentatives et reste couché. Le serveur n’avait jamais redémarré depuis ce durcissement : la panne était en place depuis des mois, invisible.

Contraintes

  • Machine de production avec des clients dessus : ni réinstallation, ni improvisation.
  • Le correctif ne doit rien céder sur la posture : SSH doit rester joignable uniquement via le VPN.
  • Le chemin de secours doit rester simple : c’est lui qu’on emprunte quand tout le reste est cassé.

Options envisagées

  1. Écouter sur toutes les interfaces, filtrer au pare-feu sur l’interface VPN : robuste face au problème d’ordre de démarrage, mais la restriction quitte le bind pour le pare-feu, et une erreur de règle expose le port.
  2. Ordonnancement systemd (After= le démon VPN, plus une boucle d’attente de l’adresse) : fragile, un service marqué démarré n’a pas forcément son adresse déjà assignée ; il faut scripter l’attente et gérer le timeout.
  3. Faire transiter SSH par le reverse proxy (passthrough TCP) : rejeté d’emblée. Le chemin de secours dépendrait alors de Docker et du reverse proxy ; on ne répond pas à une panne de dépendance de démarrage en ajoutant deux dépendances.
  4. net.ipv4.ip_nonlocal_bind=1 (sysctl) : autorise un processus à se binder sur une adresse qui n’existe pas encore. sshd démarre toujours, et n’écoute toujours que sur l’adresse du VPN.

Décision & pourquoi

L’option 4 : le changement minimal qui corrige l’ordre de démarrage sans toucher à la posture de sécurité, une ligne dans /etc/sysctl.d/. Complétée par un test de non-régression qui reproduit les conditions du boot :

systemctl stop <vpn> && systemctl restart ssh && systemctl is-active ssh

Si sshd est active alors que le VPN est coupé, la course au démarrage ne peut plus se reproduire. En parallèle, une seconde porte de secours indépendante est conservée (la console out-of-band) : les deux mécanismes ne partagent aucune dépendance.

Tradeoff assumé

ip_nonlocal_bind est un réglage global : n’importe quel processus peut désormais se binder sur une adresse non locale. Acceptable sur un serveur mono-administrateur ; l’alternative pare-feu (option 1) reste documentée si le contexte change.

Résultat

SSH survit au redémarrage quel que soit l’ordre de démarrage des services. Et trois leçons durables :

  • Un service qui tourne depuis des mois n’a jamais prouvé qu’il savait démarrer. Seul un reboot le prouve ; d’où l’intérêt d’en planifier régulièrement, sans attendre que le noyau l’exige.
  • Le chemin de secours doit dépendre du minimum de choses. Chaque dépendance ajoutée est une raison de plus de se retrouver devant une console KVM à minuit.
  • Le durcissement crée des couplages. Restreindre un service à une interface, c’est le coupler au cycle de vie de cette interface : un couplage qui se teste.

L’incident est résumé au journal d’exploitation.

← Toute l'expertise