Gestionnaire de mot de passe avec tmux

Comment entrer automatiquement son mot de passe depuis tmux ?

On va utiliser deux super capacités de tmux:

  • récupérer ce qui est affiché dans le terminal: tout ce qui est affiché peut être intercepté et traité dans un script avec la commande capture-pane;
  • entrer des caractères comme s’ils provenaient directement de l’utilisateur à la manière de l’outil expect.

Une commande pour récupérer le contenu du terminal:

$ tmux capture-pane -p -t "$TMUX_PANE"

la variable $TMUX_PANE contient l’identifiant du panneau courant depuis lequel on exécute la commande.

Une commande pour envoyer des caractères vers un panneau de tmux:

$ tmux send-keys -t "$PANE" "echo hello" ENTER

Ici, le mot ENTER désigne le retour à la ligne.

Pour gérer la liste de mots de passe et la sélection par l’utilisateur

Pour permettre à l’utilisateur de choisir le mot de passe qu’il veut entrer, on va utiliser fzf, l’outil incontournable de menu avec sélection fuzzy.

Enfin, on va définir un format de fichier texte pour stocker notre liste de mot de passe:

système1 mot_de_passe1
système2 mot_de_passe2

On considérera l’espace comme notre séparateur de champs.

Une commande pour utiliser fzf avec la sélection offerte sur le premier champs (celui associé au système) et renvoyant le second champ (celui associé au mot de passe) lors de la sélection par l’utilisateur:

$ cat ma_liste | fzf --with-nth=1 --bind 'enter:execute(echo {2})+abort'

Mais comment assurer la sécurité de la liste des mots de passe ?

Tout d’abord, on peut en restreindre l’accès par les autorisations d’accès fichier:

$ chmod 600 ma_liste

Ensuite, on peut en chiffrer le contenu:

$ openssl enc -d -pbkdf2 -aes-128-cbc -in ma_liste -out ma_liste_chiffree

Oui mais comment faire en sorte d’en autoriser l’accès une fois en début de session et de permettre l’accès sans restriction ensuite à la manière du ssh-agent?

  • on va déchiffrer le contenu du fichier de mots de passe dans une variable d’un script résident;
  • on va créer un fichier fifo avec des droits d’accès restreint sur lequel notre script va écrire de manière automatique et continuelle;
  • dans notre script d’entrée automatique de mot de passe utilisant tmux, on va lire à la demande de l’utilisateur le contenu pour sélectionner et obtenir le mot de passe à entrer;

Les avantages d’un tel système sont de:

  • protéger la liste de mot de passe sur le disque: ils sont chiffrés et ne peuvent pas être retrouvés si un intrus récupére le contenu du répertoire de l’utilisateur;
  • déchiffrer la liste de mots de passe uniquement en mémoire et seulement une fois lors de l’exécution initiale du script;
  • échanger ces mots de passe avec le script d’entrée dans tmux par l’intermédiaire d’un fichier fifo avec la protection des droits d’accès fichier.

Le script réalisant l’accès à la liste des mots de passe:

#!/bin/bash

# command to cipher the password file
# openssl enc -pbkdf2 -aes-128-cbc -pass pass:$MDP -in ~/.ssh/logins -out ~/.ssh/loginz
# chmod 600 ~/.ssh/loginz
# rm ~/.ssh/logins

umask 077
FIFODIR="/tmp/$USER/secure"
FIFOPATH="$FIFODIR/fifo"
DATA="$(openssl enc -d -pbkdf2 -aes-128-cbc -in ~/.ssh/loginz)"
rm -rf "$FIFODIR"
mkdir -p "$FIFODIR"
mkfifo "$FIFOPATH" || { rmdir "$FIFODIR"; exit 1; }

while true
do
  echo "Access request at $(date)"
  echo "$DATA" > $FIFOPATH
done

Le fichier ~/.ssh/loginz contient la liste des mots de passe chiffrée.

Et maintenant le script tmux_password

#!/bin/bash

umask 077
PANE=$TMUX_PANE
# Verify if the last line of the panel in tmux contains the string `password` to avoid leaking the password on the terminal
OK=$(tmux capture-pane -p -t "$PANE" | sed '/^[[:blank:]]*$/ d'| tail -n 1 | grep password)
if [ -n "$OK" ]
then
tmux popup -T "Select password" -E bash -c "cat /tmp/$USER/secure/fifo | fzf --with-nth=1 --bind 'enter:execute(echo {2})+abort' > /tmp/$USER/secure/.xxx"
tmux send-keys -t "$PANE" $(cat ~/.xxx) ENTER
rm /tmp/$USER/secure/.xxx
fi

Ici, je vérifie que la dernière ligne du terminal contient bien la chaîne password avant d’envoyer les caractères du mot de passe.

Une meilleure méthode serait de vérifier l’état du terminal et savoir si l’écho est actif ou non, c-à-d si le terminal affiche ou non les caractères entrés au clavier:

  • récupérer le tty du panneau:
$ tmux list-panes -aF "#{pane_tty}:#{pane_id}" | grep '%23' | grep -oE "^[^:]*"
/dev/pts/12
$ PTY=$(tmux list-panes -aF "#{pane_tty}:#{pane_id}" | grep '%23' | grep -oE "^[^:]*")
$ stty -a -F $PTY

%23 est l’identifiant du panneau donné par la variable d’environnement $TMUX_PANE

  • lire les modes de configuration du terminal:
$ stty -a -F /dev/pts/12
speed 38400 baud; rows 35; columns 135; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = <undef>; discard = <undef>; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig -icanon iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

Le mode echo qui régit ou non l’affichage des caractères est inactif avec le préfixe -: il vaut -echo

Malheureusement, sous zsh le seul mode qui diffère est le mode icanon entre le mode sans saisie de mot de passe de ssh (où tout caractère entré est affiché sur le terminal) et le mode avec saisie de mot de passe (où les caractères entrés ne sont pas affichés sur le terminal):

$ stty -a -F /dev/pts/12
speed 38400 baud; rows 35; columns 135; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten -echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

On peut néanmoins vérifier avec strace le travail de ssh lors de la saisie de mot de passe:

openat(AT_FDCWD, "/dev/tty", O_RDWR)    = 4
ioctl(4, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(4, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(4, SNDCTL_TMR_CONTINUE or TCSETSF, {B38400 opost isig icanon -echo ...}) = 0
ioctl(4, TCGETS, {B38400 opost isig icanon -echo ...}) = 0

ssh demande bien le changement du mode de terminal -echo

Cette vérification supplémentaire peut être ajoutée dans le script mais peut ne fonctionner qu’avec le shell zsh.

En conclusion

tmux est un outil formidable avec beaucoup d’applications possibles !

Dernière modification: 02/04/2023