1. Crea una chiave per il tunnel
Sulla macchina che avvia il tunnel, crea un account locale e una chiave senza passphrase, perché il servizio parte senza nessuno presente. Poi salva la chiave host del server, così la prima connessione non si ferma a chiedere:
sudo apt install autossh
sudo useradd -m -s /usr/sbin/nologin tunnel
sudo -u tunnel mkdir -m 700 /home/tunnel/.ssh
sudo -u tunnel ssh-keygen -t ed25519 -N "" -C tunnel -f /home/tunnel/.ssh/id_ed25519
ssh-keyscan -t ed25519 server | sudo -u tunnel tee /home/tunnel/.ssh/known_hosts
Confronta quella chiave host con la vera impronta del server prima di fidarti. Vedi known_hosts spiegato.
2. Limita l'account sul server
Una chiave senza passphrase su una macchina remota dovrebbe aprire solo questo tunnel. Sul server, crea un account chiamato tunnel e metti la chiave pubblica nel suo ~/.ssh/authorized_keys:
restrict,port-forwarding ssh-ed25519 AAAA... tunnel
Poi aggiungi questo in fondo a /etc/ssh/sshd_config:
Match User tunnel
AllowTcpForwarding remote
PermitListen localhost:2222
X11Forwarding no
ForceCommand /usr/sbin/nologin
sudo sshd -t && sudo systemctl reload ssh
Abbiamo provato il risultato con questa chiave:
| Tentativo | Risultato |
|---|---|
-R 2222:localhost:22 | Funziona |
-R 2223:localhost:22 | remote port forwarding failed for listen port 2223 |
| Eseguire un comando o una shell | This account is currently not available. |
-L 9000:localhost:22 | Connessione chiusa, non passa nulla |
L'ultima riga richiede il blocco Match. Con il solo permitlisten="localhost:2222" in authorized_keys, nella nostra prova un inoltro -L funzionava ancora, e permetteva alla chiave di raggiungere i servizi sul loopback del server. Altre informazioni sulle opzioni delle chiavi sono in come limitare una chiave SSH.
3. Crea il servizio
Salva questo come /etc/systemd/system/autossh-tunnel.service:
[Unit]
Description=Reverse SSH tunnel to server
Wants=network-online.target
After=network-online.target
[Service]
User=tunnel
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now autossh-tunnel.service
systemctl status autossh-tunnel
Cosa fa ogni parte
-M 0disattiva la porta di monitoraggio di autossh, che quindi riavviasshsolo quandosshtermina. Il README di autossh suggerisce le opzioni keepalive in sostituzione della porta di monitoraggio.ServerAliveInterval=30eServerAliveCountMax=3fanno usciresshdopo circa 90-120 secondi di silenzio dal server. Nella nostra prova, un intervallo di 5 secondi con lo stesso conteggio ha impiegato 20 secondi, e un congelamento di 100 secondi con questi valori non lo ha fatto scattare.ExitOnForwardFailure=yesfa usciresshse la porta non si può aprire. Senza, puoi ritrovarti con una connessione viva ma senza tunnel.AUTOSSH_GATETIME=0fa continuare a provare autossh anche se il primo tentativo fallisce, cosa importante all'avvio.Restart=alwaysfa ripartire il servizio se muore autossh stesso.- Non usare qui l'opzione
-fdi autossh, perché systemd si aspetta che il processo resti in primo piano.
4. Guardalo riprendersi
| Cosa abbiamo fatto | Cosa è successo |
|---|---|
| Ucciso la sessione sul server | autossh ha avviato subito un nuovo ssh |
| Congelato il server per 28 secondi, con intervallo di 5 secondi | Timeout, server server not responding. dopo 20 secondi, poi una nuova connessione quando il server è tornato |
Ucciso autossh con kill -9, e il suo ssh | systemd ha riavviato il servizio e NRestarts è andato a 1 |
Puoi seguirlo tu stesso con journalctl -u autossh-tunnel -f. Sul server, ss -ltn | grep 2222 mostra la porta.
Se la porta resta occupata dopo un guasto di rete
Dopo un vero guasto, il server può tenere ancora la vecchia connessione e la sua porta, quindi la nuova fallisce con remote port forwarding failed finché la vecchia non scade. Nel blocco Match sul server, aggiungi:
ClientAliveInterval 30
ClientAliveCountMax 3
Il server allora scollega i client morti dopo circa 90 secondi e i nuovi tentativi riescono.
Per gli altri motivi per cui la porta può essere occupata, vedi tunnel SSH: Address already in use e remote port forwarding failed.
Ti serve davvero autossh
Con systemd e le opzioni keepalive funziona anche ssh semplice. ssh termina e Restart=always lo rilancia. Sostituisci il comando nell'unità con:
ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
Nella nostra prova si è ripreso da una sessione uccisa con un riavvio di systemd, dopo i 10 secondi di RestartSec. autossh riparte prima, ed è l'opzione se non hai systemd.
Una differenza che abbiamo visto: systemctl restart ha registrato status=1/FAILURE per autossh mentre si fermava. Il servizio è comunque ripartito normalmente.
Domande
Cosa fa autossh -M 0?
Disattiva la porta di monitoraggio. autossh riavvia ssh solo quando ssh esce, e le opzioni ServerAliveInterval e ServerAliveCountMax fanno uscire ssh quando la connessione muore.
Serve autossh se uso systemd?
Non sempre. Anche ssh da solo, con le opzioni keepalive e Restart=always, torna su. autossh riparte prima e funziona anche senza systemd.
Perché il tunnel inverso fallisce dopo un'interruzione di rete?
Il server può tenere ancora la vecchia connessione e la sua porta. Aggiungi sul server ClientAliveInterval 30 e ClientAliveCountMax 3, così chiude i client morti dopo circa 90 secondi.
Fonti
- README di autossh
- ssh_config(5), manuale OpenBSD
- sshd_config(5), manuale OpenBSD
- Manuale systemd.service(5)