SSH 端口转发与内网穿透技术
1. 概述 (Overview)
SSH 端口转发(Port Forwarding),俗称“SSH 隧道”,是 OpenSSH 协议提供的一种将 TCP 流量封装在加密的 SSH 连接中传输的技术。
核心价值:
- 安全性:通过加密通道传输明文流量(如 HTTP, Telnet, 早期 MySQL 协议)。
- 穿透性:能够绕过防火墙限制,访问仅监听在内网(localhost/127.0.0.1)的服务。
- 双向性:既可以将远程服务映射到本地(Local),也可以将本地服务暴露给远程(Remote)。
2. 本地端口转发 (Local Port Forwarding)
方向:将远程服务器的端口“拉”到本地。
适用场景:访问生产环境数据库、访问内网运维后台(如 XXL-JOB, RabbitMQ Management)、绕过防火墙限制。
2.1 标准命令语法
Bash
ssh -N -L [本地监听IP:]本地端口:目标地址:目标端口 用户名@跳板机IP-N: 不执行远程命令(仅建立连接,不打开 Shell 终端)。-L: 标识 Local Forwarding。本地监听IP: 可选。默认为127.0.0.1。若需局域网内其他人访问你的电脑,需指定为0.0.0.0。目标地址: 相对于跳板机而言的地址。通常是127.0.0.1(指跳板机自己)或跳板机所在内网的其它 IP。
2.2 典型实战案例
场景 A:连接生产环境 MySQL
生产数据库 (3306) 仅允许 localhost 访问,你需要用本地 IDEA/Navicat 连接。
Bash
# 将远程 3306 映射到本地 13306
ssh -N -L 13306:127.0.0.1:3306 root@192.168.1.232- 本地连接地址:
localhost:13306
场景 B:访问内网 XXL-JOB 调度中心
调度中心运行在远程服务器 8080 端口,且有防火墙拦截外部 HTTP 请求。Bash
# 将远程 8080 映射到本地 18080
ssh -N -L 18080:127.0.0.1:8080 root@192.168.1.232- 浏览器访问:
http://localhost:18080/xxl-job-admin
3. 远程端口转发 (Remote Port Forwarding)
方向:将本地端口“推”到远程服务器。
适用场景:内网穿透、本地开发环境演示(给异地同事看)、调试第三方 Webhook 回调(微信支付/GitHub)。
3.1 标准命令语法
Bash
ssh -N -R [远程监听IP:]远程端口:本地地址:本地端口 用户名@跳板机IP-R: 标识 Remote Forwarding。远程监听IP: ⚠️ 默认情况下 SSH 服务端只允许绑定127.0.0.1。若需绑定0.0.0.0(公网访问),需修改服务器配置(详见 3.3 节)。
3.2 典型实战案例
场景 C:让服务器访问我本地的 Java 服务
我在本地启动了 Spring Boot (8080),需要服务器上的脚本调用我的接口。
Bash
# 将本地 8080 映射到服务器的 9090
ssh -N -R 9090:127.0.0.1:8080 root@192.168.1.232- 服务器端调用:
curl http://localhost:9090-> 流量直达你本地电脑。
场景 D:公网演示 (需服务器配置支持)
我在本地开发了网页,想发个 URL 给异地产品经理体验。
Bash
# 假设服务器公网 IP 为 1.2.3.4,且已开启 GatewayPorts
ssh -N -R 0.0.0.0:80:127.0.0.1:8080 root@1.2.3.4- 外部访问: 产品经理访问
http://1.2.3.4即可看到你本地的内容。
3.3 关键配置说明 (GatewayPorts)
默认情况下,使用 -R 映射的远程端口仅监听在 127.0.0.1。如果需要让其他人通过服务器 IP 访问该端口,必须修改服务器端的 /etc/ssh/sshd_config:
Bash
# 1. 编辑配置
vim /etc/ssh/sshd_config
# 2. 修改或添加以下参数
GatewayPorts yes
# 3. 重启 SSHD
systemctl restart sshd4. 生产环境高级技巧 (Pro Tips)
在日常高频使用中,为了保证隧道的稳定性和易用性,建议采用以下方案。
4.1 自动重连 (Autossh)
原生 ssh 命令在网络波动断开后不会自动重连。推荐使用 autossh 工具。
- 安装:
brew install autossh(Mac) /apt install autossh(Ubuntu) 命令模版:
Bash
# -M 0: 关闭监控端口(依赖 SSH 自身的保活机制) # -o "ServerAliveInterval 30": 每 30 秒发送心跳包 # -o "ServerAliveCountMax 3": 3 次心跳失败则重连 autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -N -L 3306:127.0.0.1:3306 root@server
4.2 后台静默运行
如果不想占用一个终端窗口,可以添加 -f 参数。
Bash
ssh -N -f -L 3306:127.0.0.1:3306 root@server- 注意: 第一次运行时如果需要输密码,
-f可能会导致输入困难。建议配置 SSH Key 免密登录 后再配合-f使用。
4.3 多级跳板 (ProxyJump)
如果目标服务器在内网深处,需要经过跳板机(Bastion Host)才能访问。
- 拓扑: 本地 -> 跳板机(1.1.1.1) -> 目标机(10.0.0.2)
命令:
Bash
# -J 指定跳板机 ssh -N -L 3306:127.0.0.1:3306 -J root@1.1.1.1 root@10.0.0.2
5. 常见问题排查 (Troubleshooting)
| 现象 | 可能原因 | 检查步骤 |
|---|---|---|
| Connection Refused | 1. 目标服务没启动 2. 目标端口写错 | 在服务器执行 netstat -tulpn 确认目标端口处于 LISTEN 状态。 |
| 端口被占用 | 本地端口已被其他程序使用 | 本地执行 lsof -i :端口号 查看占用进程。 |
| 远程转发外部连不上 | 服务器未开启 GatewayPorts | 检查 /etc/ssh/sshd_config,或只能在服务器本机通过 localhost 访问。 |
| 闲置一会就断开 | 防火墙切断了空闲连接 | 增加 SSH 心跳参数 -o ServerAliveInterval=60。 |
6. 总结速查表 (Cheat Sheet)
| 需求 | 采用模式 | 命令口诀 | 记忆法 |
|---|---|---|---|
| 我想访问服务器的服务 | 本地转发 (-L) | ssh -L 本地端口:目标IP:目标端口 | Local (拉过来) |
| 我想让服务器访问我的服务 | 远程转发 (-R) | ssh -R 远程端口:本地IP:本地端口 | Remote (推过去) |
| 后台运行不占终端 | 后台模式 | 加上 -N -f | No Shell, Fork |