Quand l’agent ne peut pas cliquer : Tailscale SSH

  • agents
  • tailscale
  • ssh
  • exploitation

Un agent qui agit sur un serveur passe par les mêmes portes qu’un administrateur, mais il n’a ni navigateur, ni téléphone pour valider une authentification forte. Quand une de ces portes demande une preuve d’identité à un humain, l’agent ne voit qu’une connexion qui n’aboutit pas. Mon agent d’infrastructure en a fait l’expérience avec Tailscale SSH, et la correction tient en sept lignes.

Le contexte

Mon assistant Jarvis Local, dont le code est public, a un agent d’infrastructure qui surveille un serveur de mon parc. Il ouvre une connexion SSH, lance une série de commandes de lecture en un seul passage (charge, mémoire, disques, conteneurs, services en échec) et donne le résultat à un modèle local pour le diagnostic. Les commandes qui modifient quelque chose passent par une validation humaine ; c’est le sujet d’un article précédent.

La connexion passe par Tailscale SSH, et elle est lancée sans terminal : ssh -o BatchMode=yes, avec un délai de connexion de quinze secondes. C’est le réglage normal pour un programme, qui ne doit jamais rester bloqué sur une question qu’il ne sait pas lire.

Pourquoi l’agent ne voyait qu’un délai dépassé

Tailscale SSH propose un mode de règle appelé « check ». La documentation de Tailscale le décrit ainsi : il exige que l’utilisateur se ré-authentifie périodiquement, toutes les douze heures par défaut, et l’initialisation de la session SSH fournit une adresse de connexion à ouvrir dans un navigateur, éventuellement avec une authentification multifacteur.

Pour un humain, c’est un clic. Pour un programme lancé sans terminal, personne n’ouvre l’adresse. Avant la correction, l’agent rendait un délai dépassé, sans dire pourquoi : rien dans son message n’indiquait qu’il suffisait de se reconnecter.

Ce que j’ai changé

Le commit du 18 juin 2026 ajoute une lecture de la réponse de SSH dans ssh_run, la fonction qui lance toutes les connexions de l’agent :

blob = (r.stderr or "") + (r.stdout or "")
if "login.tailscale.com" in blob or "Tailscale SSH requires" in blob:
    m = re.search(r"https://login\.tailscale\.com/\S+", blob)
    url = m.group(0) if m else f"(lance `ssh {host}` dans un terminal pour obtenir le lien)"
    return False, (f"Tailscale SSH demande une ré-authentification → visite {url} "
                   f"puis réessaie (ou lance `ssh {host}` en interactif).")

Quand la réponse contient l’adresse de connexion de Tailscale, l’agent l’extrait et la renvoie avec une consigne : ouvrir le lien, puis réessayer. S’il ne la trouve pas, il dit comment l’obtenir à la main.

Deux détails qui comptent autant que le message

L’agent garde en cache, trente secondes, le relevé du serveur, pour ne pas rouvrir une connexion à chaque question. Seuls les relevés réussis y entrent : une erreur n’est jamais mise en cache. Une fois la ré-authentification faite, la question suivante repart vers le serveur, sans attendre l’expiration d’un cache qui répéterait l’échec.

Quand le relevé échoue, le modèle n’est pas appelé. Le message d’erreur est rendu tel quel, et la demande de correction répond qu’il n’y a rien de sûr à proposer. Un modèle à qui l’on donnerait un relevé vide écrirait quand même un diagnostic ; ici, il ne parle que de ce qu’il a lu.

Ce que ça dit d’un agent en entreprise

La cause de fond n’est pas Tailscale : l’agent utilise mon identité. Une règle conçue pour un humain, une ré-authentification périodique, s’applique donc à un programme qui ne peut pas la satisfaire. En entreprise, le même cas se présente avec une session SSO, une authentification multifacteur ou un accès à privilèges qui expire : l’agent tombe en panne au moment où la session humaine qu’il emprunte arrive à échéance.

Le message actionnable est une bonne parade pour un assistant personnel, où l’humain est à côté. Pour un agent d’exploitation, la règle reste celle d’un compte dédié, avec ses propres droits, réduits à ce qu’il doit lire, et une échéance que l’on surveille comme celle d’un certificat. Dans les deux cas, une erreur d’accès doit dire à un humain quoi faire, pas seulement qu’un délai a expiré.

Une question sur ce sujet ?

Si votre cas ressemble à celui décrit ici et que vous voulez un avis avant de vous lancer, écrivez-moi. Une réponse courte et honnête ne coûte rien.