SSH 端口转发实现内网穿透

FreeGuideOnline 最新 2026-07-09

bash ssh -L [本地地址:]本地端口:目标地址:目标端口 用户名@跳板机IP


**示例:访问跳板机内网的数据库**  
假设跳板机所在内网有一台数据库服务器(IP `10.0.0.5`,端口 `3306`),你在本地执行:

```bash
ssh -L 3306:10.0.0.5:3306 user@123.123.123.123

之后,你在本地连接 127.0.0.1:3306,就等于直接连接到了内网数据库,流量经过加密且无需直接暴露数据库端口。

远程转发(-R)

将远程主机端口的流量,通过 SSH 隧道转发到本地(或本地可达)的指定端口。这正是实现内网穿透的核心模式

命令格式

ssh -R [远程地址:]远程端口:目标地址:目标端口 用户名@跳板机IP

示例:让公网能够访问内网的 Web 服务
你的内网树莓派上运行着一个 Web 服务(监听在 0.0.0.0:8080),希望从公网访问。在树莓派上执行:

ssh -R 8080:localhost:80 user@123.123.123.123

效果:跳板机上的 8080 端口会被监听,所有发往该端口的流量都会被安全隧道转发到树莓派的 80 端口。此时,在公网访问 http://123.123.123.123:8080 即可看到树莓派上的页面。

注意:默认情况下,远程转发只在跳板机的 127.0.0.1 上监听,外部无法直接访问。需要在跳板机的 /etc/ssh/sshd_config 中设置:

GatewayPorts yes

然后重启 SSH 服务(sudo systemctl restart sshd)。也可以在建立转发时绑定公网地址:ssh -R 0.0.0.0:8080:localhost:80 ...,但需要配置文件支持。

动态转发(-D)

在本地创建一个 SOCKS 代理,所有通过该代理的流量都会经过跳板机发出。常用于浏览器安全上网或访问内网所有服务

命令格式

ssh -D [本地地址:]本地端口 用户名@跳板机IP

示例ssh -D 1080 user@123.123.123.123,然后在浏览器的网络设置中配置 SOCKS5 代理 127.0.0.1:1080,即可使用跳板机的网络出口。


实战:稳定可靠的内网穿透方案

远程转发建立的连接一旦断开,内网服务就会失联。我们需要保证隧道长期稳定运行。

1. 使用 Autossh 自动重连

autossh 是一个用于监控并自动重启 SSH 连接的工具。

安装 Autossh(以 Debian/Ubuntu 为例):

sudo apt update && sudo apt install autossh

使用 Autossh 建立远程转发

autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR 8080:localhost:80 user@123.123.123.123

参数解释:

  • -M 0:关闭 autossh 自身的监控端口,完全依赖 ServerAliveInterval 进行检测
  • -o "ServerAliveInterval 30":每 30 秒发送一个心跳包,保持连接活跃
  • -o "ServerAliveCountMax 3":连续 3 次心跳无响应则判定连接断开,autossh 会自动重启 SSH
  • -N:不执行远程命令,只做端口转发
  • -R 8080:localhost:80:远程转发 8080 到本地 80

2. 配置免密登录

为了避免手动输入密码,建议在内网机器上生成密钥对,并将公钥添加到跳板机。

在内网机器上生成密钥(若已有可跳过):

ssh-keygen -t ed25519 -C "autossh-tunnel"

将公钥拷贝到跳板机

ssh-copy-id user@123.123.123.123

之后测试免密登录:ssh user@123.123.123.123,确保无需密码即可登录。

3. 创建 Systemd 服务实现开机自启

为了让隧道在系统启动时就自动运行,并保持后台长驻,推荐创建 systemd 服务。

创建服务文件 /etc/systemd/system/autossh-tunnel.service

[Unit]
Description=AutoSSH tunnel for exposing local service
After=network-online.target
Wants=network-online.target

[Service]
User=pi
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NR 8080:localhost:80 user@123.123.123.123
Restart=always
RestartSec=10
Environment="AUTOSSH_GATETIME=0"

[Install]
WantedBy=multi-user.target

注意将 User 修改为你实际运行该服务的用户名(如 pi),ExecStart 中填写正确的用户、地址和端口。

启动并启用服务

sudo systemctl daemon-reload
sudo systemctl start autossh-tunnel
sudo systemctl enable autossh-tunnel

检查服务状态:sudo systemctl status autossh-tunnel,确保没有报错。此时,内网服务已稳定暴露于公网。


安全加固与注意事项

  • 限制登录用户权限:在跳板机 /etc/ssh/sshd_config 中可以针对隧道用户限制命令:
    Match User tunnel-user
        PermitOpen any:8080
        ForceCommand echo "Only tunnel allowed"
    
  • 使用非标准端口(可选):将跳板机的 SSH 服务改为非 22 端口(例如 2222),降低被扫描的风险。
  • 防火墙配置:确保跳板机上的转发端口(如 8080)在防火墙中开放:sudo ufw allow 8080/tcp
  • 访问控制:如果只想让特定 IP 访问被穿透的服务,可以在跳板机上利用 iptables 或 Nginx 反向代理做限制,而不是将端口完全对公网开放。
  • 保持跳板机安全:及时更新系统,禁用密码登录只允许密钥验证,减少暴露面。

常见问题排查

1. 公网无法访问,但在跳板机本地 curl 127.0.0.1:8080 可以

  • 检查跳板机 /etc/ssh/sshd_config 是否设置了 GatewayPorts yes,并重启 sshd
  • 检查云服务商的安全组/防火墙规则是否放行了该端口

2. 连接经常断开

  • 引入 autossh 并设置合理的心跳间隔
  • 确认网络稳定,可在 ~/.ssh/config 中添加连接保活参数:
    Host jump
        HostName 123.123.123.123
        ServerAliveInterval 30
        ServerAliveCountMax 3
    

3. 端口被占用

  • 使用 netstat -tlnp | grep 8080 查看端口占用,更换其他未使用端口

4. 多级转发需求

  • 比如需要经过 A 跳板机访问 B 内网的服务,可结合本地转发 -L 构建链式隧道:
    ssh -L 8080:localhost:8080 user@A  # 在本地执行,将 A 的 8080 映射到本地
    # 在 A 上再建立远程转发:ssh -R 8080:localhost:80 user@B