SSH 端口转发实现内网穿透
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