2026-08-12
alias, tag et match
Imaginons le scénario suivant:
- je dois me connecter à 3 domaines: foo.fr, bar.org et baz.com
- chaque domaine est divisé en 2 sous-domaines: .dev et .prod
- je dois me connecter avec mon login actuel, un login spécifique (“pouet”) et en “root” (pas la peine de gnagna root gnagna pabien: je sais)
- le nommage des machines sur lesquelles je dois me connecter est identique: ‘vm’ + ‘-’ + service (où service est dans la liste http, cache, db)
Un extrait des connexions possibles:
$ ssh vm-http.dev.foo.fr
$ ssh pouet@vm-cache.prod.bar.org
$ ssh root@vm-db.dev.baz.com
La complétion
On peut se simplifier la vie en créant des fichiers correspondant aux connexions:
$ mkdir .ssh/completion/ && cd .ssh/completion
$ touch vm-http.dev.foo.fr pouet@vm-cache.prod.bar.org root@vm-db.dev.baz.com ...
et compléter la commande ssh avec la liste de ces fichiers. Inconvénient: la touche Tab va passer un sale quart d’heure.
Mon idéal
Il faudrait avoir une “commande” par domaine: foo pour se connecter à foo.fr, bar pour bar.org et baz pour baz.com . En rajoutant une lettre (minuscule et/ou majuscule ?) pour le sous-domaine, on aurait Pfoo pour se connecter à .prod.foo.fr et dbaz pour .dev.baz.com .
Pour les logins et les machines, il ne faudrait préciser que le service et utiliser une lettre pour le login: Rdb pour root@vm-db, pcache pour pouet@vm-cache, http pour vm-http .
En assemblant le tout, l’extrait des connexions possibles deviendrait:
$ dfoo http
$ Pbar pcache
$ dbaz Rdb
Le mixte minuscule/majuscule n’est là que pour illustrer le propos et pourrait ici se traduire par “si c’est important/risqué/dangereux alors c’est en majuscule”.
Match
Une de mes utilisations de cette directive de mon .ssh/config est de définir des “raccourcis”:
Match originalhost owrt
HostName openwrt.mon.domaine.que.j.ai
mais si je veux pouvoir préciser un éventuel login, je dois être un peu plus générique:
Match originalhost *db
HostName vm-db
qu’on traduira par “si le nom de machine de la commande ssh se termine par db alors le nom de machine à utiliser est vm-db”. J’utilise la même chose pour les logins:
Match originalhost p*
User pouet
Match originalhost R*
User root
À noter que le “p” passerait en majuscule (pour bien le distinguer du nom de machine) dans le cas où un service commencerait par “p” (proxy, pgsql …).
Tag
C’est avec cette fonctionnalité que je vais résoudre en partie la simplification domaine / sous-domaine. Il est possible de passer un “tag” à une connexion à l’aide du paramètre -P tag et de définir des valeurs par “tag”:
Match tagged old
KexAlgorithms diffie-hellman-group1-sha1
Pour vérifier si la directive est prise en compte:
$ ssh -P old -G pouet | grep ^kexalgorithms
kexalgorithms diffie-hellman-group1-sha1
Chaque domaine / sous-domaine va correspondre à un “tag” qui va définir le nom de domaine:
Match tagged devfoo
CanonicalDomains .dev.foo.fr
...
Match tagged prodbaz
CanonicalDomains .prod.baz.com
Je dois aussi forcer les machines commençant par “vm-” à utiliser un nom de domaine:
Match host vm-*
CanonicalizeHostname yes
Je vérifie mes combinaisons “tag” / “host”:
$ ssh -G -P devfoo Rdb | grep -e '^user ' -e '^hostname '
user root
hostname vm-db.dev.foo.fr
Attention, une connexion ne peut avoir qu’un seul “tag” et l’ordre des directives est important !
Alias
Je n’ai plus qu’à définir les alias:
alias dfoo ssh -P devfoo
alias Pfoo ssh -P prodfoo
...
alias Pbaz ssh -P prodbaz
et attendre que mes doigts mémorisent tout ça.
Un petit pour la route
Parce que la vie n’est qu’une longue suite d’exceptions, imaginons qu’une machine d’un sous-domaine ne respecte pas la convention de nommage:
# putain de vs-database de mes couilles
Match tagged devbar originalhost *db
HostName vs-database
Match originalhost *db
HostName vm-db
Tiré d’un exemple @TAF (et oui, le commentaire est d’origine).
Commentaires: https://github.com/bsdsx/blog_posts/issues/24
2023-06-10
Une configuration ssh aux petis oignons
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 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.ni3
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 ni3.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