SSH(Secure Shell)是 Linux/Unix 系统中最常用的远程登录工具,但它的功能远不止于此。SSH 的端口转发(Port Forwarding)功能,也被称为SSH 隧道(SSH Tunnel),是网络工程师和系统管理员的利器。通过 SSH 隧道,你可以安全地访问内网服务、绕过防火墙限制、保护不安全的网络连接。

本文将深入讲解 SSH 的三大隧道技术:

  • 本地端口转发-L):将本地端口映射到远程服务器
  • 远程端口转发-R):将远程端口映射到本地机器
  • 动态端口转发-D):创建 SOCKS 代理服务器

一、SSH 端口转发基础

什么是 SSH 隧道?

SSH 隧道是通过 SSH 协议建立的加密通道,用于在不安全的网络中安全地传输数据。它的核心原理是:

  1. 加密传输:所有通过 SSH 隧道的数据都经过加密,防止被窃听
  2. 端口映射:将本地或远程的端口映射到另一台机器上
  3. 绕过限制:可以穿透防火墙,访问受限网络资源

为什么使用 SSH 隧道?

场景解决方案
需要访问内网数据库使用 -L 建立本地转发
外网访问内网服务使用 -R 建立远程转发
需要安全的上网代理使用 -D 建立 SOCKS 代理
保护不安全的协议通过 SSH 隧道加密传输

二、本地端口转发(-L)

原理说明

本地端口转发(Local Port Forwarding)将本地机器的某个端口映射到远程服务器的某个端口。当你访问本地端口时,数据会通过 SSH 隧道转发到远程服务器。

[本地机器] --(SSH隧道)--> [SSH服务器] --(直连)--> [目标服务]
   :8080                    ssh.example.com          :80

基本语法

ssh -L [本地地址:]本地端口:目标地址:目标端口 用户名@SSH服务器

常用示例

示例 1:访问内网 Web 服务

假设你有一台公网服务器 jump.example.com,内网有一台 Web 服务器 192.168.1.100:80

# 将本地的 8080 端口映射到内网服务器的 80 端口
ssh -L 8080:192.168.1.100:80 user@jump.example.com

# 现在访问 http://localhost:8080 就相当于访问内网的 192.168.1.100:80

示例 2:连接内网 MySQL 数据库

# 将本地的 3307 端口映射到内网数据库的 3306 端口
ssh -L 3307:192.168.1.50:3306 user@jump.example.com

# 使用本地 MySQL 客户端连接
mysql -h 127.0.0.1 -P 3307 -u dbuser -p

示例 3:绑定到特定地址

# 只允许本机访问(默认)
ssh -L 127.0.0.1:8080:internal.server:80 user@jump.example.com

# 允许局域网内其他机器访问(需要 GatewayPorts 配置)
ssh -L 0.0.0.0:8080:internal.server:80 user@jump.example.com

后台运行

# 使用 -N 不执行远程命令,-f 后台运行
ssh -N -f -L 8080:internal.server:80 user@jump.example.com

# 使用 -T 禁用伪终端分配(更轻量)
ssh -NT -f -L 8080:internal.server:80 user@jump.example.com

三、远程端口转发(-R)

原理说明

远程端口转发(Remote Port Forwarding)与本地转发相反,它将远程服务器的某个端口映射到本地机器的某个端口。这允许从外网访问你本地或内网的服务。

[远程用户] --(SSH服务器)--> [SSH服务器] --(SSH隧道)--> [本地机器]
                                              :8080              :3000

基本语法

ssh -R [远程地址:]远程端口:本地地址:本地端口 用户名@SSH服务器

常用示例

示例 1:让外网访问本地开发服务器

你在本地开发了一个 Web 应用,运行在 localhost:3000,想让外网访问:

# 将远程服务器的 8080 端口映射到本地的 3000 端口
ssh -R 8080:localhost:3000 user@vps.example.com

# 外网用户访问 http://vps.example.com:8080 就能看到你的本地应用

示例 2:内网穿透(Reverse Tunnel)

你的机器在内网,没有公网 IP,需要让外网访问:

# 在具有公网 IP 的服务器上,确保 GatewayPorts 设置为 yes
# /etc/ssh/sshd_config: GatewayPorts yes

# 从内网机器执行
ssh -R 0.0.0.0:8080:localhost:80 user@public-server.com

# 现在外网可以通过 public-server.com:8080 访问内网的 Web 服务

示例 3:远程访问家庭 NAS

# 将远程服务器的 9000 端口映射到家庭 NAS 的 Web 界面
ssh -R 9000:192.168.1.10:5000 user@vps.example.com

# 在外网通过 vps.example.com:9000 访问家庭 NAS

持久化配置

远程转发默认只在 SSH 连接期间有效,可以使用 autossh 保持连接:

# 安装 autossh
sudo apt-get install autossh  # Debian/Ubuntu
sudo yum install autossh      # CentOS/RHEL

# 使用 autossh 保持隧道
autossh -M 0 -N -R 8080:localhost:3000 user@vps.example.com

四、动态端口转发(-D)

原理说明

动态端口转发(Dynamic Port Forwarding)在本地创建一个 SOCKS 代理服务器。与本地/远程转发不同,它不需要预先指定目标地址和端口,而是动态地将连接转发到任何目标。

[应用程序] --(SOCKS协议)--> [本地SOCKS代理] --(SSH隧道)--> [SSH服务器] --(直连)--> [任意目标]
     :1080                      ssh.example.com              internet

基本语法

ssh -D [本地地址:]本地端口 用户名@SSH服务器

常用示例

示例 1:创建 SOCKS5 代理

# 在本地 1080 端口创建 SOCKS 代理
ssh -D 1080 user@vps.example.com

# 或者后台运行
ssh -N -f -D 1080 user@vps.example.com

示例 2:浏览器使用 SOCKS 代理

Firefox 配置:

  1. 设置 → 网络设置 → 手动代理配置
  2. SOCKS 主机:127.0.0.1,端口:1080
  3. 选择 SOCKS v5
  4. 勾选 "使用 SOCKS v5 时代理 DNS 查询"

Chrome 命令行启动:

# Windows
chrome.exe --proxy-server="socks5://127.0.0.1:1080"

# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --proxy-server="socks5://127.0.0.1:1080"

# Linux
google-chrome --proxy-server="socks5://127.0.0.1:1080"

示例 3:命令行工具使用代理

# curl 使用 SOCKS 代理
curl --socks5 127.0.0.1:1080 http://example.com

# wget 使用 SOCKS 代理
wget --execute="http_proxy=socks5://127.0.0.1:1080" http://example.com

# git 使用 SOCKS 代理
git config --global http.proxy socks5://127.0.0.1:1080

示例 4:系统级代理(ProxyChains)

# 安装 proxychains
sudo apt-get install proxychains4

# 配置 /etc/proxychains4.conf
[ProxyList]
socks5 127.0.0.1 1080

# 使用 proxychains 运行任何程序
proxychains4 curl http://example.com
proxychains4 firefox

五、高级用法和技巧

1. 组合使用多种转发

# 同时使用本地和动态转发
ssh -L 8080:internal.server:80 -D 1080 user@jump.example.com

# 同时使用本地和远程转发
ssh -L 3307:db.internal:3306 -R 8080:localhost:3000 user@jump.example.com

2. 配置文件简化命令

~/.ssh/config 中添加配置:

Host tunnel
    HostName jump.example.com
    User myuser
    LocalForward 8080 internal.server:80
    LocalForward 3307 db.internal:3306
    DynamicForward 1080
    ServerAliveInterval 60
    ServerAliveCountMax 3

然后只需运行:

ssh tunnel

3. 使用 systemd 保持隧道持久

创建 /etc/systemd/system/ssh-tunnel.service

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Type=simple
User=youruser
ExecStart=/usr/bin/autossh -M 0 -N -L 8080:internal:80 -D 1080 user@jump.example.com
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

启用服务:

sudo systemctl enable ssh-tunnel
sudo systemctl start ssh-tunnel

4. 跳板机(Jump Host)配置

# 通过跳板机连接到目标服务器
ssh -J user@jump.example.com user@target.internal

# 在配置文件中
Host target
    HostName target.internal
    User myuser
    ProxyJump jump.example.com

六、安全注意事项

1. 限制监听地址

# 只监听本地(安全)
ssh -L 127.0.0.1:8080:target:80 user@server

# 监听所有接口(风险,需要 GatewayPorts)
ssh -L 0.0.0.0:8080:target:80 user@server

2. 使用密钥认证

避免使用密码,使用 SSH 密钥:

# 生成密钥对
ssh-keygen -t ed25519 -C "your_email@example.com"

# 复制公钥到服务器
ssh-copy-id user@server

3. 防火墙配置

# 限制 SSH 访问(仅允许特定 IP)
sudo ufw allow from 192.168.1.0/24 to any port 22

# 或者使用 fail2ban 防止暴力破解
sudo apt-get install fail2ban

4. 监控隧道连接

# 查看当前 SSH 连接
ss -tan | grep :22

# 查看端口转发状态
ssh -O check tunnel

# 检查 SOCKS 代理是否工作
curl --socks5 127.0.0.1:1080 http://ipinfo.io

七、常见问题排查

问题 1:连接被拒绝

# 检查 SSH 服务是否运行
sudo systemctl status sshd

# 检查端口是否被占用
sudo ss -tlnp | grep :8080

# 检查防火墙
sudo ufw status

问题 2:远程转发无法从外网访问

# 在 SSH 服务器上修改配置
sudo nano /etc/ssh/sshd_config

# 添加或修改
GatewayPorts yes

# 重启 SSH 服务
sudo systemctl restart sshd

问题 3:连接不稳定

# 使用 autossh 自动重连
autossh -M 20000 -N -L 8080:target:80 user@server

# 或者在 SSH 配置中添加
ServerAliveInterval 60
ServerAliveCountMax 3

八、总结对比

类型参数方向典型场景
本地转发-L本地 → 远程访问内网服务、数据库
远程转发-R远程 → 本地内网穿透、暴露本地服务
动态转发-D本地 SOCKS 代理安全上网、绕过防火墙

选择建议

  • 需要访问内网资源 → 使用 -L 本地转发
  • 需要让外网访问本地服务 → 使用 -R 远程转发
  • 需要安全的上网代理 → 使用 -D 动态转发
  • 需要同时满足多种需求 → 组合使用多个参数

SSH 隧道是网络工程师的瑞士军刀,掌握这三种端口转发技术,你就能在各种复杂的网络环境中游刃有余。记住:安全是第一位的,始终使用加密连接,合理配置访问权限,保护好你的密钥。