
Routeur SSH conscient du réseau - achemine les connexions vers différentes IP/ports/clés/hôtes de saut en fonction du VPN actif ou du réseau
Routeur SSH conscient du réseau. Détecte votre réseau actif ou VPN et sélectionne automatiquement le bon hôte, port, fichier d'identité et hôte de rebond pour chaque connexion SSH — sans toucher à ~/.ssh/config.
Définissez chaque hôte logique une seule fois avec un profil default et des surcharges optionnelles par réseau. À chaque connexion, sshroute détecte sur quel réseau vous êtes (VPN, LAN de bureau, pair WireGuard, etc.) et résout les paramètres SSH corrects avant de passer la main au vrai /usr/bin/ssh.
ssh myserver
→ sshroute detecte : corp-vpn est actif
→ résout : 10.100.0.50:2222 via bastion.corp.internal
→ exec /usr/bin/ssh -p 2222 -i ~/.ssh/corp_key -J bastion.corp.internal 10.100.0.50
Votre lab a probablement au moins deux réalités : vous êtes soit à la maison sur le LAN, soit absent et vous vous connectez via WireGuard ou un autre VPN. Le problème est que ~/.ssh/config ne sait pas dans lequel vous êtes — vous finissez donc avec des alias séparés (server-lan, server-vpn), ou un hôte de rebond qui ne fonctionne qu'à moitié, ou vous mémorisez les IP.
sshroute résout ce problème en détectant votre réseau actuel avant chaque connexion. Lorsque l'interface WireGuard est active et que la route du pair existe, il se connecte directement à l'IP du tunnel. Quand vous êtes sur le LAN, il utilise l'adresse locale. Quand aucun des deux n'est joignable, il utilise le nom d'hôte public en secours. Un alias, trois réalités, zéro changement manuel.
Il intercepte également SSH de manière transparente — git push, rsync, scp passent tous automatiquement par lui une fois que vous avez configuré le mode shadow. Pas de wrappers, pas de fonctions shell, pas de réflexion.
Les réseaux d'entreprise sont pires. Vous avez l'internet public, peut-être un VPN site-à-site, peut-être un VPN personnel split-tunnel, et à l'intérieur vous avez différents hôtes de rebond selon l'environnement que vous ciblez — dev, staging, prod, chacun avec son bastion et sa clé. Garder cela en ordre dans ~/.ssh/config signifie soit un énorme fichier qui plante dès que l'infrastructure change, soit un script que toute l'équipe maintient différemment.
sshroute vous permet de définir la logique de routage de manière déclarative, de la conserver dans un fichier YAML versionné et de la partager dans toute l'équipe. La même configuration fonctionne pour tout le monde — le bon réseau est détecté automatiquement en fonction des interfaces ou routes actives sur chaque machine. Les clés, ports, utilisateurs et hôtes de rebond se résolvent sans que l'utilisateur ait à y penser.
Teleport et Boundary sont une catégorie différente — ils ajoutent le contrôle d’accès, les journaux d’audit et l’authentification par certificat en plus du routage. Si c’est ce dont vous avez besoin, utilisez-les. sshroute est pour quand vous voulez l’intelligence de routage sans la surcharge opérationnelle de gérer un serveur d’authentification central.
Téléchargez la dernière version depuis GitHub Releases. Des binaires sont disponibles pour Linux, macOS et Android sur AMD64 et ARM64.
go install github.com/thereisnotime/sshroute@latest
Téléchargez l’archive android_arm64 depuis GitHub Releases, extrayez-la et placez le binaire dans ~/.local/bin :
mkdir -p ~/.local/bin
curl -Lo "$TMPDIR/sshroute.tar.gz" \
https://github.com/thereisnotime/sshroute/releases/latest/download/sshroute_android_arm64.tar.gz
tar -xzf "$TMPDIR/sshroute.tar.gz" -C ~/.local/bin sshroute
chmod +x ~/.local/bin/sshroute
Ajoutez ~/.local/bin à votre PATH dans ~/.bashrc ou ~/.profile si ce n’est pas déjà fait :
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Sinon, compilez depuis les sources avec le Go de Termux. Comme la chaîne d’outils officielle de Go ne publie pas de binaires android/arm64, définissez GOTOOLCHAIN=local pour utiliser ce que Termux fournit :
GOTOOLCHAIN=local go install github.com/thereisnotime/sshroute@latest
Après l’installation, définissez le chemin du binaire SSH car Termux n’a pas /usr/bin/ssh :
# ~/.config/sshroute/config.yaml
ssh_binary: /data/data/com.termux/files/usr/bin/ssh
Ou via variable d’environnement : export SSHROUTE_SSH=$(which ssh)
docker run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
Sur les systèmes avec SELinux activé (Fedora, RHEL, etc.), ajoutez :Z au drapeau de volume :
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute:Z \
ghcr.io/thereisnotime/sshroute network
Installez sshroute en tant que ssh plus tôt dans votre $PATH. Tous les appels SSH — depuis votre terminal, git, rsync, scp — sont interceptés automatiquement. Les hôtes qui ne sont pas dans votre configuration sont transmis tels quels à /usr/bin/ssh.
mkdir -p ~/.local/bin
ln -s $(which sshroute) ~/.local/bin/ssh
# Ajoutez à ~/.bashrc ou ~/.zshrc si ce n’est pas déjà présent :
export PATH="$HOME/.local/bin:$PATH"
# Ajouter un hôte avec un profil par défaut
sshroute add myserver --host myserver.example.com --user alice --key ~/.ssh/id_ed25519
# Ajouter une surcharge spécifique à un VPN
sshroute add myserver --network vpn --host 10.8.0.50 --port 2222 --jump bastion.vpn
# Se connecter — le réseau est détecté automatiquement
sshroute connect myserver
# Afficher la commande résolue sans l’exécuter
sshroute connect myserver --dry-run
# Voir quel réseau est actif actuellement
sshroute network
Ces drapeaux s’appliquent à chaque commande :
initCrée un fichier de configuration de démarrage avec des exemples commentés. Échoue si le fichier existe déjà.
| Drapeau | Défaut | Description |
|---|---|---|
--force | false | Écrase un fichier de configuration existant |
connect <alias>Détecte le réseau actif, résout les paramètres SSH pour alias et exécute le vrai binaire SSH. Les arguments supplémentaires après l’alias sont transmis tels quels à SSH.
Avec --reconnect, sshroute maintient ssh actif malgré les coupures de connexion (mise en veille du portable, basculement WiFi, itinérance entre réseaux). Comme il redétecte le réseau à chaque reconnexion, il vous suit sur une route différente : par exemple, se mettre en veille sur le LAN et se réveiller sur un hotspot se reconnecte via la route publique au lieu de réessayer l’adresse LAN maintenant inaccessible. Une déconnexion propre (exit 0) ou un échec d’authentification/commande distante arrête la boucle ; seules les véritables coupures de connexion déclenchent une reconnexion. Reconnect exécute ssh comme un sous-processus (comme --fallback), donc sshroute reste résident pendant la session ; SIGINT/SIGTERM le termine. L’état de session au-delà de la coupure est géré par votre multiplexeur (tmux/zellij) ; combinez --reconnect avec -- tmux attach ou -- zellij attach -c <nom> pour retomber directement dans votre session :
sshroute connect myserver --reconnect --fallback -- zellij attach -c work
listListe tous les hôtes configurés et les paramètres SSH qui seraient utilisés sur le réseau actuel. Supporte -o table|json|yaml.
add <alias>Ajoute un hôte ou met à jour un existant. Les drapeaux omis conservent leur valeur actuelle. Exécutez plusieurs fois avec différentes valeurs de --network pour construire des surcharges par réseau.
remove <alias>Supprime tous les profils de alias de la configuration.
networkAffiche le nom du réseau actuellement détecté (ou default si aucun ne correspond).
network listListe tous les réseaux configurés avec leur priorité, leurs règles de vérification et leur état actif actuel. Supporte -o table|json|yaml.
network test <name>Exécute chaque vérification pour le réseau name et affiche succès/échec par règle. Utile pour déboguer la logique de détection.
configAffiche le chemin résolu vers le fichier de configuration.
config editOuvre le fichier de configuration dans $EDITOR (utilise nano par défaut). Crée le fichier et son répertoire parent s’ils n’existent pas.
resolve <alias>Affiche les paramètres SSH qui seraient utilisés pour alias sur le réseau actuel. Utile pour le débogage et les scripts. Utilisez --network <name> pour remplacer le réseau détecté. Supporte -o table|json|yaml.
| Drapeau | Défaut | Description |
|---|---|---|
--network | auto-détection | Profil réseau pour lequel résoudre |
copy <alias> <src> <dst>Copie des fichiers vers ou depuis un hôte configuré en utilisant scp avec les mêmes paramètres résolus (clé, port, rebond) que connect. Utilisez la syntaxe <alias>:<path> pour les chemins distants :
sshroute copy myserver ./local.txt myserver:/remote/path/
sshroute copy myserver myserver:/remote/file.txt ./local/
La variable d’environnement SSHROUTE_SCP remplace le binaire scp utilisé.
versionAffiche la version, le commit git, la date de build et les informations d’exécution Go.
updateMet à jour sshroute sur place vers la dernière version GitHub. Il télécharge l’archive pour votre plateforme, vérifie son sha256 par rapport à checksums.txt, et — si cosign est installé — vérifie la signature cosign de la version, avant de remplacer atomiquement le binaire en cours d’exécution.
sshroute update # télécharger, vérifier et installer la dernière version
sshroute update --check # seulement signaler si une version plus récente est disponible
sshroute update --force # réinstaller la dernière version même si déjà à jour
Si la vérification sha256 (ou cosign, quand présente) échoue, la mise à jour est annulée et le binaire reste intact. Cette commande cible les installations du binaire de version ; si vous avez installé via go install ou un gestionnaire de paquets, mettez à jour avec ceux-ci.
Emplacement par défaut : ~/.config/sshroute/config.yaml
networks:
corp-vpn:
priority: 10 # plus petit = vérifié en premier
checks:
- type: interface
match: wg0
- type: route
match: 10.100.0.0
office:
priority: 20
checks:
- type: ping
host: 192.168.1.1
timeout: 500ms
hosts:
myserver:
default: # obligatoire — utilisé quand aucun réseau ne correspond
host: myserver.example.com
port: 22
user: alice
key: ~/.ssh/id_ed25519
options: # optionnel — passé comme drapeaux SSH -o Key=Value
ConnectTimeout: "10"
ServerAliveInterval: "30"
corp-vpn:
host: 10.100.0.50
port: 2222
key: ~/.ssh/corp_key
jump: bastion.corp.internal
options:
ConnectTimeout: "5" # remplace le défaut pour ce réseau uniquement
office:
host: 192.168.1.50
Chaque hôte doit avoir un profil default. Les profils réseau n’ont besoin de spécifier que les champs qui diffèrent du défaut — les champs non définis héritent de default.
Les clés de options sont fusionnées de default dans les profils réseau — les valeurs réseau remplacent les clés correspondantes, les clés non chevauchantes sont héritées.
Les réseaux sont évalués dans l’ordre de priority (la valeur la plus petite en premier). L’ordre alphabétique départage les ex æquo. Le premier réseau dont toutes les vérifications réussissent est utilisé ; si aucun ne correspond, default s’applique.
Plusieurs vérifications au sein d’une même définition de réseau utilisent la logique ET — toutes doivent réussir.
Des fichiers de configuration prêts à l’emploi se trouvent dans examples/ :
Des guides détaillés se trouvent dans docs/ :
Toutes les commandes de liste supportent plusieurs formats de sortie :
sshroute list # table (défaut)
sshroute list -o json # JSON — pour les scripts
sshroute list -o yaml # YAML
sshroute network list -o json
Obtenir le logiciel — téléchargez un binaire pré-compilé depuis Releases, installez avec go install github.com/thereisnotime/sshroute@latest, ou compilez depuis les sources.
Retours et signalements de bugs — ouvrez un ticket sur GitHub Issues. Utilisez le modèle de rapport de bug pour les comportements inattendus et le modèle de demande de fonctionnalité pour les idées.
Contribuer — voir CONTRIBUTING.md pour savoir comment configurer le projet, exécuter les tests et ouvrir une pull request. Les vulnérabilités de sécurité doivent être signalées de manière privée via GitHub Security Advisories.
git clone [email protected]:thereisnotime/sshroute.git
cd sshroute
just build # sortie dans bin/sshroute
just build-all # cross-compile linux/darwin × amd64/arm64
just test # exécute les tests avec détecteur de race
just install # go install avec les ldflags de version injectés
|
|
| Fonctionnalité | ~/.ssh/config | WireGuard seulement | Teleport / Boundary | sshroute |
|---|
| Détecte votre réseau actuel | ❌ | ❌ | ❌ | ✅ |
| Choisit automatiquement le meilleur chemin | ❌ | ❌ | ❌ | ✅ |
| Secours en cas d’échec de connexion | ❌ | ❌ | ✅ | ✅ |
| Reconnexion automatique + reroutage à la coupure | ❌ | ⚠️ itinérance du tunnel | ⚠️ via proxy fixe | ✅ |
| Une commande par hôte, n’importe où | ❌ | ⚠️ VPN doit être actif | ✅ | ✅ |
| Taille de config pour 10 hôtes × 4 chemins | 📄 ~600 lignes | 📄 ~600 lignes + config VPN | 📄 config côté serveur | 📄 ~60 lignes |
| Appareils mobiles itinérants | ⚠️ alias manuels | ⚠️ VPN requis | ✅ | ✅ |
| Chaînage automatique d’hôte de rebond | ⚠️ -J manuel | ➖ n/a | ✅ | ✅ |
| Fonctionne avec scp / rsync / git / Ansible | ✅ | ✅ | ⚠️ partiel | ✅ |
| Aucune installation côté serveur sur les cibles | ✅ | ❌ | ❌ | ✅ |
| Pas de serveur d’authentification ni de démon | ✅ | ❌ | ❌ | ✅ |
| Pas d’agent client | ✅ | ❌ | ❌ | ✅ |
| Open source, entièrement auto-hébergé | ✅ | ✅ | ⚠️ open-core | ✅ |
| Drapeau | Variable d’environnement | Défaut | Description |
|---|
--config | SSHROUTE_CONFIG | ~/.config/sshroute/config.yaml | Chemin du fichier de configuration |
-o, --output | table | Format de sortie : table, json, yaml | |
-v, --verbose | SSHROUTE_VERBOSE=1 | false | Journalisation de débogage sur stderr |
--dry-run | false | Affiche la commande SSH résolue sans l'exécuter |
| Drapeau | Défaut | Description |
|---|
--fallback | false | Essaie chaque profil dans l’ordre de priorité, en réessayant le suivant seulement en cas d’échec de connexion (code de sortie 255) |
--reconnect | false | Supervise la connexion et se reconnecte automatiquement lorsqu’elle est interrompue, en redétectant le réseau actif et en résolvant à nouveau la route à chaque fois |
--reconnect-delay | 2s | Délai entre les tentatives de reconnexion quand --reconnect est défini |
| Drapeau | Défaut | Description |
|---|
--host | Nom d’hôte ou adresse IP | |
--port | 22 | Port SSH |
--user | Nom d’utilisateur SSH | |
--key | Chemin du fichier d’identité (supporte ~) | |
--jump | Hôte de rebond — passé en -J à SSH | |
--network | default | Profil réseau dans lequel écrire les paramètres |
| Champ | Type | Description |
|---|
host | string | Nom d'hôte ou adresse IP |
port | int | Port SSH (défaut : 22) |
user | string | Utilisateur SSH |
key | string | Chemin du fichier d'identité (~ est expansé) |
jump | string | Alias d'hôte de rebond ou user@host |
options | map | Drapeaux SSH arbitraires -o Key=Value (ex. ConnectTimeout, StrictHostKeyChecking) |
comment | string | Description affichée dans sshroute list |
tags | list | Tags pour le filtrage avec sshroute list --tag |
| Type de vérification | Réussite quand | Champs requis |
|---|
route | Le sous-réseau/IP apparaît dans la table de routage du noyau | match |
interface | L’interface nommée existe et est opérationnellement active | match |
ping | L’hôte répond à l’écho ICMP dans le délai imparti | host, timeout (optionnel, défaut 2s) |
exec | La commande shell se termine avec le code 0 | command |
| Fichier | Cas d’utilisation |
|---|
basic.yaml | Hôte unique, VPN vs secours public |
multi-network.yaml | LAN du bureau, VPN entreprise, VPN distant, public |
wireguard-backconnect.yaml | Pair WireGuard qui se connecte en retour vers vous |
jump-hosts.yaml | Différents bastions par réseau |
multi-zone-roaming.yaml | Home lab multi-zone avec passerelle WireGuard et appareils mobiles itinérants |
| Guide | Description |
|---|
| Configuration home lab | Home lab multi-zone avec WireGuard, hôtes de rebond, NAS, nœuds k3s |
| Itinérance multi-zone | Plusieurs LAN, passerelle WireGuard, appareils mobiles qui se déplacent entre réseaux |
| Environnement professionnel / multi-environnement | Dev/staging/prod avec bastions par environnement et détection VPN |
| Mode shadow | Remplacement transparent de SSH — git, rsync, scp, Ansible |
| Complétion shell | Complétion dynamique d’alias pour bash, zsh, fish |
| Scripts et automatisation | Utilisation de resolve et copy dans des scripts et pipelines CI |