内网穿透
# 内网穿透技术指南:原理、方案与实践建议
内网穿透(NAT Traversal)是一种允许外部互联网用户访问位于私有网络(NAT/防火墙后)内设备或服务的技术。在家庭办公、远程运维以及分布式系统联调中,它是解决“无公网 IP”痛点的核心方案。
# 🌐 核心原理详解
# 1. 为什么无法直接访问?
- 私有地址空间: 家庭或公司内部设备通常分配的是非公网路由地址(如
192.168.x.x),这些地址在公共互联网中不可识别。 - NAT 的保护/阻隔: 路由器通过网络地址转换(NAT)技术允许内部设备共享一个公网出口,但它会拦截所有来自外部、未经请求的主动连接。
# 2. 隧道建立的三步曲
- 主动出击: 内网设备首先向具有固定公网 IP 的中转服务器发起连接,并建立一个持久的通信隧道。
- 流量监听: 公网服务器在特定端口上监听外部请求。
- 反向转发: 当外部请求到达服务器时,服务器通过已建立的隧道将请求“推送”给内网设备,从而绕过防火墙的入站限制。
# 🛠️ 主流内网穿透方案对比
根据不同的使用场景和技术需求,可以参考下表选择最合适的工具:
| 方案 | 技术核心 | 典型工具 | 适用场景 |
|---|---|---|---|
| SSH 隧道转发 | SSH 协议加密隧道 | 原生 OpenSSH | 开发者临时调试、单端口加密传输。 |
| 应用层穿透 | TCP/UDP 流量转发 | frp, nps, ngrok | 长期挂载 Web 服务、远程桌面、游戏服务器。 |
| 虚拟局域网 | P2P 组网 / 虚拟网卡 | ZeroTier, Tailscale | 跨地域设备互联、构建私有办公内网。 |
| P2P 打洞 | UDP 状态检测 | WebRTC 协议等 | 高带宽、低延迟实时通信(如视频通话)。 |
# 🚀 进阶实践:基于 SSH 的远程端口转发
对于拥有云服务器(VPS)的用户,使用 SSH 远程转发(Remote Forwarding)是最安全、快捷的实现方式。
# 1. 执行命令
# 1.1. 服务端操作(在内网服务器上执行)
# -N: 不执行远程命令,只做端口转发
# -R: 远程端口转发(反向)
ssh -N -R <remote_port>:localhost:<local_port> <user>@<server_ip>
2
3
此命令会将公网服务器端口接收到的流量,通过 SSH 安全加密隧道转发至本地指定端口。
# 1.2 客户端操作 (Client-side Access)
当反向隧道建立后,根据 VPS 的安全配置,客户端有以下两种访问方式:
1.2.1 情况 A:通过 VPS 公网直接访问 (最常用)
若 VPS 防火墙已放行 <remote_port>,且 SSH 配置(sshd_config)已开启 GatewayPorts yes (安全风险):
- 前置条件:VPS 防火墙允许外部访问指定端口,且 SSH 允许绑定非回环接口。
- 访问方式:直接在浏览器或工具中输入:
http://<server_ip>:<remote_port>
1.2.2 情况 B:通过“隧道中的隧道”访问 (更安全)
若 VPS 安全级别较高(默认仅监听 127.0.0.1),则需要在客户端执行 本地端口转发 (-L) 来二次对接隧道:
- 执行命令:
# 将本地 client_port 流量通过 SSH 转发至 VPS 的 remote_port ssh -L <client_port>:localhost:<remote_port> <user>@<server_ip>1
2 - 访问方式:在客户端本地浏览器输入:
http://localhost:<client_port>
# 2. 部署建议与安全优化
- 持久化连接: 使用 autossh 或系统的
systemd服务来管理隧道,确保在网络波动(如运营商重新分配动态 IP)导致断开后能自动重连。 - 最小暴露原则: 仅映射业务所需的特定端口,严禁将 SSH 或数据库管理端口直接暴露在公网。
- 防火墙策略: 确保公网服务器的防火墙(如 UFW 或 iptables)仅放行必要的转发端口,并配置强口令或 SSH 密钥认证。
- IPv6 备选方案: 在运营商支持 IPv6 的环境下,可以尝试利用 IPv6 的公网寻址能力结合 DDNS 实现更直接的访问,作为穿透方案的有效补充。
# 3. 执行中遇到的问题记录
# 3.1 实现绑定非回环接口(GatewayPorts yes)
要让你的 SSH 转发真正“对外生效”,你需要配置 VPS 的 SSH 守护进程(sshd)来打破回环接口的限制:
- 修改OpenSSH 守护进程配置:打开
/etc/ssh/sshd_config文件。 - 开启选项:将 GatewayPorts 设置为 yes。
# 开启所有网络接口监听 GatewayPorts yes1
2 - 重启服务:
sudo systemctl restart ssh - 注意:配置修改后,需要重新创建新的ssh隧道才能起作用,以前的隧道还是旧的配置,不能立即生效。 销毁先前的进程如下:
# 查看进程占用 sudo netstat -tlnp | grep <port> # 强制结束该进程: sudo kill -9 <pid> # 确认端口已释放. 预期无任何输出 sudo netstat -tlnp | grep <port>1
2
3
4
5
6
7
8
# 3.2 保持隧道稳定
在linux上
SSH 连接容易因超时断开,生产环境推荐用 autossh 自动重连:
# 安装 apt install autossh # 自动重连的反向隧道 autossh -M 9090 -N -R 8080:localhost:80 user@公网服务器IP1
2
3
4
5写成 systemd(linux) 服务开机自启
# /etc/systemd/system/ssh-tunnel.service [Unit] Description=SSH Reverse Tunnel After=network.target [Service] ExecStart=autossh -M 9090 -N -R 8080:localhost:80 user@公网服务器IP Restart=always RestartSec=10 [Install] WantedBy=multi-user.target1
2
3
4
5
6
7
8
9
10
11
12然后使用
systemctl enable --now ssh-tunnel
在mac上
Mac 上推荐用 launchd(macOS 原生服务管理)来代替 Linux 的 systemd。
方案一: launchd(推荐)
创建配置文件:
nano ~/Library/LaunchAgents/com.ssh.tunnel.plist1写入内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.ssh.tunnel</string> <key>ProgramArguments</key> <array> <string>/usr/bin/ssh</string> <!-- 不执行任何远程命令,仅建立隧道连接,纯转发模式 --> <string>-N</string> <!-- 每隔 60 秒向服务器发送一次心跳包,防止因长时间无数据传输被防火墙/路由器断开。 --> <string>-o</string><string>ServerAliveInterval=60</string> <!-- 心跳包连续发送 3 次没有收到响应,则判定连接已死,主动断开。(即最多等 60×3=180 秒) --> <string>-o</string><string>ServerAliveCountMax=3</string> <!-- 如果端口转发建立失败(比如服务器端口被占用),ssh 进程直接退出。这样 launchd 检测到进程退出后会自动重启,避免僵死。 --> <string>-o</string><string>ExitOnForwardFailure=yes</string> <string>-R</string> <string>8080:localhost:80</string> <string>user@公网服务器IP</string> </array> <!-- 断开后自动重启 --> <key>KeepAlive</key> <true/> <!-- 开机自启 --> <key>RunAtLoad</key> <true/> <!-- 日志 --> <key>StandardOutPath</key> <string>/tmp/ssh-tunnel.log</string> <key>StandardErrorPath</key> <string>/tmp/ssh-tunnel.err</string> </dict> </plist>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39加载并启动:
# 加载服务 launchctl load ~/Library/LaunchAgents/com.ssh.tunnel.plist # 查看状态 launchctl list | grep ssh.tunnel # 停止 launchctl unload ~/Library/LaunchAgents/com.ssh.tunnel.plist1
2
3
4
5
6
7
8
方案二:autossh + launchd
先安装 autossh:
brew install autossh1plist 改为调用 autossh:
<!-- autossh 的 -M 9090 会额外开一个监控端口,检测隧道是否存活,比原生 ssh 重连更可靠。 --> <key>ProgramArguments</key> <array> <string>/opt/homebrew/bin/autossh</string> <string>-M</string><string>9090</string> <string>-N</string> <string>-R</string> <string>8080:localhost:80</string> <string>user@公网服务器IP</string> </array>1
2
3
4
5
6
7
8
9
10必要前提:免密登录 launchd 运行时没有交互界面,必须配置 SSH 密钥登录,否则无法自动重连:
# 生成密钥(如果还没有) ssh-keygen -t ed25519 # 上传公钥到服务器 ssh-copy-id user@公网服务器IP # 测试免密登录 ssh -N user@公网服务器IP1
2
3
4
5
6
7
8
# ✅ 总结
内网穿透的本质是通过**“内网主动出击”来换取“外部受控进入”**。选择方案时应平衡安全性、稳定性和操作复杂度:临时需求选 SSH,长期服务选 frp,多设备互联选虚拟网卡组网
- 02
- VisiData 终极生存指南(vd)04-27
- 03
- macOS 定时任务实现:Git 仓库状态自动同步方案04-26