10/06/2023
202306100800 ssh
ssh possède une option assez pratique pour qui veut apporter des modifications à sa configuration sans tout casser:
-G Causes ssh to print its configuration after evaluating Host and Match blocks and exit.
Pour avoir une idée de ce qu'il est possible de configurer:
$ ssh -F /dev/null -G localhost | wc -l 81
On va donc plutôt filtrer la sortie:
$ ssh -F /dev/null -G localhost | grep -e port -e localhost host localhost hostname localhost port 22 gatewayports no nohostauthenticationforlocalhost no
AddressFamily
Cette option permet de définir l'usage d'ipv4, d'ipv6 ou des deux. Comme chez moi, le ssh ne fonctionne qu'en ipv6, je peux ajouter dans mon ~/.ssh/config:
AddressFamily inet6
Sauf que:
- certains pouilleux ne veulent pas faire d'ipv6
- certains équipements sont incompatibles avec ipv6
- on peut vouloir forcer l'usage d'ipv4
Je commence par identifier le problème:
$ echo 'AddressFamily inet6' > /tmp/ssh_config $ ssh -F /tmp/ssh_config github.com ssh: Could not resolve hostname github.com: Address family for hostname not supported $ ssh -F /tmp/ssh_config github.com 192.168.0.1 ssh: Could not resolve hostname 192.168.0.1: Name does not resolve
Je distingue ces citoyens de seconde zone avec une directive Match:
$ cat /tmp/ssh_config Match host github.com,10.*,192.168.* AddressFamily inet Match all AddressFamily inet6
et je teste:
$ ssh -F /tmp/ssh_config -G github.com | grep address addressfamily inet $ ssh -F /tmp/ssh_config -G gitlab.com | grep address addressfamily inet6 $ ssh -F /tmp/ssh_config -G 192.168.0.1 | grep address addressfamily inet
En cas de liste, attention à ne pas utiliser d'espace, uniquement la virgule. Pour le réseau 172.16.0.0/12, c'est un peu plus long:
$ grep 172 /tmp/ssh_config $ Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.*
Pour mon usage d'ipv4, je ne souhaite pas polluer mon .ssh/known_hosts:
$ cat /tmp/ssh_config Match host github.com,192.168.*,10.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6
IdentityFile
J'ai une clef pour chaque serveur publique sur lequel je me connecte et j'ai la bonne idée de la nommer en fonction du serveur:
$ cat /tmp/ssh_config Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 Match host github.com,gitlab.com IdentityFile ~/.ssh/%h
Je teste:
$ ssh -F /tmp/ssh_config -G github.com | grep -e address -e identityfile addressfamily inet identityfile ~/.ssh/%h $ ssh -F /tmp/ssh_config -G gitlab.com | grep -e address -e identityfile addressfamily inet6 identityfile ~/.ssh/%h $ ssh -F /tmp/ssh_config -G localhost | grep -e address -e identityfile addressfamily inet6 identityfile ~/.ssh/id_rsa identityfile ~/.ssh/id_ecdsa identityfile ~/.ssh/id_ecdsa_sk identityfile ~/.ssh/id_ed25519 identityfile ~/.ssh/id_ed25519_sk identityfile ~/.ssh/id_xmss identityfile ~/.ssh/id_dsa
Parce que l'ordre compte
J'utilise toujours une clef sauf pour le tld .ru:
Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 PreferredAuthentications publickey Match host github.com,gitlab.com IdentityFile ~/.ssh/%h Match host *.ru PreferredAuthentications password
Pas marche:
$ ssh -F /tmp/ssh_config -G fail.ru | grep -e preferredauthentications preferredauthentications publickey
On ne peut pas redéfinir une directive, comme indiqué en début de page de manuel:
Unless noted otherwise, for each parameter, the first obtained value will be used.
Pour mon cas d'usage je peux:
- déplacer ma dernière directive avant le Match all
- ajouter en fin de fichier une directive Match all contenant la directive PreferredAuthentications publickey
Include et alias
La directive suivante définit un raccourci vers github.com
Host gh Hostname github.com
que l'on peut placer dans un fichier dédié. Une directive Include :
$ cat /tmp/ssh_config_alias Host gh Hostname github.com $ cat /tmp/ssh_config Include /tmp/ssh_config_alias # Chemin complet car le fichier de config n'est pas un de ceux par défaut Match host github.com,10.*,192.168.*,172.16.*,172.17.*,172.18.*,172.19.*,172.2?.*,172.30.*,172.31.* AddressFamily inet Match host 1* UserKnownHostsFile none Match all AddressFamily inet6 Match host github.com,gitlab.com IdentityFile ~/.ssh/%h Match host *.ru PreferredAuthentications password Match all PreferredAuthentications publickey
et le tour est joué:
$ ssh -F /tmp/ssh_config -G gh | grep -e address -e identityfile addressfamily inet identityfile ~/.ssh/%h
J'ai quelques machines virtuelles qui sont dans un sous-domaine dédié:
Host nbsd2?,al3?,fbsd4? Hostname %n.xen
Mais ce n'est pas la bonne solution: le token %n n'est pas disponible dans une directive Hostname. Je vais utiliser les directives Canonicalize :
Match host nbsd2? al3? fbsd4? CanonicalizeHostname yes CanonicalDomains xen.bsdsx.fr # oui, le domaine complet
Si la configuration DNS est correcte alors:
$ ssh -F /tmp/ssh_config -G al30 | grep -e hostname hostname al30.ni3.bsdsx.fr canonicalizehostname true
Control
Quand on doit avoir plusieurs connections vers une machine, on comprend vite l'utilité des directives Control et ça ne coûte pas plus cher de l'activer par défaut:
$ cat /tmp/ssh_config_alias ControlMaster auto ControlPath ~/.ssh/.%r@%h:%p ...
Match vs Host
J'ai toujours utilisé la directive Host, c'est pour ce billet que je suis passé à Match. Ces deux directives ne diffèrent que par leur syntaxe:
Host *.ru *.ch Match host *.ru,*.ch
mais seul Match permet ce genre de chose:
Match host *.prod.mon.domaine user backup localuser !production exec "id -Gn %u | grep -q admin" PreferredAuthentications publickey IdentitiesOnly yes IdentityFile ~/.ssh/id_rsa_backup_with_force_command
Si:
- un utilisateur qui n'est pas 'production' et qui fait partie du groupe 'admin'
- se connecte en tant que 'backup'
- vers une machine du domaine '.prod.mon.domaine'
alors on utilise une clef spécifique. Oui cet exemple est plutôt capillotracté (mais aucun drosophile n'a été ... lors de la rédaction de ce billet).
Un dernier pour la route
C'est plus par principe qu'autre chose mais si il y a bien un truc que je ne supporte pas c'est tout ce qui ne sert à rien (oui, je ne supporte pas grand chose :) . Quand je vois tous les fichiers testés par
$ ssh -v ...
je me dis que ces quelques lignes ne peuvent pas faire de mal:
GlobalKnownHostsFile /dev/null # je n'ai jamais créé les fichiers de cette directive UserKnownHostsFile ~/.ssh/known_hosts # known_hosts2 n'existe pas # Pour ne pas tester des clefs qui n'existent pas IdentityFile ~/.ssh/id_rsa IdentityFile ~/.ssh/id_ecdsa_sk AddKeysToAgent yes # ajouter les clefs à l'agent lors de leur première utilisation
Enjoy !
Commentaires: https://github.com/bsdsx/blog_posts/issues/18