
SSH 端口转发:三种隧道的完整指南
2026/6/22大约 10 分钟

SSH 不仅仅是连接服务器的工具。
它的隧道转发功能更强大。
本地转发、远程转发、动态转发,
三种隧道,三种场景。
什么是 SSH 隧道?
隧道是一种把一种网络协议封装进另外一种网络协议进行传输的技术。
┌─────────────────────────────────────────────────────────┐
│ SSH 隧道的工作原理 │
├─────────────────────────────────────────────────────────┤
│ │
│ 网络通讯流程: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 本地应用 │ │
│ │ ↓ │ │
│ │ 本地端口(被 SSH 绑定) │ │
│ │ ↓ │ │
│ │ SSH 加密通道 │ │
│ │ ↓ │ │
│ │ 远端系统 │ │
│ │ ↓ │ │
│ │ 目标服务 │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ · 所有网络通讯都是加密的 │
│ · 又被称作端口转发 │
│ │ · 因为通常会绑定一个本地端口 │ │
│ │ · 所有发向这个端口的数据包都被加密传输 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘三种端口转发
基于 SSH 的端口转发可分为三类:
┌─────────────────────────────────────────────────────────┐
│ 三种 SSH 端口转发 │
├─────────────────────────────────────────────────────────┤
│ │
│ 本地端口转发(-L) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -g -L listen_port:DST_Host:DST_port│ │
│ │ user@Tunnel_Host │ │
│ │ │ │
│ │ 用途:将本地端口转发到远端指定机器的指定端口 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 远程端口转发(-R) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -g -R listen_port:DST_Host:DST_port│ │
│ │ user@Tunnel_Host │ │
│ │ │ │
│ │ 用途:将远程端口转发到本地端指定机器的指定端口 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 动态端口转发(-D) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -g -D listen_port user@Tunnel_Host │ │
│ │ │ │
│ │ 用途:创建 SOCKS 代理服务器 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘参数详解
三个命令共用的参数:
┌─────────────────────────────────────────────────────────┐
│ SSH 参数详解 │
├─────────────────────────────────────────────────────────┤
│ │
│ -C │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Enable compression │ │
│ │ 压缩数据传输 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ -f │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Fork into background after authentication │ │
│ │ 认证后在后台运行 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ -N │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Do not execute a shell or command │ │
│ │ 不执行任何命令,仅做端口转发 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ -g │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Allow remote hosts to connect to forwarded ports │ │
│ │ 允许远程主机连接到转发的端口 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘本地端口转发(-L)
命令格式:
ssh -C -f -N -g -L listen_port:DST_Host:DST_port user@Tunnel_Host参数解释:
-L port:remote_host:remote_port
作用:
将本地机(客户机)的某个端口转发到远端指定机器的指定端口
工作原理:
1. 本地机器上分配了一个 socket 侦听 port 端口
2. 一旦这个端口上有了连接,该连接就经过安全通道转发出去
3. 同时远程主机(SSH Server)将和 remote_host 的 remote_port 端口建立连接
注意:
只有 root 才能转发特权端口(< 1024)
IPv6 地址用另一种格式说明:port/host/hostport场景示例
场景:访问内网数据库
┌─────────────────────────────────────────────────────────┐
│ 场景:访问内网 MySQL 数据库 │
├─────────────────────────────────────────────────────────┤
│ │
│ 网络拓扑: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 你的电脑(本地) │ │
│ │ 无法直接访问内网 │ │
│ │ │ │
│ │ 跳板机(Tunnel_Host) │ │
│ │ 可以访问内网 │ │
│ │ │ │
│ │ 内网 MySQL(DST_Host:DST_port) │ │
│ │ 192.168.1.100:3306 │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 命令: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -g -L 3307:192.168.1.100:3306 │ │
│ │ user@jump.example.com │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 使用: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ mysql -h 127.0.0.1 -P 3307 -u dbuser -p │ │
│ │ │ │
│ │ 实际连接的是: │ │
│ │ 192.168.1.100:3306(通过 SSH 隧道) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘流程说明:
- 本地监听: SSH 在本地 3307 端口建立监听
- 数据加密: 连接到 3307 的数据被 SSH 加密
- 隧道传输: 加密后的数据通过 SSH 隧道传输到跳板机
- 转发目标: 跳板机将数据转发到内网 MySQL 192.168.1.100:3306
- 反向返回: MySQL 的响应数据沿原路返回
更多场景
访问内网 Web 服务:
# 将本地 8080 端口转发到内网 Web 服务
ssh -C -f -N -L 8080:192.168.1.200:80 user@jump.example.com
# 访问
curl http://127.0.0.1:8080
# 实际访问的是 192.168.1.200:80访问内网 Redis:
# 将本地 6380 端口转发到内网 Redis
ssh -C -f -N -L 6380:192.168.1.150:6379 user@jump.example.com
# 连接
redis-cli -h 127.0.0.1 -p 6380
# 实际连接的是 192.168.1.150:6379远程端口转发(-R)
命令格式:
ssh -C -f -N -g -R listen_port:DST_Host:DST_port user@Tunnel_Host参数解释:
-R port:host:hostport
作用:
将远程主机(服务器)的某个端口转发到本地端指定机器的指定端口
工作原理:
1. 远程主机上分配了一个 socket 侦听 port 端口
2. 一旦这个端口上有了连接,该连接就经过安全通道转发出去
3. 同时和本地主机 host 的 hostport 端口建立连接
注意:
只有用 root 登录远程主机才能转发特权端口
IPv6 地址用另一种格式说明:port/host/hostport场景示例
场景:让外网访问内网服务
┌─────────────────────────────────────────────────────────┐
│ 场景:让外网访问内网 Web 服务 │
├─────────────────────────────────────────────────────────┤
│ │
│ 网络拓扑: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 外网用户 │ │
│ │ 无法直接访问内网 │ │
│ │ │ │
│ │ 云服务器(Tunnel_Host) │ │
│ │ 有公网 IP │ │
│ │ │ │
│ │ 你的电脑(DST_Host:DST_port) │ │
│ │ 本地 Web 服务 localhost:3000 │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 命令(在本地执行): │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -g -R 8080:localhost:3000 │ │
│ │ user@cloud.example.com │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 外网访问: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ curl http://cloud.example.com:8080 │ │
│ │ │ │
│ │ 实际访问的是: │ │
│ │ 你的本地 localhost:3000(通过 SSH 隧道) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘流程说明:
- 远程监听: SSH 在云服务器的 8080 端口建立监听
- 数据接收: 外网访问 cloud.example.com:8080
- 隧道传输: 数据通过 SSH 隧道传输到你的本地
- 转发目标: 本地将数据转发到 localhost:3000
- 反向返回: Web 服务的响应沿原路返回
更多场景
本地调试分享:
# 将云服务器 8080 端口转发到本地 3000 端口
ssh -C -f -N -R 8080:localhost:3000 user@cloud.example.com
# 外网访问
http://cloud.example.com:8080
# 实际访问的是你的本地 localhost:3000内网穿透:
# 将云服务器 2222 端口转发到内网 SSH 22
ssh -C -f -N -R 2222:192.168.1.50:22 user@cloud.example.com
# 从云服务器 SSH 到内网机器
ssh -p 2222 localhost
# 实际连接的是 192.168.1.50:22动态端口转发(-D)
命令格式:
ssh -C -f -N -g -D listen_port user@Tunnel_Host参数解释:
-D port
作用:
指定一个本地机器「动态的」应用程序端口转发
工作原理:
1. 本地机器上分配了一个 socket 侦听 port 端口
2. 一旦这个端口上有了连接,该连接就经过安全通道转发出去
3. 根据应用程序的协议判断远程主机将和哪里连接
支持协议:
目前支持 SOCKS4/5 协议,ssh 将充当 SOCKS4/5 服务器
注意:
只有 root 才能转发特权端口
可以在配置文件中指定动态端口的转发场景示例
场景:全局代理
┌─────────────────────────────────────────────────────────┐
│ 场景:创建 SOCKS 代理 │
├─────────────────────────────────────────────────────────┤
│ │
│ 网络拓扑: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 你的电脑(本地) │ │
│ │ 需要访问外网 │ │
│ │ │ │
│ │ 跳板机(Tunnel_Host) │ │
│ │ 可以访问外网 │ │
│ │ │ │
│ │ 目标网站 │ │
│ │ www.example.com │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 命令: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ssh -C -f -N -D 1080 user@jump.example.com │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 浏览器配置: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ SOCKS 代理:127.0.0.1:1080 │ │
│ │ SOCKS 版本:SOCKS5 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 工作流程: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 1. 浏览器连接到本地 1080 端口 │ │
│ │ 2. SSH 充当 SOCKS5 服务器 │ │
│ │ 3. 数据通过 SSH 隧道传输到跳板机 │ │
│ │ 4. 跳板机访问目标网站 │ │
│ │ 5. 响应数据沿原路返回 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘安全范围说明
⚠️ 重要:SSH 隧道的安全范围
┌─────────────────────────────────────────────────────────┐
│ SSH 隧道的安全范围 │
├─────────────────────────────────────────────────────────┤
│ │
│ 加密范围: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ✓ 浏览器端(SSH Client)到 SSH Server 端 │ │
│ │ ✗ SSH Server 端到目标网站的连接 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 含义: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ · SSH 只保护从你的电脑到跳板机的连接 │ │
│ │ · 不包含从跳板机到目标网站的连接 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 结论: │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 如果后半截连接的安全不能得到充分保证: │ │
│ │ · 这种方式仍不是合适的解决方案 │ │
│ │ │ │
│ │ 建议: │ │
│ │ · 跳板机到目标网站也使用加密连接 │ │
│ │ · 或使用 VPN 等更安全的方案 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘三种转发对比
┌─────────────────────────────────────────────────────────┐
│ 三种 SSH 端口转发对比 │
├─────────────────────────────────────────────────────────┤
│ │
│ 本地端口转发(-L) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 命令:-L local_port:remote_host:remote_port │ │
│ │ │ │
│ │ 方向:本地 → 远端 │ │
│ │ │ │
│ │ 用途: │ │
│ │ · 访问内网服务 │ │
│ │ · 通过跳板机访问无法直接访问的服务 │ │
│ │ │ │
│ │ 示例: │ │
│ │ ssh -L 3307:192.168.1.100:3306 user@jump │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 远程端口转发(-R) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 命令:-R remote_port:local_host:local_port │ │
│ │ │ │
│ │ 方向:远端 → 本地 │ │
│ │ │ │
│ │ 用途: │ │
│ │ · 内网穿透 │ │
│ │ · 让外网访问内网服务 │ │
│ │ │ │
│ │ 示例: │ │
│ │ ssh -R 8080:localhost:3000 user@cloud │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 动态端口转发(-D) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 命令:-D local_port │ │
│ │ │ │
│ │ 方向:本地 → 动态目标 │ │
│ │ │ │
│ │ 用途: │ │
│ │ · 创建 SOCKS 代理 │ │
│ │ · 全局代理 │ │
│ │ │ │
│ │ 示例: │ │
│ │ ssh -D 1080 user@jump │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘实战技巧
技巧 1:持久化隧道
使用 autossh 保持隧道稳定:
# 安装 autossh
sudo apt-get install autossh
# 使用 autossh 替代 ssh
autossh -C -f -N -L 3307:192.168.1.100:3306 user@jump.example.com
# 或指定监控端口和重试参数"
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-C -f -N -L 3307:192.168.1.100:3306 user@jump.example.com技巧 2:配置文件管理
在 ~/.ssh/config 中配置隧道:
# ~/.ssh/config
Host tunnel-mysql
HostName jump.example.com
User user
LocalForward 3307 192.168.1.100:3306
Compression yes
ServerAliveInterval 30
Host tunnel-web
HostName jump.example.com
User user
LocalForward 8080 192.168.1.200:80
Compression yes使用:
# 启动隧道
ssh -f -N tunnel-mysql
# 停止隧道
pkill -f tunnel-mysql技巧 3:多隧道管理
使用脚本管理多个隧道:
#!/bin/bash
# tunnels-manager.sh
case "$1" in
start)
echo "Starting SSH tunnels..."
ssh -C -f -N -L 3307:192.168.1.100:3306 user@jump
ssh -C -f -N -L 8080:192.168.1.200:80 user@jump
ssh -C -f -N -L 6380:192.168.1.150:6379 user@jump
echo "Tunnels started"
;;
stop)
echo "Stopping SSH tunnels..."
pkill -f "3307:192.168.1.100:3306"
pkill -f "8080:192.168.1.200:80"
pkill -f "6380:192.168.1.150:6379"
echo "Tunnels stopped"
;;
status)
echo "Active tunnels:"
ps aux | grep ssh | grep -v grep
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
;;
esac技巧 4:检查隧道状态
检查端口是否在监听:
# 检查本地端口是否在监听
netstat -tuln | grep 3307
# 或使用 lsof
lsof -i :3307
# 检查 SSH 隧道进程
ps aux | grep "ssh.*3307"技巧 5:测试隧道连接
测试隧道是否工作:
# 测试本地端口转发
nc -zv 127.0.0.1 3307
# 测试连接
telnet 127.0.0.1 3307
# 测试实际服务
mysql -h 127.0.0.1 -P 3307 -u dbuser -p -e "SELECT 1"常见问题
问题 1:隧道连接断开
现象: 隧道在使用一段时间后自动断开。
解决:
# 使用 ServerAliveInterval 保持连接
ssh -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-C -f -N -L 3307:192.168.1.100:3306 user@jump
# 或使用 autossh
autossh -M 0 -o "ServerAliveInterval 30" \
-C -f -N -L 3307:192.168.1.100:3306 user@jump问题 2:特权端口错误
现象:
Privileged ports can only be forwarded by root.解决:
# 使用 sudo
sudo ssh -C -f -N -L 80:192.168.1.200:80 user@jump
# 或使用非特权端口(推荐)
ssh -C -f -N -L 8080:192.168.1.200:80 user@jump问题 3:远程主机无法连接
现象: 使用 -g 参数后,远程主机仍无法连接到转发的端口。
解决:
# 1. 确保使用了 -g 参数
ssh -C -f -N -g -L 3307:192.168.1.100:3306 user@jump
# 2. 检查防火墙设置
# 3. 检查 GatewayPorts 设置(服务器端)
# 在 /etc/ssh/sshd_config 中:
GatewayPorts yes
# 重启 SSH 服务
sudo systemctl restart sshd最佳实践
1. 使用配置文件
管理多个隧道:
# ~/.ssh/config
Host tunnel-prod
HostName jump.prod.com
User deploy
LocalForward 3307 mysql.prod.internal:3306
LocalForward 6380 redis.prod.internal:6379
LocalForward 8080 web.prod.internal:80
Compression yes
ServerAliveInterval 302. 保持隧道活跃
防止连接超时:
# 在 ~/.ssh/config 中添加
Host *
ServerAliveInterval 30
ServerAliveCountMax 33. 使用密钥认证
避免每次输入密码:
# 生成 SSH 密钥
ssh-keygen -t rsa -b 4096
# 复制公钥到跳板机
ssh-copy-id user@jump.example.com
# 现在可以无密码建立隧道
ssh -C -f -N -L 3307:192.168.1.100:3306 user@jump4. 日志和监控
监控隧道状态:
#!/bin/bash
# monitor-tunnels.sh
tunnels=(
"3307:192.168.1.100:3306"
"8080:192.168.1.200:80"
"6380:192.168.1.150:6379"
)
for tunnel in "${tunnels[@]}"; do
port=$(echo $tunnel | cut -d: -f1)
if nc -z 127.0.0.1 $port 2>/dev/null; then
echo "✓ Tunnel on port $port is active"
else
echo "✗ Tunnel on port $port is down"
fi
done安全建议
1. 限制转发权限
在服务器端限制端口转发:
# /etc/ssh/sshd_config
# 允许端口转发
AllowTcpForwarding yes
# 限制特定用户
PermitOpen localhost:3306 localhost:6379
# 或禁用远程转发(只允许本地转发)
PermitRemoteOpen none2. 使用独立账户
为隧道创建专用账户:
# 创建专用账户
sudo useradd -m -s /bin/bash tunnel-user
# 限制权限
sudo usermod -aG nogroup tunnel-user
# 只允许端口转发,禁止 shell
# 在 /etc/ssh/sshd_config 中:
Match User tunnel-user
ForceCommand /bin/false
AllowTcpForwarding yes3. 监控和审计
记录隧道使用情况:
# /etc/ssh/sshd_config
# 启用详细日志
LogLevel VERBOSE
# 记录转发连接
# 在 /etc/rsyslog.d/ssh.conf 中
:programname, isequal, "sshd" /var/log/ssh-forward.log
& stop收尾金句
- SSH 不仅仅是连接服务器的工具,它的隧道转发功能更强大
- 本地转发(-L):访问内网服务
- 远程转发(-R):让外网访问内网
- 动态转发(-D):创建 SOCKS 代理
- SSH 隧道只保护从 SSH Client 到 SSH Server 的连接
- 如果后半截连接的安全不能得到充分保证,这种方式仍不是合适的解决方案
- 使用 autossh 保持隧道稳定
- 在配置文件中管理多个隧道
- 使用密钥认证避免每次输入密码
SSH 端口转发
三种隧道的完整指南
