SSH 端口转发与隧道
SSH 隧道是最强大的网络瑞士军刀之一——无需 VPN 即可安全穿越防火墙、访问内网服务、搭建 SOCKS 代理。
速查卡片
| 项目 | 语法 |
|---|---|
| 本地转发 | ssh -L [bind:]port:host:hostport user@host |
| 远程转发 | ssh -R [bind:]port:host:hostport user@host |
| 动态转发(SOCKS) | ssh -D [bind:]port user@host |
| 跳板机 | ssh -J user@jump_host user@target |
| ProxyCommand | ssh -o ProxyCommand="ssh -W %h:%p jump" target |
| TUN VPN | ssh -w 0:1 user@host |
| sshuttle | sshuttle -r user@host 0/0 |
概述
SSH 端口转发(Port Forwarding),也称 SSH 隧道(SSH Tunneling),允许通过加密的 SSH 连接转发任意 TCP 流量。三种转发模式覆盖了几乎所有网络穿透场景:
| 模式 | 方向 | 典型用途 |
|---|---|---|
| 本地转发(-L) | 本地 → SSH 服务器 → 目标 | 访问远程内网服务(数据库、Web) |
| 远程转发(-R) | SSH 服务器 → 本地 → 目标 | 将本地服务暴露给远程(内网穿透) |
| 动态转发(-D) | 本地 → SSH 服务器 → 任意目标 | SOCKS 代理(浏览器安全上网) |
本地端口转发(-L)
将本地端口的流量通过 SSH 转发到远程可达的目标。
text
语法:ssh -L [bind_address:]local_port:target_host:target_port user@ssh_server
本地监听 SSH加密隧道 远端可达的目标
localhost:8080 ──→ ═══ SSH Server ═══ → internal_db:3306示例一:访问远程内网的数据库
bash
# 将远程内网的 MySQL 映射到本地 3306
ssh -L 3306:internal-db-server:3306 user@gateway
# 然后本地连接
mysql -h 127.0.0.1 -P 3306 -u root -p示例二:仅绑定本地回环(安全,默认行为)
bash
# 默认只绑 127.0.0.1
ssh -L 8080:localhost:80 user@host
# 显式指定 bind 地址
ssh -L 127.0.0.1:8080:localhost:80 user@host示例三:绑定所有接口(允许局域网访问)
bash
# 让局域网内其他机器也能使用这个隧道
ssh -L 0.0.0.0:8080:localhost:80 user@host
# 或简写
ssh -L \*:8080:localhost:80 user@host示例四:Unix 套接字转发
bash
# 本地 Unix 套接字 → 远程 TCP 端口
ssh -L /tmp/local.sock:localhost:5432 user@host
# 本地 TCP 端口 → 远程 Unix 套接字
ssh -L 8080:/var/run/app.sock user@host示例五:多端口转发
bash
# 同时转发多个端口
ssh -L 8080:localhost:80 \
-L 3306:localhost:3306 \
-L 6379:localhost:6379 \
user@host远程端口转发(-R)
将远程 SSH 服务器上的端口流量转发回本地可达的目标。是本地转发方向的反向。
text
语法:ssh -R [bind_address:]remote_port:target_host:target_port user@ssh_server
远程监听 SSH加密隧道 本地可达的目标
remote:9090 ──→ ═══ SSH Client ═══ → local_app:3000示例六:内网穿透——将本地 Web 服务暴露给公网服务器
bash
# 在内网机器上执行:将本地 3000 端口映射到公网服务器的 80 端口
ssh -R 8080:localhost:3000 user@public-server默认情况下,远程端口只绑定 127.0.0.1。要让外部请求能访问,需修改服务端 sshd_config:
text
GatewayPorts yes或在命令中指定绑定地址:
bash
ssh -R 0.0.0.0:8080:localhost:3000 user@public-server示例七:动态分配远程端口
bash
# 使用端口 0 让服务器自动分配端口
ssh -R 0:localhost:3000 user@host
# 输出:Allocated port 12345 for remote forward示例八:SOCKS 反向代理
bash
# 远程端口转发做 SOCKS 代理(不指定目标)
ssh -R 1080 user@host
# 远程机器可通过 socks://localhost:1080 访问本地网络动态端口转发(-D)
在本地创建一个 SOCKS5 代理,所有通过该代理的流量都经 SSH 隧道转发。
text
语法:ssh -D [bind_address:]local_port user@ssh_server
本地SOCKS代理 SSH加密隧道
localhost:1080 ──→ ═══ SSH Server ═══ → 互联网(任意目标)示例九:浏览器安全上网
bash
# 启动 SOCKS 代理
ssh -D 1080 user@remote-server
# 浏览器设置 SOCKS5 代理:127.0.0.1:1080
# Firefox: 设置 → 网络设置 → SOCKS Host
# Chrome: chrome --proxy-server="socks5://127.0.0.1:1080"示例十:命令行工具通过 SOCKS 代理
bash
# curl 通过 SOCKS5
curl --socks5 127.0.0.1:1080 https://example.com
# git 通过 SOCKS5
git clone --config http.proxy=socks5://127.0.0.1:1080 https://...
# 绑定所有接口让局域网共享
ssh -D 0.0.0.0:1080 user@host跳板机(ProxyJump / -J)
直接通过中间跳板机连接到最终目标,免去手动配置端口转发的麻烦。
bash
# 单跳板
ssh -J user@jump-host user@target-host
# 多跳板(逗号分隔)
ssh -J user@jump1,user@jump2 user@target
# 支持用户和端口
ssh -J admin@jump:2222 user@target
# 等同于在 ~/.ssh/config 中配置:
# Host target
# HostName target-host
# User user
# ProxyJump user@jump-hostProxyCommand
ProxyCommand 是更灵活的代理方式,可以搭配 nc(netcat)、socat、HTTP 代理等。
通过 nc 代理
bash
# 通过跳板机连接(等效于 -J)
ssh -o ProxyCommand="ssh -W %h:%p jump-host" target
# 通过 HTTP 代理连接
ssh -o ProxyCommand="nc -X connect -x proxy:8080 %h %p" user@host
# 通过 SOCKS5 代理连接
ssh -o ProxyCommand="nc -X 5 -x proxy:1080 %h %p" user@host~/.ssh/config 配置
# 不同场景的 ProxyCommand
# 通过跳板机
Host internal-*
ProxyCommand ssh gateway -W %h:%p
# 通过 HTTP 代理
Host *.corp.com
ProxyCommand nc -X connect -x proxy.corp.com:8080 %h %p
# 通过 socat
Host target
ProxyCommand socat - PROXY:proxy.corp.com:%h:%p,proxyport=8080%h = 目标主机名,%p = 目标端口(
ssh自动替换这些占位符)
TUN 设备转发(-w)—— 二层/三层 VPN
直接将两个网络的 TUN 设备桥接,实现真正的 VPN。
bash
# 客户端:创建 TUN 隧道
ssh -f -w 0:1 user@gateway true
ifconfig tun0 10.0.1.1 10.0.1.2 netmask 255.255.255.252
route add 10.0.99.0/24 10.0.1.2
# 服务端:配置对应的 TUN 接口
ifconfig tun1 10.0.1.2 10.0.1.1 netmask 255.255.255.252
route add 10.0.50.0/24 10.0.1.1需要服务端 sshd_config 启用:
text
PermitTunnel yes # 允许 TUN 转发sshuttle —— 简易 VPN
sshuttle 是第三方工具(pip install sshuttle),通过 SSH 实现类似 VPN 的效果,无需服务端额外配置。
bash
# 代理所有流量(全局 VPN)
sshuttle -r user@host 0/0
# 代理特定子网
sshuttle -r user@host 10.0.0.0/8 192.168.0.0/16
# 排除特定网段
sshuttle -r user@host -x 10.0.0.0/8 0/0
# DNS 代理
sshuttle --dns -r user@host 0/0
# 指定 SSH 密钥
sshuttle -r user@host --ssh-cmd 'ssh -i ~/.ssh/id_ed25519' 0/0常见实战场景
场景一:通过跳板机访问内网数据库
bash
# 两步法
# 终端1: 建立跳板到内网的隧道
ssh -L 3306:db-internal:3306 user@jump
# 终端2: 连接数据库
mysql -h 127.0.0.1 -P 3306 -u root -p
# 一步法(使用 -J + -L)
ssh -L 3306:db-internal:3306 -J user@jump user@db-internal -N场景二:让同事访问你本机的开发服务
bash
# 在你的机器上运行(确保远程服务器 GatewayPorts yes)
ssh -R 0.0.0.0:8080:localhost:3000 user@public-server
# 同事访问:http://public-server:8080场景三:通过 SSH 代理安全上网(咖啡厅/WiFi)
bash
# 在本地建立 SOCKS 代理
ssh -D 1080 -N user@home-server
# 浏览器配置 SOCKS5 代理 127.0.0.1:1080
# 所有流量加密通过家庭服务器转发注意事项
- 特权端口(<1024):只有 root 可绑定转发,普通用户需使用 1024 以上的端口
- GatewayPorts:远程端口转发默认只绑定
127.0.0.1,外部访问需服务端配置GatewayPorts yes - ExitOnForwardFailure:设置后转发失败会导致整个连接终止,生产环境建议开启
- Agent Forwarding(-A)风险:在跳板场景优先使用
-J/ProxyJump替代-A - TCP 保活:长时间闲置的隧道可能被防火墙切断,配置
ServerAliveInterval 60维持心跳 - SOCKS 代理只支持 TCP,UDP 流量不能通过 SSH 隧道转发
来源
| 来源 | 链接 |
|---|---|
| OpenSSH ssh(1) 手册 | https://man.openbsd.org/ssh |
| ssh_config(5) ProxyJump/ProxyCommand | https://man.openbsd.org/ssh_config |
| sshuttle 项目 | https://github.com/sshuttle/sshuttle |
