<?xml version="1.0" encoding="utf-8"?>
<rfcdesc title="SSH Agent Protocol" num="9987" status="standards" wg="sshm">
  <authors><author>D. Miller (OpenSSH)</author></authors>
  <rfcdate><month>May</month><year>2026</year></rfcdate>
  <date>2026-05-28</date>
<content>
  <p>Voici encore un <wikipedia name="Request for
  comments">RFC</wikipedia> qui normalise quelque chose qui existait
  depuis longtemps : le protocole Agent de <wikipedia name="Secure
  Shell">SSH</wikipedia>.</p>
  <p><wikipedia name="Secure Shell">SSH</wikipedia> est normalisé dans
  le <rfc num="4251" local="true"/> et il permet la connexion à
  distance (<rfc num="4253" local="true"/>) avec
  <wikipedia>authentification</wikipedia> (<rfc num="4252"
  local="false"/> et <rfc num="4254" local="false"/>) par exemple avec
  une <wikipedia name="Cryptographie asymétrique">clé
  publique</wikipedia>. Il est probablement inutile de le présenter
  davantage aux lecteurices de ce blog. Une des fonctions géniales de
  SSH est la possibilité d'avoir un agent (rien à voir avec
  l'<wikipedia name="Intelligence artificielle agentique">IA agentique</wikipedia> qui est à la mode en ce
  moment) qui mémorise les clés privées et peut effectuer les
  opérations demandées. Ainsi mémorisées, les clés seront utilisables
  sans nouvelle intervention de l'utilisateur (taper une
  <wikipedia name="Phrase de passe">phrase de passe</wikipedia>, etc) tout en restant bien
  sécurisées. Et l'agent peut intervenir sur des connexions distantes,
  ce qui évite de copier sa clé privée sur des serveurs à qui on ne
  fait pas forcément totalement confiance. L'agent ne tourne pas dans
  le client SSH mais dans un processus dédié, ce qui améliore sa
  sécurité. Si vous êtes connecté en ce moment, vous avez sans doute
  un agent SSH qui tourne (ici sur une
  <wikipedia>Debian</wikipedia>) :
  <code>
% ps uxwww | grep ssh
bortzme+  459934  0.0  0.0  10700  4964 ?        Ss   09:44   0:00 /usr/bin/ssh-agent /home/bortzmeyer/.xsession
  </code>
  Comme indiqué au début, ce mécanisme d'agent est connu, mis en œuvre
  et utilisé depuis très longtemps, ce <wikipedia name="Request for
  comments">RFC</wikipedia> est une documentation <foreign
  xml:lang="la">a posteriori</foreign> (le très ancien document
  <computer><link
  url="https://datatracker.ietf.org/doc/draft-ietf-secsh-agent/">draft-ietf-secsh-agent</link></computer>
  décrivait un protocole différent).</p>
  <p>Donc, comment fonctionne ce protocole Agent (section 2 du RFC) ?
  Il est <wikipedia>client-serveur</wikipedia>, le serveur étant
  l'agent et le client de l'agent n'étant pas forcément un client SSH
  (mais, bon, c'est le cas le plus courant).  Le client envoie des
  requêtes à l'agent et reçoit des réponses (comme dans beaucoup de
  <wikipedia name="Protocole de communication">protocoles</wikipedia> réseau…). L'agent est un serveur
  pur, il ne fait que répondre au client, sans prendre
  d'initiatives. Les requêtes typiques sont le chargement d'une clé,
  la suppression d'une clé, la <wikipedia name="Signature numérique">signature</wikipedia> en
  utilisant une des clés. Le serveur reste maitre d'accepter ou pas
  les requêtes et le client doit donc être prêt à voir une requête
  refusée, par exemple parce que l'agent n'accepte que les clés d'un
  certain type.</p>
  <p>La section 3 du RFC détaille les messages échangés entre le
  client et l'agent (le serveur). Ils sont de type <wikipedia
  name="Type-length-value">TLV</wikipedia> et les types figurent dans
  <link
  url="https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-agent-protocol-message-type-numbers">un
  registre IANA</link>. La longueur peut être nulle, par exemple il
  existe des messages de type <computer>SSH_AGENT_FAILURE</computer>
  (type numérique 5) qui n'ont pas de valeur. Le client demande
  l'ajout d'une clé avec des messages de type
  <computer>SSH_AGENTC_ADD_IDENTITY</computer> (type numérique 17). La
  valeur est composée du type de la clé, de la clé elle-même et du
  commentaire que vous avez indiqué lors de la création de la clé ; si
  vous utilisez, par exemple, une clé <wikipedia>Ed25519</wikipedia>,
  cf. <rfc num="8709" local="true"/>, le type est
  <computer>ssh-ed25519</computer> (la liste est dans <link
  url="https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-19">un
  registre IANA</link>). Avec <wikipedia>OpenSSH</wikipedia>, vous
  trouverez ce commentaire dans
  <computer>~/.ssh/id_ed25519.pub</computer>.</p> <p>De la même façon,
  on peut retirer une clé avec les messages de type
  <computer>SSH_AGENTC_REMOVE_IDENTITY</computer> (type 18) et
  <computer>SSH_AGENTC_REMOVE_ALL_IDENTITIES</computer> (type 19).</p>
  <p>Une fois les clés dans l'agent, le client peut lui demander de
  <wikipedia name="Signature numérique">signer</wikipedia> avec le type de
  message <computer>SSH_AGENTC_SIGN_REQUEST</computer> (type 13),
  message qui comprendra les données à signer.</p> <p>Pour se
  connecter à l'agent (section 4 du RFC), le client doit utiliser une
  méthode sûre. Èvidemment pas question d'ouvrir l'agent à tout
  l'Internet. Sur <wikipedia>Unix</wikipedia>, la méthode la plus
  courante est d'utiliser une <wikipedia xml:lang="en" name="Unix
  domain socket">prise locale</wikipedia>. Souvent, elle est trouvée
  par une <wikipedia name="Variable d'environnement">variable d'environnement</wikipedia> définie lors
  de la connexion, en général
  <computer>SSH_AUTH_SOCK</computer>. C'est ce que fait
  <wikipedia>OpenSSH</wikipedia> mais ce n'est pas imposé par la
  norme, qui laisse le choix aux programmes.</p> <p>Voici un exemple
  avec OpenSSH :
<code>
# On lance l'agent (on aurait normalement utilisé eval ou un
# équivalent, pour définir la variable d'environnement) :
% ssh-agent 
SSH_AUTH_SOCK=/tmp/ssh-xrLh0tnpVwLF/agent.23934; export SSH_AUTH_SOCK;
SSH_AGENT_PID=23935; export SSH_AGENT_PID;
echo Agent pid 23935;

% ls -l /tmp/ssh-ZzouiZGBumyI/agent.23934
srw------- 1 stephane stephane 0 May  4 18:38 /tmp/ssh-ZzouiZGBumyI/agent.23934

% SSH_AUTH_SOCK=/tmp/ssh-xrLh0tnpVwLF/agent.23934; export SSH_AUTH_SOCK

% ssh -vvv SERVEUR-DISTANT
…
debug1: Next authentication method: publickey
debug3: ssh_get_authentication_socket_path: path '/tmp/ssh-xrLh0tnpVwLF/agent.23934'
debug1: get_agent_identities: bound agent to hostkey
debug1: get_agent_identities: ssh_fetch_identitylist: agent contains no identities

[Et si on ajoute une clé dans l'agent ?]
% ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/stephane/.ssh/id_ed25519: 
Identity added: /home/stephane/.ssh/id_ed25519 (stephane@foobar)

% ssh -vvv SERVEUR-DISTANT
…
debug1: Next authentication method: publickey
debug3: ssh_get_authentication_socket_path: path '/tmp/ssh-xrLh0tnpVwLF/agent.23934'
debug1: get_agent_identities: bound agent to hostkey
debug1: get_agent_identities: agent returned 1 keys
</code>
  </p>
  <p>Autre possibilité très intéressante de l'agent (section 5), on
  peut faire suivre les communications sur un canal SSH (un peu comme
  avec <wikipedia name="X Window System">X11</wikipedia>). Cela permet, lorsque la machine A
  se connecte à la machine B puis à la C, d'utiliser les clés de la
  machine A pour s'authentifier sur la machine C. Cela utilise le
  mécanisme d'extension à SSH qui avait été normalisé dans le <rfc
  num="8308" local="true"/> pour signaler qu'on gère cette possibilité
  (mais comme le protocole Agent existait avant ce RFC, certains
  programmes n'annoncent pas cette gestion). La section 9 du RFC
  rappelle toutefois que cette fonction, si pratique, crée de nouveaux
  risques puisque elle introduit une relation de confiance
  transitive. Le RFC exige donc qu'elle ne soit pas activée par
  défaut.</p>
  <p>Le protocole a entrainé la création de cinq nouveaux registres
  <wikipedia name="Internet Assigned Numbers
  Authority">IANA</wikipedia> (section 7), dont celui <link
  url="https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-agent-protocol-message-type-numbers">des
  types de messages</link> (pour en ajouter un, politique « Examen par
  un expert », cf. <rfc num="8126" local="true"/>.)</p>
  <p>Un petit mot sur la sécurité (section 8 du RFC) puisqu'après
  tout, SSH est là pour améliorer notre sécurité. L'agent est chargé
  de garder des clés privées, il est donc très sensible et doit être
  de confiance. Mais le RFC rappelle aussi que l'accès à l'agent est
  évidemment très critique et doit être sécurisé (regardez les
  permissions de la prise dans l'exemple Unix plus haut), le protocole
  ne prévoyant aucune authentification.</p>
  <p>Si on a accès à l'agent, et qu'il a chargé des clés, on peut
  signer ce qu'on veut et donc s'authentifier auprès de serveurs
  distants. Par contre, on ne peut pas récupérer de clés privées via
  le protocole, qui n'a pas d'opération pour cela. Mais comme l'agent
  garde les clés privées en mémoire, il faut faire attention à ce que
  personne ne puisse lire cette mémoire. (La page de manuel de OpenSSH
  est très nette à ce sujet et conseille d'utiliser plutôt la fonction
  <foreign>ProxyJump</foreign>, via le <computer>-J</computer>.)</p>
  <p>Ah, et puisque l'agent, lorsqu'il charge une clé, demande la
  <wikipedia>phrase de passe</wikipedia> de la clé, il faut aussi
  qu'il prenne des précautions pour limiter le risque d'une
  <wikipedia name="Attaque par force brute">attaque par force brute</wikipedia> (quand un attaquant
  essaie plein de phrases possibles). Par exemple, il peut introduire
  un délai après une phrase incorrecte.</p>
  <p>Le protocole Agent est très ancien et est donc déjà mis en œuvre
  dans de nombreux programmes, par exemple
  <wikipedia>OpenSSH</wikipedia> (depuis
  <wikipedia>2000</wikipedia> !), <wikipedia>PuTTY</wikipedia>,
  <wikipedia>Dropbear</wikipedia>, <link url="https://www.paramiko.org/">Paramiko</link>, la
  bibliothèque standard de <wikipedia name="Go
  (langage)">Go</wikipedia>, etc.</p>
  <p>Si vous voulez afficher les messages échangés entre le client SSH
  et l'agent, je ne connais pas
  l'équivalent de <wikipedia>tcpdump</wikipedia> ou
  <wikipedia>Wireshark</wikipedia> pour cela. Avec
  <wikipedia>OpenSSH</wikipedia>, <computer>ssh-agent -d</computer>
  affiche les connexions mais pas les messages. Sinon, on peut lire les
  messages échangés avec <wikipedia xml:lang="en" name="netcat" anchor="Ports_and_reimplementations">socat</wikipedia> (ici, un exemple
  pour <wikipedia>OpenSSH</wikipedia> sur
  <wikipedia>Debian</wikipedia>) :
  <code>
[Dans une fenêtre]
% ssh-agent -D

[Dans une autre]
[Copier-coller la première ligne, celle qui définit SSH_AUTH_SOCK]
% mv $SSH_AUTH_SOCK /tmp/real-agent.sock
% socat -x UNIX-LISTEN:$SSH_AUTH_SOCK,fork UNIX-CONNECT:/tmp/real-agent.sock    

[Dans une troisième]
[Copier-coller la première ligne, celle qui définit SSH_AUTH_SOCK]
% ssh un-serveur
  </code>
  Mais les messages seront bruts, sans formatage. À vous de les décoder. <!-- idées ChatGPT
  https://chatgpt.com/share/69f0a2ae-cf20-8390-a370-7c988f700646 -->
  Par exemple, ici,suite à un <computer>ssh-add</computer>, on voit :
  <code>
<![CDATA[
> 2026/05/11 17:47:13.000145101  length=142 from=1152 to=1293
 00 00 00 8a 11 00 00 00 …
< 2026/05/11 17:47:13.000146117  length=5 from=10 to=14
 00 00 00 01 06
]]>
  </code>
  (Pour décoder, référez-vous au RFC, section 3, et au <link
  url="https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-agent-protocol-message-type-numbers">registre
  IANA</link>.) Le premier message (après le &gt;) a une longueur de 138 octets (les
  quatre premiers octets, 0000008A, nous le disent, socat l'affiche
  mais lui ajoute les quatre octets de la longueur). Le type du
  message (indiqué par l'octet suivant) est 17,
  <computer>SSH_AGENTC_ADD_IDENTITY</computer>. L'agent répond (après
  la &lt;) par un message d'un seul octet, de type 6
  (<computer>SSH_AGENT_SUCCESS</computer>) et de contenu nul. Si je me
  connecte en SSH à un serveur, en utilisant la clé qui vient d'être
  chargée, j'ai :
  <code>
<![CDATA[    
> 2026/05/11 17:59:54.000526321  length=5 from=665 to=669
 00 00 00 01 0b
< 2026/05/11 17:59:54.000526468  length=535 from=5 to=539
 00 00 02 13 0c 00 00 00 …
> 2026/05/11 17:59:54.000609874  length=1017 from=670 to=1686
 00 00 03 f5 0d 00 00 01 …
< 2026/05/11 17:59:54.000615719  length=285 from=540 to=824
 00 00 01 19 0e 00 00 01 14 …
]]>
  </code>
  Le premier message, très court, est de type 11,
  <computer>SSH_AGENTC_REQUEST_IDENTITIES</computer>, il obtient une
  réponse 12 (<computer>SSH_AGENT_IDENTITIES_ANSWER</computer>), puis
  le client SSH demande une signature avec la clé privée que stocke
  l'agent (type 13,
  <computer>SSH_AGENTC_SIGN_REQUEST</computer>) et a une réponse (type
  14, <computer>SSH_AGENT_SIGN_RESPONSE</computer>).
 </p>
  <p>Enfin, le fichier <computer>./PROTOCOL.agent</computer> dans le source
  de OpenSSH documente les <emphasis>extensions</emphasis> d'OpenSSH
  pour ce protocole agent-client.</p>
</content>
</rfcdesc>

