Inoltro locale (-L): la porta è occupata sul tuo computer
Con ssh -L 8080:localhost:80 user@server puoi vedere:
bind [127.0.0.1]:8080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 8080
Could not request local forwarding.
Scopri chi occupa la porta. Funziona su macOS e Linux:
lsof -nP -iTCP:8080 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh 4242 user 4u IPv4 ... 0t0 TCP 127.0.0.1:8080 (LISTEN)
Su Windows usa netstat -ano | findstr :8080.
ssh: un tunnel precedente è ancora attivo, spesso avviato con-fe dimenticato. Eseguikill 4242con il PID mostrato, oppure vedi la sezione sulle connessioni condivise più sotto.- Un server di sviluppo, Docker o un database: scegli un'altra porta locale. Va cambiato solo il numero a sinistra:
ssh -L 18080:localhost:80 user@server. - Su macOS,
ControlCesulla porta 5000 o 7000 è il ricevitore AirPlay. Scegli un'altra porta, oppure disattiva il ricevitore AirPlay in Impostazioni di Sistema.
Non sai quale opzione ti serve? Tunnel SSH: inoltro locale, remoto e dinamico confronta i tre tipi.
È comparsa solo la prima riga
Se vedi solo bind [127.0.0.1]:8080: Address already in use e la sessione si apre, SSH ha agganciato il lato IPv6 ma non quello IPv4. -L 8080:... prova entrambi. La porta funziona a metà: i client che usano 127.0.0.1 raggiungono l'altro programma, non il tuo tunnel. Indica l'indirizzo e SSH segnalerà l'errore correttamente:
ssh -o ExitOnForwardFailure=yes -L 127.0.0.1:8080:localhost:80 user@server
Inoltro remoto (-R): la porta è occupata sul server
Con ssh -R 8080:localhost:80 user@server:
Warning: remote port forwarding failed for listen port 8080
La sessione si apre comunque e l'avviso è facile da perdere. La causa è sul server, e il suo log contiene la stessa riga bind. Guarda chi occupa la porta lì:
ssh user@server "ss -ltnp | grep ':8080'"
Il nome del processo compare solo per i tuoi processi, a meno che tu non lo esegua con sudo. A volte il proprietario è sshd o sshd-session: la connessione precedente dal tuo portatile o lo script del tuo tunnel è morto senza che il server se ne accorgesse, e la porta è ancora occupata. Trova e chiudi quella sessione sul server:
sudo ss -ltnp | grep ':8080'
sudo kill PID
Per far pulire questi casi al server da solo, aggiungi questo a sshd_config e ricarica il servizio. Scollega un client rimasto in silenzio per circa 90 secondi:
ClientAliveInterval 30
ClientAliveCountMax 3
Una porta occupata non è l'unico motivo di questo avviso. Se la porta è libera, il server potrebbe vietare l'inoltro, caso trattato in administratively prohibited.
Se non ti serve una porta fissa, lascia che sia il server a sceglierla. Il client stampa la porta ottenuta:
ssh -R 0:localhost:80 user@server
Allocated port 38587 for remote forward to localhost:80
Far fallire un tunnel in modo evidente
Per impostazione predefinita SSH ti fa entrare anche quando l'inoltro è fallito, quindi gli script vanno avanti e falliscono dopo per un motivo poco chiaro. Con questa opzione invece esce con stato 255:
ssh -o ExitOnForwardFailure=yes -N -L 127.0.0.1:8080:localhost:80 user@server
Con -R, il messaggio diventa:
Error: remote port forwarding failed for listen port 8080
Usa questa opzione in tutto ciò che è automatizzato, anche in autossh. Così un ciclo di tentativi vede il fallimento e riprova quando la porta è libera.
Anche le connessioni condivise occupano porte
Con ControlMaster, l'inoltro appartiene al processo master in background, quindi chiudere il terminale non libera la porta. Puoi parlare con il master usando lo stesso host e la stessa configurazione:
ssh -O check user@server
ssh -O cancel -L 127.0.0.1:8080:localhost:80 user@server
ssh -O exit user@server
Per saperne di più vedi SSH multiplexing.
Domande
Come trovo quale processo usa una porta?
Su macOS e Linux esegui lsof -nP -iTCP:8080 -sTCP:LISTEN, oppure ss -ltnp su Linux. Su Windows usa netstat -ano | findstr :8080. L'output mostra il programma e il suo PID.
Perché SSH entra anche se il port forwarding è fallito?
Di base un inoltro fallito è solo un avviso. Aggiungi -o ExitOnForwardFailure=yes e ssh esce con stato 255, così gli script se ne accorgono.
Come chiudo un tunnel SSH in background?
Trova il suo processo ssh con lsof ed esegui kill con il PID. Se appartiene a una connessione ControlMaster, usa ssh -O cancel o ssh -O exit con lo stesso host.