Générateur de config SSH : stanzas wildcard, alias et connexions automatisées
Générez votre config SSH en quelques secondes — puis comprenez comment elle fonctionne : un wildcard élimine chaque ProxyJump et IdentityFile répété, les stanzas de domaine définissent les noms d'utilisateur pour les FQDN, les alias courts ont besoin de leur propre ligne User, et RemoteCommand vous connecte directement en tant qu'utilisateur applicatif.
Traduction automatique
Cet article fait partie de la série SSH — de votre première clé au développement distant :
- GitHub - Connectez votre compte en SSH et travaillez avec le protocole git@
- Docker secrets : utiliser votre clé SSH pendant le build
- Utiliser sftp en ligne de commande, avec ou sans proxy
- SSH - Autosuggestions avec ZSH
- SSH - Lancer un terminal sur votre session sans avoir à vous authentifier
- Comment se connecter à votre serveur d'hébergement en SSH
- Développement distant en SSH avec VSCode
- Ouvrir un DevContainer sur un serveur distant avec VSCode Remote SSH et ProxyJump
- Générateur de config SSH : stanzas wildcard, alias et connexions automatisées

SSH lit chaque stanza correspondante de haut en bas, et la première valeur l'emporte pour chaque directive — c'est ce qui rend Host * !bastion si puissant : une seule stanza couvre tous les serveurs d'un coup, fini la répétition de ProxyJump et IdentityFile. Les stanzas de domaine ajoutent le bon User pour les connexions par FQDN ; les alias courts doivent le répéter explicitement, car SSH fait la correspondance sur ce que vous tapez, pas sur le hostname résolu. Trois directives — ServerAliveInterval, ControlMaster, ConnectTimeout — renforcent les valeurs globales sans en changer la logique. Pour une machine qui mélange SSH professionnel et personnel, Include sépare proprement les deux. RemoteCommand transforme une séquence sudo su + cd en un simple alias.
Chaque serveur de mon environnement n'est accessible qu'à travers ma propre VM Windows qui joue le rôle de bastion. L'article sur ProxyJump et les DevContainers montre une config avec un seul alias. Ajoutez un deuxième serveur et la config double. Ajoutez-en dix et ça devient un problème de maintenance : changez une fois le hostname du bastion et vous réécrivez dix stanzas, chacune portant les mêmes lignes ProxyJump et IdentityFile.
La config ci-dessous couvre n'importe quel nombre de serveurs sans aucune répétition.
La configuration
Les deux chemins de connexion fonctionnent immédiatement — un alias court et un FQDN complet, sans aucun flag :
Pas de flag ProxyJump, pas de clé explicite — la config gère les deux de façon transparente. Créez C:\Users\your_laptop_user\.ssh\config (Windows ; WSL inclus) ou ~/.ssh/config (Linux/macOS) avec ceci :
# 1. Bastion — no ProxyJump, no IdentityFile
Host windows-vm-ip
User vm-user
# 2. Domain defaults — in effect when connecting via full FQDN only
Host *.cloud.project-a.internal
User user-a
Host *.cloud.corp.example
User user-b
Host *.office.corp.example
User user-b
# 3. Short aliases — HostName + User
# (domain defaults above do not apply to aliases — see article)
Host project_test
HostName server-test.cloud.project-a.internal
User user-a
Host project_prod
HostName server-prod.office.corp.example
User user-b
# 4. Explicit exceptions — bypass the bastion for specific hosts
# Must come BEFORE "Host *" — SSH uses first-value-wins per directive
Host github.com
ProxyJump none
# 5. Global defaults — fallback for every host except the bastion
Host * !windows-vm-ip
ProxyJump vm-user@windows-vm-ip
IdentityFile ~/.ssh/id_ed25519
Comment SSH lit un fichier de configuration
Trois règles expliquent pourquoi les stanzas se composent sans conflit :
- Plusieurs stanzas peuvent correspondre. Lancer
ssh project_prodamène SSH à parcourir le fichier de haut en bas et à collecter les réglages de chaque stanzaHostqui correspond àproject_prod, pas seulement la première. - La première valeur l'emporte pour chaque réglage — l'inverse de la plupart des outils. En CSS, dans les fichiers
.envou dans la plupart des systèmes de configuration, la dernière déclaration écrase les précédentes. SSH fait l'inverse : la première valeur rencontrée est conservée, et les stanzas suivantes ne peuvent que compléter les réglages non encore définis. Mettez la stanza la plus spécifique en premier ; le fallback wildcard en dernier. - Les motifs correspondent à l'argument de la ligne de commande, pas au hostname résolu.
Host *.office.corp.examplecorrespond àssh server-prod.office.corp.example. Il ne correspond pas àssh project_prod, même si leHostNamedeproject_prodse résout enserver-prod.office.corp.example.
Les cinq sections
Le snippet de config ci-dessus est découpé en cinq sections numérotées — voici ce que fait chacune et pourquoi elle est placée dans cet ordre.
Section 1 — Définition du bastion. Déclare le jump host avec son nom d'utilisateur. Pas de ProxyJump ici (il boucherait sur lui-même) et pas d'IdentityFile (la VM utilise l'authentification que vous avez configurée séparément pour elle).
Section 2 — Valeurs par défaut par domaine. Elles s'appliquent quand vous tapez un FQDN complet comme ssh server-prod.office.corp.example. SSH récupère le User ici, puisqu'aucune stanza précédente ne l'a défini. Plusieurs stanzas de domaine avec le même nom d'utilisateur, c'est normal — une par suffixe de domaine. Ces stanzas sont aussi utiles pour les scripts qui construisent des hostnames par programme ou pour les appels ssh-copy-id.
Section 3 — Alias courts. Chaque alias déclare HostName (le vrai FQDN) et User. Ajouter un nouvel alias de serveur, c'est exactement deux lignes.
Section 4 — Exceptions explicites. Tout host qui ne doit pas passer par le bastion — un service d'hébergement Git, un runner CI, une API SaaS — reçoit ProxyJump none ici. La position est structurante : cette stanza doit venir avant Host *.
Section 5 — Valeurs globales par défaut. Le préfixe ! dans Host * !windows-vm-ip signifie « tous les hosts sauf le bastion ». Placée en dernier, cette stanza fournit ProxyJump et IdentityFile comme fallbacks pour chaque stanza précédente qui ne les a pas définis — c'est-à-dire toutes. N'importe quelle stanza des sections 2, 3 ou 4 peut écraser ces valeurs simplement en déclarant la directive en premier, sans toucher à la section 5.
Renforcer la section 5 (optionnel)
Quatre directives étendent les valeurs globales sans en changer la logique. Ajoutez celles qui correspondent à votre environnement — aucune n'est requise pour que la config fonctionne.
# 4. Explicit exceptions — bypass the bastion for specific hosts
# Must come BEFORE "Host *" — SSH uses first-value-wins per directive
Host github.com
ProxyJump none
# 5. Global defaults — fallback for every host except the bastion
Host * !windows-vm-ip
ProxyJump vm-user@windows-vm-ip
IdentityFile ~/.ssh/id_ed25519
# Optional — prevent silently-dropped idle connections
ServerAliveInterval 30
ServerAliveCountMax 3
# Optional — fail fast instead of waiting the default 2 minutes
ConnectTimeout 5
ServerAliveInterval 30/ServerAliveCountMax 3— SSH envoie un paquet keepalive toutes les 30 secondes. Après 3 paquets sans réponse (90 secondes de silence), la connexion est fermée proprement au lieu de rester bloquée sur un prompt gelé. Sans ça, un terminal inactif sur un serveur d'entreprise se fige souvent sans aucun message d'erreur.ConnectTimeout 5— échoue après 5 secondes si le serveur est injoignable. Sur un réseau d'entreprise avec un bastion fiable, le timeout par défaut de 2 minutes n'est que du bruit.
ControlMaster ne fonctionne pas avec OpenSSH sous Windows
ControlMaster auto / ControlPath / ControlPersist activent le multiplexage SSH : la première connexion ouvre une socket de domaine Unix dans ~/.ssh/ ; les connexions suivantes réutilisent le tunnel au lieu d'en négocier un nouveau à travers le bastion. Sous Linux et macOS, ça marche bien.
Sous OpenSSH Windows avec ProxyJump, ces trois directives cassent silencieusement toutes les connexions — ssh se bloque ou sort avec Read from remote host: Unknown error — parce que l'implémentation des sockets Unix sous Windows n'est pas compatible avec le forwarding stdio basé sur des pipes de ProxyJump. Le commentaire # Optional n'est pas une protection suffisante : les lignes sont copiées et cassent tout.
Ne les mettez pas sous Windows. Si vous vous connectez depuis Linux ou macOS, ajoutez-les :
ControlMaster auto
ControlPath ~/.ssh/cm_%C
ControlPersist 10m
%C est un hash des paramètres de connexion — pas de deux-points, pas de caractères spéciaux. N'utilisez jamais %r@%h:%p : le : est illégal dans les noms de fichiers Windows et corrompt silencieusement le path de la socket, même quand ControlMaster est par ailleurs fonctionnel.
Pourquoi chaque alias a besoin de sa propre ligne User (à passer si la config fonctionne déjà chez vous)
Les stanzas de domaine de la section 2 ne définissent User que quand SSH y trouve une correspondance — et comme noté plus haut, SSH fait correspondre les motifs à l'argument de la ligne de commande, pas au hostname résolu.
En lançant ssh project_prod :
- Ne correspond pas à
Host *.office.corp.example(section 2) —project_prodn'est pas un FQDN. - Correspond à
Host project_prod(section 3) → appliqueHostName server-prod.office.corp.exampleetUseruser-b. - Ne correspond pas à
Host github.com(section 4) —project_prodn'est pas dans cette liste. - Correspond à
Host * !windows-vm-ip(section 5) → appliqueProxyJumpetIdentityFile.
Sans la ligne User dans la stanza de l'alias, SSH retombe sur votre nom d'utilisateur Windows local pour cette connexion — ce qui est presque certainement faux.
En lançant directement ssh server-prod.office.corp.example :
- Correspond à
Host *.office.corp.example(section 2) →Useruser-b. - Ne correspond pas à
Host github.com(section 4) — pas dans cette liste. - Correspond à
Host * !windows-vm-ip(section 5) →ProxyJump,IdentityFile.
Les deux chemins arrivent sur le bon utilisateur. Ils ont juste besoin de stanzas différentes pour y arriver.
Utilisateurs ZSH : autocomplétez vos alias
Le plugin zsh-ssh-config-suggestions lit ~/.ssh/config et propose des complétions sur ssh <Tab>. Chaque alias de la section 3 devient une suggestion — plus votre liste d'alias est longue, plus c'est utile.
Une réserve : le wildcard est vraiment universel (à passer si cette machine est purement professionnelle)
Host * !windows-vm-ip s'applique à chaque connexion SSH depuis cette machine — y compris ssh github.com ou ssh localhost. Les connexions vers des hosts externes tenteront un ProxyJump à travers la VM et échoueront si la VM n'est pas joignable.
Devcontainers : déclarez les exceptions avant Host *
Quand la config SSH est bind-mountée dans un devcontainer, le hostname du bastion peut ne pas se résoudre dans le réseau du container. Toute connexion SSH depuis le container — git push, scp, rsync — tombe sur Host *, tente un ProxyJump à travers le bastion, n'arrive pas à le résoudre et sort avec :
ssh: Could not resolve hostname your-bastion: Name or service not known
Les hosts qui doivent contourner le bastion ont besoin d'une stanza ProxyJump none explicite avant Host *. SSH applique la règle « première valeur gagnante » par directive : un ProxyJump none placé après Host * est silencieusement ignoré. La section 4 de la config ci-dessus existe précisément pour ça — ajoutez-y tout host qui se connecte directement (hébergement Git, runners CI, API tierces).
Trois options si ça vous pose problème :
- Remplacez
Host *par une liste de domaines explicite :Host *.project-a.internal *.cloud.corp.example *.office.corp.example. Les connexions vers tout ce qui est en dehors de cette liste ignorent complètement le bastion. - Gardez
Host *si cette machine utilise SSH exclusivement pour des serveurs internes à votre réseau d'entreprise. Sur une machine de travail dédiée, le wildcard convient très bien. - Écrasez des hosts spécifiques au-dessus de la section 4. Comme les valeurs globales sont en dernier, toute stanza placée avant elles l'emporte. Ajoutez une exemption n'importe où au-dessus de la section 4 — la position à l'intérieur des sections 1 à 3 n'a pas d'importance :
# Anywhere above section 4 — fits naturally in section 3 alongside the other aliases
Host github.com gitlab.com
ProxyJump none
IdentityFile ~/.ssh/id_ed25519_personal
# Section 4 — global defaults (last, unchanged)
Host * !windows-vm-ip
ProxyJump ...
- Découpez la config avec
Include— la solution la plus propre pour une machine utilisée à la fois pour du SSH professionnel et personnel :
# ~/.ssh/config
Include ~/.ssh/config.d/work
Include ~/.ssh/config.d/personal
~/.ssh/config.d/work contient la config d'entreprise complète (bastion, stanzas de domaine, alias, valeurs globales en dernier). ~/.ssh/config.d/personal contient une config propre sans routage par bastion. Les directives Include sont traitées de haut en bas avant tout bloc Host du fichier principal — OpenSSH 7.3+ requis.
Bonus : se connecter directement en tant qu'autre utilisateur
Certains serveurs exigent que vous vous connectiez avec votre propre compte puis basculiez vers un utilisateur applicatif — sudo su app_user suivi de cd /opt/project. La directive RemoteCommand automatise toute la séquence depuis l'alias.
Le générateur ci-dessous produit deux alias par serveur : un alias simple pour VSCode Remote Explorer (sans RemoteCommand) et une variante _app pour les sessions de terminal qui vous connecte directement en tant qu'utilisateur applicatif.
Générez votre configuration complète
Remplissez vos valeurs une fois et copiez le résultat directement dans ~/.ssh/config. Pour un second projet, copiez-collez uniquement les alias de la section 3 et changez le nom du projet — le bastion et les valeurs globales sont déjà en place.
# 1. Bastion — no ProxyJump, no IdentityFile
Host windows-vm-ip
User vm-user
# 2. Domain defaults — in effect when connecting via full FQDN only
Host *.office.corp.example
User user-b
# 3. Short aliases
Host project_test_app
RequestTTY yes
RemoteCommand sudo -u app-user bash -c "cd /opt/folder_devcontainer && exec bash -l"
Host project_test project_test_app
HostName server-test.office.corp.example
User user-b
Host project_prod_app
RequestTTY yes
RemoteCommand sudo -u app-user bash -c "cd /opt/project && exec bash -l"
Host project_prod project_prod_app
HostName server-prod.office.corp.example
User user-b
# 4. Global defaults — fallback for every host except the bastion
Host * !windows-vm-ip
ProxyJump vm-user@windows-vm-ip
IdentityFile ~/.ssh/id_ed25519
# Optional — prevent silently-dropped idle connections
ServerAliveInterval 30
ServerAliveCountMax 3Testez d'abord la connexion au bastion
Avant de tester un alias complet, vérifiez que le bastion lui-même est joignable et accepte votre clé :
$ ssh vm-user@windows-vm-ip "echo bastion-ok" bastion-ok
echo bastion-ok est une commande non interactive — elle se termine immédiatement après l'affichage, sans laisser de session ouverte. Si la sortie apparaît, la section 1 et la section 4 sont correctement câblées. Si ça bloque ou échoue, le problème se situe entre votre machine et le bastion, indépendamment de tout alias de serveur.
VSCode Remote Explorer ne fonctionne pas avec RemoteCommand
VSCode Remote SSH injecte son propre processus serveur pendant la connexion. RemoteCommand remplace le shell distant avant que cette injection puisse avoir lieu — la connexion se ferme avant que VSCode ait pu prendre pied. Utilisez les alias simples project_test / project_prod pour Remote Explorer ; naviguez vers le répertoire de l'application depuis VSCode une fois connecté.
Le RemoteCommand de la variante _app enchaîne deux choses : un cd vers le répertoire de l'application et un exec bash -l pour démarrer un shell de login en tant qu'utilisateur applicatif. RequestTTY yes alloue un pseudo-terminal — sans ça, la session se ferme immédiatement après la commande au lieu de vous donner un shell interactif.
$ ssh project_test_app mass_upload@server-test:/opt/folder_devcontainer$
$ ssh project_prod_app app-user@server-prod:/opt/project$
Les quatre alias récupèrent automatiquement le ProxyJump global de la section 4.
sudo doit être sans mot de passe — ou utilisez su
sudo -u app-user dans un RemoteCommand ne fonctionne silencieusement que si le fichier sudoers accorde NOPASSWD à votre compte pour cet utilisateur précis. Sinon, la connexion se bloquera en attendant un mot de passe que SSH ne peut pas fournir avant que le TTY soit complètement établi.
Si sudo sans mot de passe n'est pas configuré, utilisez plutôt sudo su - app-user — avec RequestTTY yes, le TTY est alloué avant l'exécution de la commande, donc la demande de mot de passe s'affiche normalement :
RemoteCommand sudo su - app-user -c "cd /opt/project && exec bash -l"
Vous verrez la demande de mot de passe sudo une fois, puis vous arriverez dans le shell.
Avertissement pour les environnements de production
Une bannière RemoteCommand n'est visible que par celui qui a configuré cet alias SSH. Le bon endroit pour un avertissement de production, c'est le serveur lui-même — ainsi il atteint tous les utilisateurs, quel que soit leur client.
Déposez un script dans /etc/profile.d/ sur le serveur. Chaque shell interactif source ce répertoire à la connexion :
#!/usr/bin/env bash
case "$(hostname -s)" in
*prod*|*prd*)
printf '\033[38;5;88;48;5;224m ⚠ Production server — be careful! \033[0m\n\n'
export PS1='\[\033[38;5;88;48;5;224m\] ⚠ PROD \[\033[0m\] \u@\h:\w\$ '
;;
esac
La paire de couleurs 38;5;88 (bordeaux foncé) sur 48;5;224 (rose pâle) est clairement visible sans être agressive — un rappel, pas un panneau d'alarme.
$ ssh project_prod_app ⚠ Production server — be careful! app-user@server-prod:/opt/project$
Comme exec bash -l dans le RemoteCommand démarre un shell de login, les scripts de profil s'exécutent et la bannière apparaît aussi sur chaque connexion _app. Aucun changement nécessaire dans la config SSH.
Détection par hostname vs fichier explicite
Faire correspondre hostname -s à *prod* est pratique mais fragile — un host nommé deployments-node ne correspondrait à rien. Un fichier marqueur explicite est plus fiable :
if [[ -f /etc/env-type ]] && grep -qx "production" /etc/env-type; then
Créez /etc/env-type avec le contenu production lors du provisioning d'un serveur. Le test est sans ambiguïté, quel que soit le hostname.
Conclusion
Une seule stanza wildcard élimine la répétition de ProxyJump et IdentityFile dans chaque alias. Les stanzas de domaine ajoutent les noms d'utilisateur pour l'accès direct par FQDN. Les alias courts répètent User explicitement — parce que SSH fait la correspondance sur ce que vous tapez, pas sur ce que l'alias résout.
Ajouter un nouveau serveur, c'est deux lignes : HostName et User. Le routage est déjà géré. Quand un serveur exige un utilisateur et un répertoire spécifiques, RemoteCommand transforme cette séquence en un simple alias. Et quand un serveur de production a besoin d'un avertissement visuel clair pour tout le monde, /etc/profile.d/ est le bon endroit — pas la config SSH.
Si vous devez générer ou gérer vos clés SSH, cet article couvre la génération de clés ed25519 et l'autorisation GitHub depuis zéro — la même clé que celle utilisée dans la ligne IdentityFile ci-dessus.
Cet article fait partie de la série SSH — de votre première clé au développement distant :
- GitHub - Connectez votre compte en SSH et travaillez avec le protocole git@
- Docker secrets : utiliser votre clé SSH pendant le build
- Utiliser sftp en ligne de commande, avec ou sans proxy
- SSH - Autosuggestions avec ZSH
- SSH - Lancer un terminal sur votre session sans avoir à vous authentifier
- Comment se connecter à votre serveur d'hébergement en SSH
- Développement distant en SSH avec VSCode
- Ouvrir un DevContainer sur un serveur distant avec VSCode Remote SSH et ProxyJump
- Générateur de config SSH : stanzas wildcard, alias et connexions automatisées





