
sshroute v0.2.11
Router SSH consapevole della rete - instrada le connessioni verso diversi IP/porte/chiavi/host di salto in base alla VPN attiva o alla rete
sshroute
Router SSH consapevole della rete. Rileva la rete attiva o la VPN e seleziona automaticamente l'host, la porta, il file di identità e il jump host corretti per ogni connessione SSH, senza toccare ~/.ssh/config.
Come funziona
Definisci ogni host logico una volta con un profilo default e override opzionali per rete. Ad ogni connessione, sshroute rileva su quale rete ti trovi (VPN, LAN ufficio, peer WireGuard, ecc.) e risolve i parametri SSH corretti prima di passare il controllo al vero /usr/bin/ssh.
ssh myserver
→ sshroute rileva: corp-vpn è attiva
→ risolve: 10.100.0.50:2222 tramite bastion.corp.internal
→ exec /usr/bin/ssh -p 2222 -i ~/.ssh/corp_key -J bastion.corp.internal 10.100.0.50
Perché sshroute?
Per gli appassionati di homelab
Il tuo laboratorio ha probabilmente almeno due realtà: sei a casa sulla LAN, oppure sei fuori e ti connetti tramite WireGuard o un'altra VPN. Il problema è che ~/.ssh/config non sa in quale ti trovi — così ti ritrovi con alias separati (server-lan, server-vpn), o un jump host che funziona solo a metà, o semplicemente memorizzi gli IP.
sshroute risolve questo problema rilevando la rete corrente prima di ogni connessione. Quando l'interfaccia WireGuard è attiva e la route del peer esiste, si connette direttamente all'IP del tunnel. Quando sei sulla LAN, usa l'indirizzo locale. Quando nessuno dei due è raggiungibile, ricade sul nome host pubblico. Un alias, tre realtà, nessuna commutazione manuale.
Intercetta anche SSH in modo trasparente — git push, rsync, scp passano tutti attraverso automaticamente una volta impostata la modalità shadow. Nessun wrapper, nessuna funzione di shell, nessun pensiero.
Per ambienti aziendali
Le reti aziendali sono peggio. Hai Internet pubblico, forse una VPN site-to-site, forse una VPN personale split-tunnel, e all'interno di questa diversi jump host a seconda dell'ambiente che stai targetizzando — dev, staging, prod, ciascuno con il proprio bastion e la propria chiave. Mantenere tutto in ordine in ~/.ssh/config significa o un file enorme che si rompe ogni volta che l'infrastruttura cambia, o scrivere uno script che ogni membro del team mantiene in modo diverso.
sshroute ti permette di definire la logica di routing in modo dichiarativo, mantenerla in un file YAML con versionamento e condividerla con il team. La stessa configurazione funziona per tutti — la rete giusta viene rilevata automaticamente in base alle interfacce o route attive su ciascuna macchina. Chiavi, porte, utenti e jump host vengono risolti senza che l'utente debba pensarci.
Confronto
| Caratteristica | ~/.ssh/config | Solo WireGuard | Teleport / Boundary | sshroute |
|---|---|---|---|---|
| Rileva la rete corrente | ❌ | ❌ | ❌ | ✅ |
| Sceglie automaticamente il percorso migliore | ❌ | ❌ | ❌ | ✅ |
| Ricade su connessione fallita | ❌ | ❌ | ✅ | ✅ |
| Riconnessione automatica + re-routing su caduta | ❌ | ⚠️ il tunnel vaga | ⚠️ tramite proxy fisso | ✅ |
| Un comando per host, ovunque | ❌ | ⚠️ VPN deve essere attiva | ✅ | ✅ |
| Dimensione configurazione per 10 host × 4 percorsi | 📄 ~600 righe | 📄 ~600 righe + config VPN | 📄 configurazione lato server | 📄 ~60 righe |
| Dispositivi mobili in roaming | ⚠️ alias manuali | ⚠️ VPN necessaria | ✅ | ✅ |
| Concatenamento automatico jump host | ⚠️ -J manuale | ➖ n/a | ✅ | ✅ |
| Funziona con scp / rsync / git / Ansible | ✅ | ✅ | ⚠️ parziale | ✅ |
| Nessuna installazione lato server sui target | ✅ | ❌ | ❌ | ✅ |
| Nessun server di autenticazione o demone da eseguire | ✅ | ❌ | ❌ | ✅ |
| Nessun agente client | ✅ | ❌ | ❌ | ✅ |
| Open source, completamente self-hosted | ✅ | ✅ | ⚠️ open-core | ✅ |
Teleport e Boundary sono una categoria diversa — aggiungono controllo degli accessi, log di audit e autenticazione basata su certificati oltre al routing. Se è ciò di cui hai bisogno, usali. sshroute è per quando vuoi l'intelligenza di routing senza il sovraccarico operativo di gestire un server di autenticazione centrale.
Installazione
Download binario
Scarica l'ultima release da GitHub Releases. I binari sono disponibili per Linux, macOS e Android su AMD64 e ARM64.
Installazione con Go
go install github.com/thereisnotime/sshroute@latest
Android (Termux)
Scarica il tarball android_arm64 da GitHub Releases, estrailo e posiziona il binario in ~/.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
Aggiungi ~/.local/bin al tuo PATH in ~/.bashrc o ~/.profile se non lo è già:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
In alternativa, compila dal sorgente con Go di Termux. Poiché la toolchain ufficiale di Go non pubblica binari android/arm64, imposta GOTOOLCHAIN=local per usare ciò che Termux fornisce:
GOTOOLCHAIN=local go install github.com/thereisnotime/sshroute@latest
Dopo l'installazione, imposta il percorso del binario SSH poiché Termux non ha /usr/bin/ssh:
# ~/.config/sshroute/config.yaml
ssh_binary: /data/data/com.termux/files/usr/bin/ssh
Oppure tramite variabile d'ambiente: export SSHROUTE_SSH=$(which ssh)
Docker
docker run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
Podman
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
Su sistemi con SELinux abilitato (Fedora, RHEL, ecc.) aggiungi :Z al flag del volume:
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute:Z \
ghcr.io/thereisnotime/sshroute network
Modalità shadow (sostituzione SSH trasparente)
Installa sshroute come ssh prima nel tuo $PATH. Tutte le chiamate SSH — dal terminale, da git, rsync, scp — vengono intercettate automaticamente. Gli host non presenti nella tua configurazione passano a /usr/bin/ssh invariati.
mkdir -p ~/.local/bin
ln -s $(which sshroute) ~/.local/bin/ssh
# Aggiungi a ~/.bashrc o ~/.zshrc se non già presente:
export PATH="$HOME/.local/bin:$PATH"
Avvio rapido
# Aggiungi un host con un profilo predefinito
sshroute add myserver --host myserver.example.com --user alice --key ~/.ssh/id_ed25519
# Aggiungi un override specifico per VPN
sshroute add myserver --network vpn --host 10.8.0.50 --port 2222 --jump bastion.vpn
# Connettiti — la rete viene rilevata automaticamente
sshroute connect myserver
# Anteprima del comando risolto senza eseguirlo
sshroute connect myserver --dry-run
# Vedi quale rete è attualmente attiva
sshroute network
Comandi
Flag globali
Questi flag si applicano a ogni comando:
| Flag | Variabile d'ambiente | Default | Descrizione |
|---|---|---|---|
--config | SSHROUTE_CONFIG | ~/.config/sshroute/config.yaml | Percorso del file di configurazione |
-o, --output | table | Formato di output: table, json, yaml | |
-v, --verbose | SSHROUTE_VERBOSE=1 | false | Log di debug su stderr |
--dry-run | false | Stampa il comando SSH risolto senza eseguire |
init
Crea un file di configurazione iniziale con esempi commentati. Fallisce se il file esiste già.
| Flag | Default | Descrizione |
|---|---|---|
--force | false | Sovrascrive un file di configurazione esistente |
connect <alias>
Rileva la rete attiva, risolve i parametri SSH per alias ed esegue il vero binario SSH. Eventuali argomenti extra dopo l'alias vengono passati a SSH invariati.
| Flag | Default | Descrizione |
|---|---|---|
--fallback | false | Prova ogni profilo in ordine di priorità, ritentando il successivo solo in caso di fallimento della connessione (exit 255) |
--reconnect | false | Supervisiona la connessione e si riconnette automaticamente quando cade, rilevando nuovamente la rete attiva e risolvendo la route ogni volta |
--reconnect-delay | 2s | Attesa tra tentativi di riconnessione quando è impostato --reconnect |
Con --reconnect, sshroute mantiene ssh vivo attraverso cadute di connessione (sospensione del laptop, handoff WiFi, roaming tra reti). Poiché rileva la rete a ogni riconnessione, ti segue su una route diversa: ad esempio, dormire sulla LAN e svegliarsi su un hotspot si riconnette tramite la route pubblica invece di ritentare l'indirizzo LAN ora irraggiungibile. Un logout pulito (exit 0) o un fallimento di autenticazione/comando remoto ferma il ciclo; solo le cadute genuine di connessione provocano la riconnessione. Riconnetti esegue ssh come sottoprocesso (come --fallback), quindi sshroute rimane residente per la sessione; SIGINT/SIGTERM lo abbatte. Lo stato della sessione attraverso il breve intervallo è compito del tuo multiplexer (tmux/zellij); combina --reconnect con -- tmux attach o -- zellij attach -c <nome> per tornare direttamente nella tua sessione:
sshroute connect myserver --reconnect --fallback -- zellij attach -c lavoro
list
Elenca tutti gli host configurati e i parametri SSH che verrebbero utilizzati sulla rete corrente. Supporta -o table|json|yaml.
add <alias>
Aggiunge un host o ne aggiorna uno esistente. I flag omessi mantengono il loro valore corrente. Esegui più volte con diversi valori --network per creare override per rete.
| Flag | Default | Descrizione |
|---|---|---|
--host | Nome host o indirizzo IP | |
--port | 22 | Porta SSH |
--user | Nome utente SSH | |
--key | Percorso del file di identità (supporta ~) | |
--jump | Jump host — passato come -J a SSH | |
--network | default | Profilo di rete in cui scrivere i parametri |
remove <alias>
Rimuove tutti i profili per alias dalla configurazione.
network
Stampa il nome della rete attualmente rilevata (o default se nessuna corrisponde).
network list
Elenca tutte le reti configurate con la loro priorità, regole di controllo e stato attuale. Supporta -o table|json|yaml.
network test <nome>
Esegue ogni controllo per la rete nome e stampa superato/fallito per ogni regola. Utile per il debug della logica di rilevamento.
config
Stampa il percorso risolto del file di configurazione.
config edit
Apre il file di configurazione in $EDITOR (ricade su nano). Crea il file e la directory padre se non esistono.
resolve <alias>
Stampa i parametri SSH che verrebbero utilizzati per alias sulla rete corrente. Utile per debug e scripting. Usa --network <nome> per sovrascrivere la rete rilevata. Supporta -o table|json|yaml.
| Flag | Default | Descrizione |
|---|---|---|
--network | rilevamento automatico | Profilo di rete su cui risolvere |
copy <alias> <src> <dst>
Copia file da o verso un host configurato usando scp con gli stessi parametri risolti (chiave, porta, jump) di connect. Usa la sintassi <alias>:<percorso> per i percorsi remoti:
sshroute copy myserver ./local.txt myserver:/percorso/remoto/
sshroute copy myserver myserver:/file_remoto.txt ./locale/
La variabile d'ambiente SSHROUTE_SCP sovrascrive il binario scp utilizzato.
version
Stampa la versione, il commit git, la data di build e le informazioni sul runtime Go.
update
Aggiorna sshroute sul posto all'ultima release di GitHub. Scarica l'archivio per la tua piattaforma, verifica il suo sha256 rispetto a checksums.txt e, se cosign è installato, verifica la firma cosign della release, prima di sostituire atomicamente il binario in esecuzione.
sshroute update # scarica, verifica e installa l'ultima release
sshroute update --check # segnala solo se è disponibile una versione più recente
sshroute update --force # reinstalla l'ultima anche se già attuale
Se la verifica sha256 (o cosign, quando presente) fallisce, l'aggiornamento viene annullato e il binario rimane intatto. Questo è destinato alle installazioni del binario di release; se hai installato tramite go install o un gestore di pacchetti, aggiorna con quello.
File di configurazione
Posizione predefinita: ~/.config/sshroute/config.yaml
networks:
corp-vpn:
priority: 10 # inferiore = controllato per primo
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: # richiesto — usato quando nessuna rete corrisponde
host: myserver.example.com
port: 22
user: alice
key: ~/.ssh/id_ed25519
options: # opzionale — passato come flag 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" # sovrascrive il default solo per questa rete
office:
host: 192.168.1.50
Ogni host deve avere un profilo default. I profili di rete devono solo specificare i campi che differiscono dal default — i campi non impostati ereditano da default.
Campi del profilo host
| Campo | Tipo | Descrizione |
|---|---|---|
host | stringa | Nome host o indirizzo IP |
port | int | Porta SSH (default: 22) |
user | stringa | Utente SSH |
key | stringa | Percorso del file di identità (~ viene espanso) |
jump | stringa | Alias del jump host o user@host |
options | mappa | Flag arbitrari SSH -o Key=Value (es. ConnectTimeout, StrictHostKeyChecking) |
comment | stringa | Descrizione mostrata in sshroute list |
tags | lista | Tag per filtrare con sshroute list --tag |
Le chiavi di options vengono unite dal default nei profili di rete — i valori di rete sovrascrivono le chiavi corrispondenti, le chiavi non sovrapposte vengono ereditate.
Rilevamento rete
Le reti vengono valutate in ordine di priority (valore più basso per primo). L'ordine alfabetico rompe i pareggi. Viene utilizzata la prima rete i cui controlli passano tutti; se nessuna corrisponde, viene applicato default.
| Tipo di controllo | Passa quando | Campi richiesti |
|---|---|---|
route | La sottorete/IP appare nella tabella di routing del kernel | match |
interface | L'interfaccia nominata esiste ed è operativamente attiva | match |
ping | L'host risponde all'echo ICMP entro il timeout | host, timeout (opzionale, default 2s) |
exec | Il comando shell esce con codice 0 | command |
Più controlli all'interno di una definizione di rete utilizzano logica AND — tutti devono passare.
Esempi
I file di configurazione pronti all'uso sono in examples/:
| File | Caso d'uso |
|---|---|
basic.yaml | Singolo host, VPN vs fallback pubblico |
multi-network.yaml | LAN ufficio, VPN aziendale, VPN remota, pubblico |
wireguard-backconnect.yaml | Peer WireGuard che si riconnette a te |
jump-hosts.yaml | Bastion diversi per rete |
multi-zone-roaming.yaml | Homelab multi-zona con gateway WireGuard e dispositivi mobili in roaming |
Documentazione
Guide approfondite sono in docs/:
| Guida | Descrizione |
|---|---|
| Configurazione homelab | Homelab multi-zona con WireGuard, jump host, NAS, nodi k3s |
| Roaming multi-zona | Più LAN, gateway WireGuard, dispositivi mobili che vagano tra reti |
| Aziendale / multi-ambiente | Dev/staging/prod con bastion per ambiente e rilevamento VPN |
| Modalità shadow | Sostituzione SSH trasparente — git, rsync, scp, Ansible |
| Completamento shell | Completamento dinamico alias per bash, zsh, fish |
| Scripting e automazione | Uso di resolve e copy in script e pipeline CI |
Formati di output
Tutti i comandi di elenco supportano più formati di output:
sshroute list # table (default)
sshroute list -o json # JSON — per scripting
sshroute list -o yaml # YAML
sshroute network list -o json
Community
Ottieni il software — scarica un binario precompilato da Releases, installa con go install github.com/thereisnotime/sshroute@latest, o compila dal sorgente.
Feedback e segnalazioni di bug — apri un issue su GitHub Issues. Usa il template per segnalazione bug per comportamenti imprevisti e il template per richiesta funzionalità per idee.
Contribuire — vedi CONTRIBUTING.md per come impostare il progetto, eseguire i test e aprire una pull request. Le vulnerabilità di sicurezza devono essere segnalate privatamente tramite GitHub Security Advisories.
Compilazione dal sorgente
git clone [email protected]:thereisnotime/sshroute.git
cd sshroute
just build # output in bin/sshroute
just build-all # cross-compila linux/darwin × amd64/arm64
just test # esegue i test con rilevatore di race condition
just install # go install con ldflags di versione iniettati