Skip to content

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
ProxyCommandssh -o ProxyCommand="ssh -W %h:%p jump" target
TUN VPNssh -w 0:1 user@host
sshuttlesshuttle -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-host

ProxyCommand

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/ProxyCommandhttps://man.openbsd.org/ssh_config
sshuttle 项目https://github.com/sshuttle/sshuttle