Autour de SSH

Depuis que j’ai commencé mon nouveau boulot, j’ai bricolé des trucs mais je suis bien incapable de reconnaître si c’est bien ou cruellement mauvais. Pour rappel je suis autodidacte, il est parfois difficile de savoir quand on suit une mauvaise voie alors je partage avec vous ;)

Complétion SSH

On tape ssh luig, on appuie sur Tab, la complétion affiche alors ssh luigi.pizza.net.

Dans mon ~/.bashrc, j’ai complete -W "$(<~/.ssh/hosts)" ssh sshfsu qui me permet d’avoir la complétion pour ssh et ma fonction sshfsu. Le fichier hosts n’est pas un fichier système, c’est moi qui le maintiens avec une liste des serveurs sur lesquels je me connecte. Ça marche parfaitement, rien à dire, je ne peux plus m’en passer. J’ai vu que certains parsent le fichier ~/.ssh/know_hosts. Sur Ubuntu par défaut le nom des hosts est hashé. On peut via une option SSH afficher en clair le nom des hosts dans know_hosts mais c’est déconseillé, ça abaisse la sécurité. Je vous renvoie vers l’article Sécuriser son fichier SSH known_hosts.

D’autres parsent le fichier ~/.ssh/config, je crois que c’est la solution que j’ai vu la plus souvent revenir. En ce qui me concerne, ça ne s’applique pas bien à mon utilisation. J’utilise une « bête » liste contenant plus de 200 serveurs. L’avantage c’est que c’est clair, simple, rien à parser. L’inconvénient, aucune automatisation. Je récupère la liste des serveurs de mon entreprise et l’envoie dans ~/.ssh/hosts via un script mais je parle d’automatisation directement avec SSH. Se baser sur ~/.ssh/know_hosts était clairement le bon plan. Le fichier se met à jour dès qu’on se connecte à un serveur et on l’a alors en complétion. Remarquons tout de même qu’à l’heure d’aujourd’hui où il n’est plus rare de jongler avec des centaines de conteneurs et serveurs, il peut être pertinent d’avoir déjà une liste bien remplie plutôt que la remplir au fil de l’eau en se connectant sur chaque serveur.

Comment procédez-vous de votre côté ? Pour Zsh, ça m’intéresse également.

ssh-agent

Wikipédia explique bien le concept de ssh-agent. Le but principal est d’éviter de retaper la phrase secrète de notre clé SSH privée 30 fois si on se connecte à 30 serveurs (je simplifie). Pour la majorité des distribs l’agent est chargé au lancement de la session graphique. Je suis sur Xubuntu et dans /etc/X11/Xsession.options, j’ai une option use-ssh-agent.

Il y a 247 manières de gérer le ssh-agent : manuellement, des fonctions dans ~/.bashrc, un service systemd (voir ArchWiki), j’ai retenu pour ma part Keychain (GitHub). C’est le plus simple je trouve, ça s’occupe de tout, ça gère SSH et GPG enfin je n’ai pas retesté depuis mais c’était le seul à ne pas me redemander ma phrase secrète lorsque j’ouvrais un nouvel onglet sur Terminator.

apt install keychain
sed -i '/use-ssh-agent/s/^/#/' /etc/X11/Xsession.options # On commente la ligne use-ssh-agent sinon on a des messages d'erreurs
eval $(keychain --eval --quiet id_ed25519) # À placer dans ~/.bashrc

La prochaine fois que vous ouvrirez un terminal, il vous demandera votre phrase secrète puis terminé. Je vous renvoie vers l’article de Qanuq très complet sur keychain. Puisqu’on parle de ssh-agent je recommande chaudement l’article de Aeris sur ssh-agent + agent-forward.

Fonctions SSHFS

Au début j’avais appelé cette fonction sshfsup puis j’ai changé son nom en sshfsu, elle est à placer typiquement dans ~/.bashrc. Elle me permet de monter aisément un serveur via SSHFS : sshfsu server1.bloglibre.net. À noter que j’ai activé la complétion pour cette fonction. Vous pouvez remplacer /var par ce que vous voulez évidemment genre ~/, / etc. Merci de ne pas me souffler dans les commentaires « oh la la c’est pas très très bien/pro », j’en ai besoin et le but est de partager, ça peut servir à d’autres dans un contexte non professionnel.

sshfsu() {
cd && mkdir -p ${1%%.*}; sshfs cascador@$1:/var ${1%%.*}; (caja ${1%%.*} &> /dev/null &)
}

J’utilise le Parameter Expansion de Bash, $1 c’est donc server1.bloglibre.net, ${1%%.*} devient donc server1.
cd && mkdir -p ${1%%.*} : Je me place dans ~/ puis crée le dossier server1
sshfs cascador@$1:/var ${1%%.*} : Je monte le serveur via SSHFS
(caja ${1%%.*} &> /dev/null &) : J’ouvre le dossier server1 dans le navigateur de fichiers (caja). A noter que j’aurais pu utiliser xdg-open à la place de caja ainsi ça aurait été le navigateur de fichiers par défaut qui aurait été utilisé, c’est ce que je vous recommande d’ailleurs

La fonction sshfsd (sshfsdown) me permet de démonter tous les montages SSHFS d’un coup (en général je n’en ai qu’un). Simple, efficace et élégante. Quelqu’un a mieux ?

sshfsd() {
mount -t fuse.sshfs | awk '{system("fusermount -u " $3 " && rmdir " $3)}'
}

mount -t fuse.sshfs : Je liste les montages dont le type de système de fichiers est SSHFS
awk '{system("fusermount -u " $3 " && rmdir " $3)}' : Grâce à awk (voir Input/Output Functions pour system) on prend la 3ème colonne qui correspond au point de montage (avec l’exemple de sshfsu, ça donnera /home/cascador/server1) puis on démonte fusermount -u et on supprime le dossier rmdir

Autres liens intéressants

Je vous invite à consulter les articles de la marque OpenSSH sur le Jdh et vous recommande particulièrement celui de Arnaud et Guillaume. Je vous rappelle également mes articles sur l’option Match de sshd_config et sur le bien utile sshrc.

Je n’ai pas parlé de ~/.ssh/config si je le fais ce sera dans un autre article, celui-ci étant déjà costaud.

Tcho !

Déjà 14 avis pertinents dans Autour de SSH

  • faelar
    Hello.

    Au boulot j’ai un script qui vient actualiser un fichier avec la liste des hosts remontés par notre gestionnaire de conf. Il sert de ref. pour fzf (https://github.com/junegunn/fzf), qui permet de filtrer sur les éléments du nom. Par exemple en écrivant ‘db5prod’ tu peux trouver un host du genre « mysqldb-5.prod.linux.osef.blah ». Quand tu valide l’élément de la liste sélectionné, ça l’ajoute à ta ligne de commande > « ssh mysqldb-5.prod.linux.osef.blah ».

  • David_5.1
    Pour le fait de hasher les noms des serveurs dans known_hosts, c’est vrai que ça évite qu’on puisse lister facilement depuis la machine (ou depuis un backup si on le sauvegarde) avec ce fichier spécifique. Mais si on garde à côté dans son historique la liste des commandes ssh et autres lancées, c’était pas forcément utile de s’embêter à les supprimer d’un fichier…
    Et si la sécurité de serveurs repose sur le fait qu’on ne puisse pas trouver la liste des machines pour lesquelles on vient de trouver des identifiants, le soucis n’est pas dans le fait que la liste soit trouvable à mon avis.

    Et trouver des listes de machines, si on a les identifiants d’un admin qui a accès au DNS, c’est pas très dur, et sinon, on peut jouer avec des scans de réseau, certifcate transparency pour voir si des certificats TLS publics ont été émis pour un ou plusieurs noms de domaines, et probablement d’autres outils encore auxquels je ne pense pas là.

  • bunam
    hello

    j’ai bien cherché il y a quelques temps comment ajouter plus simplement la completions avec les « fonctions » toutes faites de bash_completion à des commandes, alias ou fonctions :

    # force le chargement + export de la fonction _ssh
    # sinon on a une erreur : sshik bash: completion: function `_ssh’ not found
    # le loader est trouvable comme ceci, en cherchant le truc par défaut
    # $ complete | grep — -D
    # complete -F _completion_loader -D
    _completion_loader ssh

    complete -F _ssh sshw
    complete -F _ssh sshrc
    complete -F _ssh sshp
    complete -F _ssh sshik
    complete -F _known_hosts curl
    #complete -F _known_hosts dig
    complete -F _known_hosts ping

    notes :
    _ssh vas aussi chercher dans le fichiers hosts

    alias sshw= »ssh -o ConnectionAttempts=100″
    alias sshp= »ssh -o PubkeyAuthentication=no »
    function sshik () {
    ssh « ${@} » ‘bash -s’ < ~/bin/ikeys.sh
    echo "… et on se connecte …"
    ssh "${@}"
    }

    sshrc (https://github.com/Russell91/sshrc)

    @+

  • rimeno
    Merci pour l’astuce sshfs !
    Du coup, j’ai fait des petites modifications pour que ce soit plus générique :
    * utilisation possible d’un nom d’utilisateur (pas en dur dans la fonction)
    * utilisation possible d’un nom d’hôte court, sans domaine (si le serveur est dans le fichier ~/.ssh/config par exemple)
    * montage d’un répertoire au choix (pas en dur dans la fonction)
    * ouverture du répertoire dans ce que tu veux en option

    https://paste.debian.net/1025045/

    Dans ton exemple, ça serait :
    $ sshfsu cascador@server1.bloglibre.net:/var
    qui serait monté dans ~/mnt/server1__var
    si tu as complété ton fichier de ~/.ssh/config avec les infos de ce serveur, ça pourrait être :
    $ sshfsu server1:/var

    Pour ouvrir Caja dans la foulée :
    $ sshfsu server1:/var caja

    Merci encore pour tes deux fonctions sympa :)

  • rimeno
    Salut,
    merci pour tes deux fonctions sshf

    Du coup, je me suis permis de les améliorer : https://paste.debian.net/1025045/

    Le nom d’utilisateur et le chemin distant ne sont plus codés en dur, et ça permet de faire :
    * sshfsu user@host.domain:/path/to/dir
    → ~/mnt/host__path_to_dir
    * sshfsu host:/path/to/dir caja
    → ouvre caja dans le repertoire ~/mnt/host__path_to_dir

    Encore merci !

  • rimeno
    Hop,

    en fait, le dernier « rmdir ${SSHFSMNT} », c’est pour supprimer le ~/mnt si j’en ai plus besoin, le précédent, c’était juste pour supprimer le point de montage du serveur dans se répertoire.

    Dans la version modifié que je te propose, le dossier distant que je veux monter est extrait de $1, ça permet d’avoir la complétion bash de scp. Je garde le $2 pour l’action que je veux faire sur le dossier (caja, ranger, ncdu, rien…).

    Désolé pour le double commentaire, j’avais pas vu que le premier était passé en modération, j’ai donc écris un V2 que tu peux supprimer, histoire de nettoyer le bruit ;)

    ++

Les commentaires sont fermés.