Lanceur automatique d'agent ssh éphémère

Palaiseau, le 8 janvier 2026

Cher Journal,

J'ai régulièrement besoin de travailler avec un agent ssh par machine, mais en ayant simultanément de multiples terminaux sur ladite machine, histoire de mieux profiter de l'agent sur toutes mes connexions. Au fur et à mesure du temps qui est passé, j'ai raffiné mon approche afin de m'assurer que je n'accumule pas d'agents oubliés jusqu'au prochain redémarrage de la machine, que j'ai bien accès au même agent depuis de multiples connexions simultanées, ou que je sois en mesure d'arrêter un agent immédiatement après m'être déconnecté d'un système, sans attendre une expiration de la clé privée en mémoire. Le résultat est deux morceaux de code répartis dans les scripts de login et de logout de mon shell, en l'occurrence bash.

Mon fichier ~/.profile contient le segment de code suivant, qui consiste essentiellement à essayer de retrouver dans la table des processus, un ssh-agent que je puisse utiliser pour stocker et récupérer mes clés SSH privées après les y avoir stockées avec ssh-add, la dernière étape du script consistant à vérifier que le ssh-agent est bien installé et à le démarrer s'il n'y en avait aucun en route :

# start up an SSH agent, or try to hook to an existing one if any
SSH_AGENT_PATTERN='(([/]usr)?[/]bin[/])?ssh-agent'
SSH_AGENT_CMDLINE="$( \
	grep -El "$SSH_AGENT_PATTERN" /proc/*/cmdline 2> /dev/null || true
)"
if [ -n "$SSH_AGENT_CMDLINE" ]
then
	SSH_AGENT_PID="$( \
		echo "$SSH_AGENT_CMDLINE" \
		| sed -n 's@/proc/\(.*\)/cmdline@\1@p' \
	)"
	SSH_AUTH_SOCK="$( echo $HOME/.ssh/agent/s.*.agent.* )"
	# Check for old style ssh-agent socket if new style is not found.
	if [ "$SSH_AUTH_SOCK" = "$HOME/.ssh/agent/s.*.agent.*" ]
	then SSH_AUTH_SOCK="$( echo /tmp/ssh-*/agent.* )"
	fi
	if [ -r "$SSH_AUTH_SOCK" ]
	then
		export SSH_AGENT_PID SSH_AUTH_SOCK
		printf 'Existing agent running with PID %s at %s\n' \
			"$SSH_AGENT_PID" \
			"$SSH_AUTH_SOCK"
	else
		printf 'WARNING: ssh-agent running with PID %s, but without\n' \
			"$SSH_AGENT_PID"
		printf '         a usable socket.  Please investigate!\n'
			
	fi
elif command -v ssh-agent > /dev/null
then
	printf 'ssh-agent: '
	eval $(ssh-agent)
fi
unset SSH_AGENT_PATTERN
unset SSH_AGENT_CMDLINE

L'intérêt de mettre ce segment de code dans le fichier ~/.profile est que je peux également m'en servir tel quel pour les situations où j'utilise un Bourne Shell classique, comme le Debian Alquimst Shell « dash ». J'ai par ailleurs un script de nettoyage dans mon fichier ~/.bash_logout qui va compter le nombre de sessions qui me restent sur la machine. S'il ne reste plus de sessions, alors l'agent est arrêté ; il faut noter que le comptage a lieu dans un sous-shell comptant comme une session à part :

# Don't run when ps is not available, this mostly means we are just leaving
# a minimal schroot at the moment.
if ! command -v ps > /dev/null 2>&1
then return
fi

# Check whether there are still some login shells running, and if not, then
# shutdown all agents (and not just the one recorded in the environment).
NRSHELLS="$(
	ps aux \
	| grep '^'"$USER"' .* \([-]bash\|bash  *[-]l\|bash  *[-]-login\)$' \
	| wc -l
)"

printf 'login shells left: %s\n' "$NRSHELLS"

# Remark one needs to accout for the running subshell.  Note also that
# some operating systems may dupplicate the number of occurrences of
# bash, due to leftover `sh -c bash` processes, so the figure needs to
# be set to three under such circumstances.
if [ "$NRSHELLS" -le 2 ]
then
	SSH_AGENT_PATTERN="^$USER"' .* \(\(\/usr\)\?\/bin\/\)\?ssh-agent'
	SSH_AGENT_PIDS="$(
		ps aux \
		| sed -n "/$SSH_AGENT_PATTERN/ s/^$USER"' \+\([^ ]\+\) .*/\1/ p'
	)"
	for PID in $SSH_AGENT_PIDS
	do
		printf 'killing ssh-agent %s\n' "$PID"
		kill "$PID"
	done
	unset SSH_AGENT_PATTERN SSH_AGENT_PIDS PID
fi
unset NRSHELLS

Je ne crois pas qu'il y ait d'équivalent au ~/.bash_logout en Bourne Shell, mais c'est mieux que rien. Le mécanisme exposé est suffisamment robuste pour capturer des service ssh-agent ayant été démarrés par des services systemd ou bien par des environnements de bureau. Il peut être nécessaire de raffiner un peu le SSH_AGENT_PATTERN en fonction des situations, par exemple si ssh-agent est invoqué pour lancer l'environnement graphique lui-même : longtemps la commande générique a été x-session-manager, puis depuis que j'utilise Sway pour bénéficier de Wayland, l'agent ssh se trouve lancer directement sway. Je ne garantis pas de la robustesse de l'ensemble à toutes les situations qui pourraient se présenter dans la nature, mais jusqu'à présent je n'ai jamais eu à m'en plaindre. Ou alors, si j'ai eu à m'en plaindre, je me suis dépêché de faire les ajustements nécessaires pour que ce ne soit plus le cas.

Corrections du 10 janvier : j'ai initialement rédigé cette entrée pour reporter ces segments de code dans un environnement professionnel et noté quelque problèmes qui n'ont pas survécu aux subtiles variations d'environnement avec ce que j'ai implémenté à la maison. D'une part, la socket (chaussette ? Merci Vincent Bernat…) peut apparaitre dans un sous-répertoire du home correspondant au glob ~/.ssh/agent/s.*.agent.* ou bien /tmp/ssh-*/agent.* pour les implémentations plus anciennes ; j'avais initialement oublié le second glob, mais c'est corrigé. D'autre part, j'ai commis une erreur de débutant en inversant les chevrons des scripts au moment de la conversion en HTML préformaté. Une fois ces deux soucis corrigés, j'ai pu utiliser mes agents ssh comme j'en ai l'habitude.

[ICO]NameLast modifiedSize
[PARENTDIR]Parent Directory  -

  —  Dernière entrée de journal