Tuer un tunnel ne suffit pas : il revient en quinze secondes
En listant ce qui écoutait sur le serveur, on est tombé sur deux ports qu'on n'avait pas ouverts. Ils appartenaient à une session ouverte depuis deux jours et dix heures, depuis une machine du domicile. On l'a fermée. Quinze secondes plus tard, elle était revenue.
1. Ce qu'on a trouvé
La commande qui liste les ports en écoute donnait deux lignes inattendues. L'une exposait un service sur toutes les interfaces de la machine ; l'autre rendait accessible, en local, le modèle de langage qui tourne à la maison. Les deux appartenaient au même processus : une session distante restée ouverte deux jours et dix heures.
C'est un tunnel inverse. Le principe est simple et souvent utile : une machine se connecte à un serveur, et demande au passage « ouvre-moi un port chez toi, et renvoie-moi ce qui y arrive ». On s'en sert pour joindre un ordinateur derrière une box, sans configurer quoi que ce soit.
La question n'est pas de savoir si c'est utile. C'est de savoir qui décide de ce qui écoute sur un serveur. Ici, la décision avait été prise par une machine cliente, un jour, pour une raison qu'on avait oubliée depuis.
2. La fermeture qui n'a rien fermé
On a tué la session. Les deux ports ont disparu, proprement. Par acquit de conscience, on a regardé de nouveau quinze secondes plus tard : les deux ports étaient revenus.
Côté machine cliente, un utilitaire de reconnexion automatique surveillait le tunnel et le relançait dès qu'il tombait. C'est exactement son travail, et il le faisait bien. Le résultat, pour nous, est qu'il n'existait aucune commande côté serveur permettant de refermer durablement cette porte — puisque la porte était rouverte de l'extérieur, en boucle, indéfiniment.
C'est un cas d'école. Tuer un processus soulage l'observateur : la ligne disparaît de l'écran. Mais un processus qu'on tue et qui revient n'a pas été traité ; il a été dérangé. La seule surveillance qui aurait détecté la supercherie est celle qu'on a faite par réflexe — regarder une deuxième fois, un peu plus tard.
3. Le vrai correctif tient en deux lignes
La bonne réponse n'était pas de tuer plus fort, ni de bloquer la machine cliente. C'était de retirer au serveur la capacité d'accorder ce genre d'ouverture.
PermitListen none
GatewayPorts no
La première ligne refuse les demandes d'écoute émises par un client. La seconde garantit que, même si une écoute passait, elle resterait confinée à la machine et ne serait pas exposée au réseau. Après rechargement, l'utilitaire de reconnexion a continué de se connecter — et n'a plus jamais obtenu de port.
Ce qui n'est volontairement pas touché : la redirection locale, celle qu'utilisent les outils de développement pour atteindre un service distant depuis un poste. Elle ne fait écouter personne sur le serveur. Un garde-fou qui interdirait les deux serait plus simple à écrire, et se ferait désactiver le jour où il gêne.
4. La règle qu'on en tire
Elle rejoint une règle qu'on s'était déjà donnée pour les applications : rien ne tourne à découvert sur la machine, tout vit dans un conteneur. La version réseau de la même idée est celle-ci : ce qui écoute sur un serveur doit avoir été décidé sur ce serveur.
Pas parce qu'un tunnel serait dangereux en soi. Parce qu'une ouverture décidée ailleurs n'apparaît dans aucune configuration, ne se retrouve dans aucun fichier qu'on relit, et survit à la mémoire de celui qui l'a créée. Deux jours et dix heures, c'est le temps qu'il aura fallu pour que celle-ci nous surprenne. Elle aurait aussi bien pu tenir six mois.
- Un processus tué qui revient n'a pas été traité. Regarder une seconde fois, un peu plus tard, coûte quinze secondes et change la conclusion.
- Fermer un symptôme et fermer une porte sont deux gestes différents. Le premier se voit tout de suite ; seul le second tient.
- Un garde-fou qui interdit tout est plus simple à écrire — et c'est celui qu'on désactivera. Refuser précisément coûte plus cher et survit.