05 - Túnel SSH e Port Forwarding (Local, Remoto, Dinâmico)
video: https://youtu.be/... no frontmatter da nota.Túnel SSH e Port Forwarding (Local, Remoto, Dinâmico)
1. Definição
O Port Forwarding (redirecionamento de portas) é uma capacidade do SSH que permite encapsular tráfego TCP arbitrário dentro do canal cifrado SSH, criando um túnel entre dois pontos da rede. O SSH atua como um proxy encriptado, transportando o tráfere de uma porta local para uma porta remota (ou vice-versa), sem que aplicativos intermediários vejam o conteúdo. Há três modos principais:
- Local (
-L): expõe uma porta remota na máquina local do cliente. - Remoto (
-R): expõe uma porta local do cliente em um servidor remoto. - Dinâmico (
-D): cria um proxy SOCKS5 que encaminha tráfere arbitrário.
Definido na RFC 4254 (SSH Connection Protocol, seção 7), é um dos recursos mais poderosos e versáteis do SSH, frequentemente usado para acessar serviços internos, contornar firewalls restritivos, criar VPNs improvisadas e expor portas locais à internet pública sem roteamento.
Comparação Rápida
| Modo | Flag | Direção | Quem escuta | Quem conecta ao destino |
|---|---|---|---|---|
| Local | -L |
Cliente → Servidor | Cliente (porta local) | Servidor SSH |
| Remoto | -R |
Servidor → Cliente | Servidor SSH | Cliente (máquina local) |
| Dinâmico | -D |
Cliente → Servidor (SOCKS) | Cliente (porta SOCKS) | Servidor SSH |
2. Port Forwarding Local (-L)
O modo local expõe um serviço remoto em uma porta local do cliente. O SSH cria um socket TCP escutando na porta local; toda conexão recebida é encapsulada via SSH e entregue a um host:porta visível a partir do servidor SSH.
Sintaxe
ssh -L [bind_local:]porta_local:host_destino:porta_destino user@servidor_ssh
Caso Clássico: Acessar Banco de Dados Remoto
Cenário: PostgreSQL em db.interno.com:5432, acessível apenas internamente. Operador fora da rede acessa via jumphost bastion.empresa.com:
ssh -L 5432:db.interno.com:5432 user@bastion.empresa.com
# Depois: psql -h 127.0.0.1 -p 5432 -U admin dbname
[Operador] [bastion] [db.interno.com]
psql → 127.0.0.1:5432 ──SSH──> bastion ──TCP──> db:5432
(cifrado) (interno, em claro)
O tráfego entre operador e bastion é cifrado (SSH). O tráfego entre bastion e db.interno.com vai em claro na rede interna (rede confiável). Pode-se mapear múltiplas portas em uma conexão: ssh -L 5432:db:5432 -L 6379:redis:6379 bastion.
Bind Address Restritivo
Por padrão, -L escuta apenas em 127.0.0.1. Para expor à LAN do cliente, use 0.0.0.0 e habilite GatewayPorts yes no servidor:
ssh -L 0.0.0.0:5432:db.interno.com:5432 user@bastion.empresa.com
3. Port Forwarding Remoto (-R)
O modo remoto faz o inverso: expõe um serviço local do cliente em uma porta do servidor SSH. Útil para: expor serviço de casa para servidor na nuvem, acessar máquina atrás de NAT sem IP público, permitir que terceiros conectem a serviço local.
Sintaxe
ssh -R [bind_remoto:]porta_remota:host_local:porta_local user@servidor_ssh
Casos Práticos
# Expor servidor web local (porta 3000) para colega externo via nuvem
ssh -R 8080:localhost:3000 user@nuvem.exemplo.com
# Colega acessa: http://nuvem.exemplo.com:8080
# Reverse shell: máquina atrás de NAT abre túnel para suporte
ssh -R 2222:localhost:22 suporte@servidor-suporte.com -N
# Suporte conecta (no servidor-suporte): ssh usuario@localhost -p 2222
GatewayPorts no Servidor
Por padrão, -R escuta apenas em 127.0.0.1 do servidor. Para que terceiros acessem externamente, habilitar GatewayPorts clientspecified (ou yes) no sshd_config e o cliente usa 0.0.0.0:
ssh -R 0.0.0.0:8080:localhost:3000 user@nuvem.exemplo.com
4. Port Forwarding Dinâmico (-D) — Proxy SOCKS
O modo dinâmico cria um proxy SOCKS4/SOCKS5 na porta local do cliente. O SSH aceita conexões SOCKS, encapsula cada uma via canal SSH, e no servidor SSH abre conexão TCP ao destino solicitado dinamicamente pelo cliente SOCKS. Permite acessar múltiplos destinos sem configurar túneis individuais.
Sintaxe
ssh -D [bind_local:]porta_local user@servidor_ssh
Uso Prático
ssh -D 1080 -C -N user@servidor_ssh.com # Proxy SOCKS5 na porta 1080
curl --socks5-hostname 127.0.0.1:1080 http://site-interno.empresa.com
export ALL_PROXY=socks5h://127.0.0.1:1080 # Para aplicativos Python/curl
# Firefox: about:config → network.proxy.socks_remote_dns = true
Vantagens e Limitações
- Não precisa saber de antemão quais destinos acessar; DNS é resolvido no servidor (evita vazamento).
- Funciona como VPN "leve" para aplicações que suportam SOCKS.
- Limitação: apenas TCP (não UDP). Aplicações sem suporte nativo a SOCKS usam wrapper
proxychains(proxychains nmap -sT host-interno).
5. Flags Complementares
| Flag | Função |
|---|---|
-N |
Não executar shell remoto (apenas túnel) |
-f |
Ir para background após autenticação |
-T |
Desabilitar TTY (útil com -N) |
-C |
Compressão zlib (bom para texto; ruim para já comprimido) |
-g |
Permitir conexões de hosts remotos ao bind local |
-o ExitOnForwardFailure=yes |
Abortar se túnel falhar |
-o ServerAliveInterval=30 |
Keepalive a cada 30s |
-o ServerAliveCountMax=3 |
3 falhas = desconexão |
-o TCPKeepAlive=yes |
Manter TCP vivo |
Exemplo canônico:
ssh -fN -L 5432:db:5432 -o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 user@bastion
6. ProxyJump (Jump Host) — -J
Introduzido no OpenSSH 7.3 (2016), -J simplifica conexões via bastion. Em vez de túnel manual, o SSH conecta-se ao destino através do bastion automaticamente, sem expor portas locais.
ssh -J user@bastion.com user@host-interno.com
# Em ~/.ssh/config
Host interno
HostName 10.0.0.50
ProxyJump bastion.com
User admin
Vantagens sobre -L manual: não cria túnel persistente, não expõe portas, usa SSH nativo para o salto, mantém agente local (mais seguro que ForwardAgent).
7. Manter Túnel Persistente (autossh)
Túneis SSH quebram com flutuações de rede. autossh monitora e reconecta automaticamente:
autossh -M 0 -fN -L 5432:db:5432 \
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3" user@bastion
-M 0 desabilita monitoramento próprio do autossh (porta extra) e usa apenas ServerAlive*. Em produção, criar unit systemd com Restart=always e ExecStart=/usr/bin/autossh -M 0 -N -L 5432:db:5432 -o ServerAliveInterval=30 user@bastion.
8. Diagnóstico e Problemas Comuns
| Sintoma | Causa | Solução |
|---|---|---|
bind: Address already in use |
Porta local ocupada | Trocar porta ou matar processo |
remote port forwarding failed |
Porta remota em uso ou GatewayPorts=no |
Verificar GatewayPorts no servidor |
| Conexão cai após inatividade | NAT/firewall dropa conexão idle | ServerAliveInterval=30 |
| DNS vaza no modo dinâmico | Cliente resolve DNS localmente | socks5h:// ou proxy_dns no Firefox |
Permission denied (port forwarding) |
AllowTcpForwarding no no servidor |
Habilitar no sshd_config |
ss -tnp | grep ssh # Inspecionar túneis ativos
lsof -i -n -P | grep ssh # Portas escutadas pelo cliente ssh
9. Boas Práticas e Segurança
| Prática | Recomendação |
|---|---|
AllowTcpForwarding |
Habilitar apenas para usuários que precisam |
PermitTunnel |
Manter no a menos que VPN sobre SSH seja necessária |
| Bind local | Sempre 127.0.0.1, nunca 0.0.0.0 em estações compartilhadas |
GatewayPorts |
clientspecified para controle fino |
| Chaves para túnel | Chave dedicada com comando forçado (command="...") |
| Senha | Desabilitar; só chave SSH |
| Logs | LogLevel VERBOSE para auditoria |
Em ~/.ssh/authorized_keys, restringir chave apenas a túnel remoto na porta 8080 sem shell:
command="echo tunnel-only",no-pty,no-X11-forwarding,permitlisten=8080 ssh-ed25519 AAAA...
10. Conclusão
O Port Forwarding é um dos recursos mais elegantes do SSH: com um único comando, transforma uma conexão cifrada em túnel TCP reversível, proxy SOCKS ou jump host. Dominar -L, -R e -D é essencial para engenheiros que precisam acessar infraestrutura interna, contornar restrições de firewall ou criar VPNs improvisadas. Para cenários produtivos, combinar com autossh (resiliência) e ProxyJump (simplicidade) é o padrão moderno. Em servidores expostos, restringir AllowTcpForwarding e usar chaves com comando forçado reduz o risco de abuso.