Tunnel SSH spiegati: port forwarding locale, remoto e dinamico

Il port forwarding SSH è di tre tipi. ssh -L apre una porta sulla tua macchina che raggiunge qualcosa dal lato del server. ssh -R apre una porta sul server che raggiunge qualcosa dal tuo lato. ssh -D apre un proxy SOCKS locale che manda ogni connessione attraverso il server. Aggiungi -N per non eseguire nessun comando remoto e -o ExitOnForwardFailure=yes così un tunnel che non riesce ad aprirsi termina con un errore.

Aggiornata il . Tutti gli inoltri, gli indirizzi di bind, i valori di GatewayPorts e di AllowTcpForwarding e i messaggi di errore sono stati provati con OpenSSH 10.3 su macOS come client verso OpenSSH 10.0 su Debian 13, usando piccoli server HTTP come destinazioni. Più opzioni -L sulla stessa connessione, -L attraverso -J e l'errore di configurazione per i due punti in LocalForward sono stati riprovati con OpenSSH 10.0 su Debian 13.

Quale ti serve?

OpzioneLa porta si apre suUsalo per
-L localeLa tua macchinaRaggiungere un servizio che solo il server può raggiungere, come un database.
-R remotoIl serverFar raggiungere al server qualcosa sulla tua macchina, come un sito di sviluppo.
-D dinamicoLa tua macchinaNavigare o collegarti a molti host come se fossi sul server.

Un modo facile per ricordarli: -L ascolta sulla tua macchina locale, -R ascolta sul server remoto, e -D è dinamico perché la destinazione la sceglie l'app ogni volta.

Inoltro locale: -L

ssh -N -L 8080:localhost:80 user@server
your app -> your machine, port 8080 -> SSH -> server -> localhost:80 as seen by the server

Il formato è porta_locale:destinazione:porta_destinazione. Ora http://127.0.0.1:8080 sulla tua macchina mostra il server web che ascolta solo sull'indirizzo di loopback del server.

Il punto chiave è che la destinazione la risolve il server. localhost indica il server stesso, non la tua macchina. La destinazione può anche essere un altro host raggiungibile dal server, e quell'ultimo tratto non è cifrato da SSH:

ssh -N -L 8080:intranet.example.com:80 user@server

Inoltro remoto: -R

ssh -N -R 9000:localhost:3000 user@server
a program on the server -> server, port 9000 -> SSH -> your machine -> localhost:3000 as seen by your machine

Ora un programma sul server può aprire http://127.0.0.1:9000 e raggiungere l'app che gira sulla tua macchina. Funziona anche quando la tua macchina è dietro NAT, perché è il tuo lato ad aver avviato la connessione.

Inoltro dinamico: -D

ssh -N -D 1080 user@server
your app -> SOCKS proxy on your machine, port 1080 -> SSH -> server -> any host:port the app asks for

Punta un'applicazione che supporta SOCKS a 127.0.0.1:1080. Per provarlo con curl:

curl --socks5-hostname 127.0.0.1:1080 http://intranet.example.com/

--socks5-hostname fa fare la ricerca DNS al server, quindi i nomi interni funzionano e la ricerca non esce dalla tua rete. Il semplice --socks5 risolve il nome sulla tua macchina.

Impostazioni del browser e fughe DNS sono spiegate in come usare SSH come proxy SOCKS.

Più porte, o attraverso un jump host

Ripeti l'opzione per aprire più inoltri sulla stessa connessione:

ssh -N -L 127.0.0.1:8080:localhost:80 -L 127.0.0.1:5433:localhost:5432 user@server

Quando il server sta dietro un bastion, aggiungi -J. L'inoltro parte dall'ultimo server della catena, quindi è quel server a risolvere la destinazione:

ssh -N -J user@bastion -L 127.0.0.1:5433:localhost:5432 user@db-server

Le catene di server sono spiegate in come usare ProxyJump e un jump host.

Eseguire un tunnel senza shell

  • -N non esegue nessun comando remoto. La sessione trasporta solo il tunnel.
  • -f passa in background dopo il login, così puoi ancora digitare prima una passphrase.
  • -o ExitOnForwardFailure=yes termina con un errore se l'inoltro non può essere creato.
ssh -f -N -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:localhost:80 user@server

Senza ExitOnForwardFailure, ssh stampa un avviso e resta connesso senza un tunnel funzionante, cosa che in uno script confonde. Con l'opzione, una porta locale già in uso termina con codice di uscita 255 e Could not request local forwarding, mentre una porta remota già usata termina con remote port forwarding failed for listen port.

Un'insidia, vista nei nostri test: con un semplice -L 8080:..., ssh ascolta sia su 127.0.0.1 sia su ::1. Se solo uno dei due è occupato, per esempio da un altro programma su IPv4, ssh stampa bind [127.0.0.1]:8080: Address already in use e continua a girare anche con ExitOnForwardFailure=yes. La tua app potrebbe allora raggiungere quell'altro programma. Scrivere l'indirizzo, come in -L 127.0.0.1:8080:..., rende questo un vero errore, quindi usa quella forma negli script.

Per fermare un tunnel in background, trova il suo processo e terminalo:

pgrep -f 'L 127.0.0.1:8080'
kill PID

Per i tunnel che avvii spesso, un socket di controllo ti permette di fermarli per nome con ssh -O exit. Vedi come salvare, avviare e fermare i tunnel SSH.

Per tenere un tunnel nella tua configurazione, usa LocalForward, RemoteForward e DynamicForward:

Host dbtunnel
  HostName 203.0.113.10
  User user
  LocalForward 127.0.0.1:8080 localhost:80
  ExitOnForwardFailure yes

Legare solo a localhost

Una porta inoltrata è aperta a chiunque possa raggiungerla, e il servizio dietro di essa può fidarsi delle connessioni provenienti dal server. Per impostazione predefinita ssh si lega solo al loopback, quindi lascialo così a meno che tu non abbia un motivo.

Questi comandi aprono la porta locale a tutta la tua rete. Abbiamo verificato che entrambi ascoltano su tutte le interfacce:

ssh -N -L 0.0.0.0:8080:localhost:80 user@server
ssh -N -g -L 8080:localhost:80 user@server

Per -R decide il server, con GatewayPorts nella sua configurazione di sshd:

GatewayPorts-R 9000:...-R 0.0.0.0:9000:...
no (predefinito)Solo loopbackSolo loopback, senza errore
clientspecifiedSolo loopbackTutte le interfacce
yesTutte le interfacceTutte le interfacce, e anche 127.0.0.1:9000 si apre su tutte le interfacce

Quindi, se la tua porta -R non è raggiungibile dall'esterno, di solito la causa è GatewayPorts. Esporre a internet un servizio del tuo portatile in questo modo raramente è ciò che vuoi. Se ti serve, preferisci clientspecified e aggiungi una regola del firewall.

Quando il server rifiuta

Lo controlla AllowTcpForwarding nella configurazione di sshd del server. I valori sono yes (predefinito), no, local (consente -L e -D) e remote (consente -R).

  • Un -L o -D bloccato si connette senza problemi. L'errore arriva solo quando qualcosa usa la porta: channel 2: open failed: administratively prohibited: open failed.
  • Un -R bloccato dice remote port forwarding failed for listen port.

La pagina di manuale osserva che disattivare l'inoltro serve a poco se gli utenti hanno anche una shell, perché possono eseguire un proprio forwarder. Per limitare davvero qualcuno, togli anche la shell, come in un utente solo tunnel per un database.

Altri due errori capitano spesso. Quando la porta locale o remota è occupata, vedi Address already in use e remote port forwarding failed. Quando le connessioni attraverso il tunnel vengono rifiutate, vedi administratively prohibited: open failed.

Domande

Che differenza c'è tra ssh -L e ssh -R?

-L apre una porta sulla tua macchina che porta a qualcosa raggiungibile dal server. -R apre una porta sul server che riporta a qualcosa sulla tua macchina.

Come inoltro più porte con un solo comando ssh?

Ripeti l'opzione: ssh -N -L 127.0.0.1:8080:localhost:80 -L 127.0.0.1:5433:localhost:5432 user@server. Una sola connessione le porta tutte. In ~/.ssh/config aggiungi una riga LocalForward per ogni porta.

Come tengo attivo un tunnel SSH?

Avvialo con -N e -o ExitOnForwardFailure=yes, e fallo ripartire dopo una caduta con autossh o un servizio systemd. Vedi autossh e systemd.

Perché il tunnel si connette ma non funziona niente?

Il server può vietare l'inoltro con AllowTcpForwarding. Un -L o -D bloccato si connette lo stesso e fallisce solo quando si usa la porta, con administratively prohibited: open failed.

Il port forwarding SSH è sicuro?

Il traffico è cifrato tra la tua macchina e il server SSH. Il tratto dal server alla destinazione no, e una porta che ascolta su 0.0.0.0 la può usare chiunque sulla tua rete, quindi lascia come indirizzo di ascolto 127.0.0.1.

Fonti

Altro in Tunnel e jump host

Tutte le guide