内网穿透完全指南:从 NAT 原理到方案选型与实战

举报
yd_244592142 发表于 2026/10/27 10:33:36 2026/10/27
【摘要】 一、问题的起点:为什么外网访问不到我家这台机器? 1. 你的机器只有一个"小区内部门牌号"IPv4 地址一共约 43 亿个,早就分完了。为了让几十台设备共享一个公网 IP,NAT(网络地址转换)成了标配。你的机器拿到的是私有地址:地址段常见场景10.0.0.0/8企业内网、运营商分配172.16.0.0/12Docker 默认网桥、部分内网192.168.0.0/16家用路由器(你最熟悉的...

一、问题的起点:为什么外网访问不到我家这台机器?

1. 你的机器只有一个"小区内部门牌号"

IPv4 地址一共约 43 亿个,早就分完了。为了让几十台设备共享一个公网 IP,NAT(网络地址转换)成了标配。你的机器拿到的是私有地址:

地址段 常见场景
10.0.0.0/8 企业内网、运营商分配
172.16.0.0/12 Docker 默认网桥、部分内网
192.168.0.0/16 家用路由器(你最熟悉的那个)

192.168.1.100 这个地址,在你家局域网里独一无二,但全世界有几千万台机器都叫这个名字。公网路由器看到这个目的地址,只会直接丢弃——它根本不知道该往哪儿送。

2. NAT 的核心规则:出去时建表,回来时查表

NAT 网关维护一张转换表。关键在于它是单向触发的:

  • 你的机器主动访问外部 → NAT 创建一条映射 (192.168.1.100:54321) ↔ (公网IP:60001),外部回包可以顺着这条映射回来;
  • 外部主动发一个包进来 → NAT 表里没有对应条目,直接丢弃。

这就是所谓的"未经请求的入站流量会被丢弃"。不是防火墙拦你,是 NAT 压根不知道这个包该给谁。

3. 更糟的情况:CGNAT

很多宽带运营商自己也没那么多公网 IP,于是在你之上又套了一层 NAT,叫 CGNAT(运营商级 NAT)。这时候你在路由器 WAN 口看到的 IP 是 100.64.x.x——这是 RFC 6598 专门划给 CGNAT 的地址段。

一旦身处 CGNAT,连在路由器上配置端口映射的资格都没有了,因为你那个"公网 IP"本身也是内网地址。判断方法很简单:

# 查本机/路由器看到的公网 IP
curl ifconfig.me
# 再对比百度搜索"IP"显示的结果;或者看 WAN 口是否以 100.64 开头

如果两者不一致,或者 WAN 口是 100.64.x.x,那你就在 CGNAT 后面。


二、穿透的三条技术路线

所有内网穿透方案,本质上逃不出这三种思路:

路线 A:端口映射(要求有公网 IP)

在路由器上写一条规则:凡是访问我公网 IP 的 8080 端口的,都转给内网的 192.168.1.100:80。

  • ✅ 速度最快,能跑满家里上行带宽,是真·直连
  • ❌ 必须要有公网 IP;且等于把服务直接裸奔在公网上

配套 DDNS(动态域名)解决家宽 IP 会变的问题。

路线 B:反向代理 / 中转隧道(无需公网 IP)

既然"外部进不来",那就让内网机器主动出去,主动和一台有公网 IP 的服务器建立一条长连接,然后所有流量都沿着这条隧道倒流回来。

外网用户 ──▶ 公网服务器(frps / cloudflared edge)
                    ▲
                    │ 长连接(由内网机器主动发起)
                    │
              内网机器(frpc) ──▶ 本地服务 :8080

这是 frp、ngrok、Cloudflare Tunnel 的核心模型。代价是所有流量都要过一道中转,速度和带宽受限于中转服务器。

路线 C:点对点打洞(P2P)

前两条路都要依赖一台中间服务器转发。有没有可能两台都在 NAT 后面的机器直接连上?有,这就是 NAT 打洞,也是 Tailscale、ZeroTier、WebRTC 的底层魔法。


三、深入一层:打洞到底是怎么做到的?

这是整个内网穿透话题里最优雅的部分,值得单独讲。

1. 一个死锁

两台都在 NAT 后面的机器 A 和 B,面临一个死局:

  • A 想连 B,但 B 的 NAT 会丢弃 A 的包(因为 B 还没主动发出去过);
  • B 想连 A,同理也被丢弃;
  • A 要等 B 先发包才能连上,B 要等 A 先发包才能连上。

2. 破解:同时敲一拳

打洞利用了 NAT 的一个可预测行为:一旦内网主机发出了一个 UDP 包,NAT 就会创建一个映射,并且(大多数情况下)允许任何外部主机从这个映射的外部端口回包进来。

流程是这样的,需要一个双方都能访问到的会合服务器(rendezvous server):

  1. A 和 B 各自向会合服务器发一个 UDP 包;
  2. A 的 NAT 创建映射,203.0.113.10:5000;B 的 NAT 创建映射,198.51.100.20:6000;服务器记录下这两个公网端点;
  3. 服务器把对方的端点告诉 A 和 B;
  4. A 和 B 同时向对方的公网端点发 UDP 包。

关键一步来了:

  • A 发给 B 的那个包,大概率会被 B 的 NAT 丢弃(因为 B 的 NAT 还没为 A 建表)——但这一发不要紧,A 自己的 NAT 上被打出了一个洞,203.0.113.10:5000 现在开始接受入站了;
  • 与此同时 B 也发了同样的包,同样在 B 自己的 NAT 上打了个洞;
  • 于是 B 的包到达 A 时,正好撞上 A 刚打好的洞,进去了。反之亦然。

两个包都是为了被拒绝而发的,但正是这个"被拒绝"的动作,创造了对方被接受的条件。几毫秒之内,双向通路建立,会合服务器功成身退。

3. 三个标准化协议:STUN / TURN / ICE

IETF 把这套流程标准化了:

协议 作用 特点
STUN (RFC 5389/8489) 告诉设备"你在公网上看起来是哪个 IP:端口" 极轻量,只反射信息,绝不转发数据
TURN (RFC 5766/8656) 打洞失败时的兜底中继服务器 永远能用,但吃带宽、加延迟
ICE (RFC 8445) 总调度框架 收集所有候选地址(本地/STUN/TURN),并行测试,选最直的那条

ICE 的思路是"全都试一遍,谁通用谁":本地直连优先 → STUN 辅助的打洞次之 → TURN 中继最后兜底。

4. 成功率与失败原因

打洞不是万能的:

  • UDP 打洞在现实网络中的成功率约 70–82%;非对称 NAT 环境下可达 80–95%,加上 TURN 兜底后接近 100% [citation:3][citation:6]
  • 对称 NAT(Symmetric NAT):对不同的目标地址分配不同的外部端口。你从 STUN 拿到的端口,用来连 B 时根本不是同一个——信息直接失效。约 11% 的用户在这种 NAT 后面 [citation:3]。CGNAT 也常常表现为对称 NAT,这是国内打洞成功率偏低的主因之一。
  • 端口受限 NAT:只接受你主动联系过的地址回包,稍微严苛一点就失败。
  • 企业防火墙:直接封掉 UDP,或在应用层做深度检测。
  • TCP 打洞:TCP 的三次握手导致"同时打开(simultaneous open)",多数 NAT 实现处理得很糟糕,打洞本质上是一项 UDP 技术 [citation:3]。

另外,NAT 映射是有寿命的,通常 30–300 秒不通信就会被回收,所以需要周期性 keepalive 保活 [citation:6]。

顺带一提:你每天都在用打洞。微信/Teams 的视频通话、联机游戏、WebRTC,背后都是 ICE 在帮你选路。当你的游戏提示"NAT 类型严格",八成是悄悄降级到中继了。


四、主流方案横评

方案 需要公网 IP 协议支持 隐私性 速度 成本 上手难度 最适合
端口映射 + DDNS ✅ 必须 全协议 取决于你自己 ⚡ 最快 免费/极低 简单 有公网 IP 的极速直连党
frp(自建) 需要一台 VPS 全协议(TCP/UDP/HTTP/HTTPS/STCP) ✅ 数据自己掌握 🚗 取决于 VPS 位置 VPS 月费 中等 技术党、要全协议、要可控
Cloudflare Tunnel ❌ 不需要 主要 HTTP/HTTPS ⚠️ CF 会终止 TLS 🚗 中等 免费 简单 建站、博客、Web 服务
Tailscale / ZeroTier ❌ 不需要 全协议(组网后 IP 直连) ✅ 端到端加密 🚀 打洞成功即直连 免费额度充足 极简 自己远程访问自己的设备
WireGuard 自建 需要公网 IP(或配合穿透) 全协议 ✅ 完全自控 🚀 快 VPS 月费 中等 追求可控的组网
ngrok / cpolar / 花生壳 ❌ 不需要 HTTP/TCP ⚠️ 走厂商 🚗 中等 免费有额度限制 极简 临时调试、给客户演示

几个补充说明:

速度实测参考(国内访问视角):DDNS 直连 > 近机房 VPS 中转 > Cloudflare Tunnel > Tailscale Funnel 绕国外节点 [citation:2]。不过记住一个铁律——家宽的上行带宽才是最终瓶颈,很多家宽上行只有 30–50Mbps,再快的方案也跑不过这个天花板。

Tailscale vs Cloudflare Tunnel 的本质区别:前者是组网(把你的设备拉进同一个虚拟局域网,用 WireGuard 端到端加密,设备间直连),后者是反向代理(把你的某个 Web 服务发布到公网)。一个是"私有网络互联",一个是"安全的公网发布",别搞混 [citation:5]。

Cloudflare Tunnel 的两个已知代价:一是它作为反向代理会在边缘终止 TLS,即 Cloudflare 能看到明文流量(这是设计使然,不是 bug)[citation:11];二是免费套餐条款上对大流量视频/大文件传输有限制 [citation:11]。另外国内访问 Cloudflare 的连通性时好时坏,别把它当唯一依赖。


五、实战:三种典型配置

方案 1:frp 自建(最自由)

frp 从 v0.52.0 起已废弃 INI 配置格式,现在推荐 TOML / YAML / JSON。下面用 TOML,示例版本 v0.69.x,请以官方 Release 为准 [citation:7]。

公网服务器(frps) /etc/frp/frps.toml:

bindPort = 7000

[auth]
method = "token"
token = "<用 openssl rand -hex 24 生成>"

[webServer]
addr = "127.0.0.1"   # 别直接暴露到公网
port = 7500
user = "admin"
password = "<强密码>"

内网机器(frpc) /etc/frp/frpc.toml:

serverAddr = "<公网服务器IP>"
serverPort = 7000

[auth]
method = "token"
token = "<与服务端一致>"

[[proxies]]
name = "home-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6000

用 systemd 托管(/etc/systemd/system/frps.service):

[Unit]
Description=frp server
After=network-online.target

[Service]
Type=simple
ExecStart=/opt/frp/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now frps
journalctl -u frps -f   # 看日志

安全组放行 7000(frpc 连接)和你映射出去的端口(如 6000)。然后:

ssh -p 6000 user@<公网服务器IP>   # 直接 SSH 进家里那台机器

进阶姿势:如果是 HTTP 服务,更好的做法是让 frps 的 vhostHTTPPort 只监听 127.0.0.1,由 Nginx 监听 80/443 做反向代理和 TLS 终止。这样防火墙只需放行 7000 和 80/443,frps 的 HTTP 端口完全不对公网开放 [citation:4]。

proxyBindAddr = "127.0.0.1"
vhostHTTPPort = 8080

方案 2:Cloudflare Tunnel(建站最省事)

# 安装 cloudflared 并登录授权
cloudflared tunnel login

# 创建隧道
cloudflared tunnel create my-homelab

# 配置 /etc/cloudflared/config.yml
tunnel: my-homelab
credentials-file: /root/.cloudflared/<隧道ID>.json
ingress:
  - hostname: nas.example.com
    service: http://localhost:8096
  - service: http_status:404

# 跑起来
cloudflared tunnel run my-homelab

零端口开放、自带 DDoS 防护和免费证书,还有 WAF 挡在前面,对个人建站来说性价比极高 [citation:14]。

方案 3:Tailscale(自己访问自己的设备,首选)

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

装完即用。之后你的每台设备都有一个 100.x.x.x 的虚拟内网 IP,SSH、RDP、访问 NAS 全都走这条加密隧道,且打洞成功后是设备间直连,不经过任何中转。

个人用途我几乎总是推荐这个——它把"远程访问自己设备"这件事从"开一个公网端口挨打"变成了"只有我的设备能进来"。不想依赖官方控制面?还有开源的 Headscale 可以自建。


六、安全红线(这一段请认真看)

内网穿透最大的风险不是"配不通",而是配通了,然后把一个没有鉴权的服务挂在了公网上。下面这些是底线:

绝对不要直接暴露到公网的端口:

端口 服务 为什么危险
3306 / 5432 MySQL / PostgreSQL 暴力破解 + 拖库
6379 Redis 未授权访问可直接拿 shell
22 SSH 全天被机器人扫(换非标准端口只能降噪,不能防攻击)
9443 Portainer 拿到它=拿到 Docker =拿到宿主机
docker.sock Docker 套接字 等于把 root 交出去

必须做的几件事:

  1. 最小暴露面:只放行 80/443,其余服务全部由反向代理转发,顺便统一 HTTPS 和访问控制 [citation:14]。
  2. token 要够随机:openssl rand -hex 24,别用 123456——那是教程里的占位符,不是让你照抄的 [citation:7]。
  3. SSH 强制密钥登录:确认密钥可用后 PasswordAuthentication no,再加 fail2ban 防爆破。
  4. frp Dashboard 别裸奔:webServer.addr 保持 127.0.0.1,需要时通过 SSH 隧道或反向代理访问。
  5. 别把配置文件传进公开仓库:/etc/frp/*.toml 里有 token。
  6. 中转节点的信任问题:Cloudflare Tunnel 之类的托管方案,服务商在边缘终止 TLS,理论上看得到明文。敏感数据要么端到端再加密一层,要么改用 Tailscale 这类端到端方案 [citation:11]。
  7. 合规提醒:在国内,把宽带线路用于对外提供经营性/公开服务,可能涉及运营商条款与 ICP 备案要求;家庭宽带通常不允许开设对外服务。自用调试、个人远程访问一般没问题,长期公开建站建议用合规的云服务器。

七、选型决策树

别纠结了,照着走:

你的服务需要公开给「不装任何客户端的普通人」访问吗?
├─ 是 ──▶ 你有公网 IP 吗?
│         ├─ 有 ──▶ 端口映射 + DDNS + Nginx(速度最快)
│         └─ 没有 ─▶ Web 服务?
│                    ├─ 是 ──▶ Cloudflare Tunnel
│                    └─ 否 ──▶ 买台 VPS 跑 frp
│
└─ 否(只有你自己/团队成员访问)
          ├─ 追求极简 ──────────▶ Tailscale / ZeroTier(首选)
          ├─ 追求完全自控 ──────▶ WireGuard 自建 / Headscale
          └─ 临时演示/调试 ─────▶ ngrok / cpolar

一句话总结:想省事就 Tailscale,建网站就 Cloudflare Tunnel,想自由就 VPS + frp,有公网 IP 就 DDNS 直连。


八、排错清单

端口不通?从上到下逐层排查:

  1. 服务真的在监听吗? → ss -ltnp | grep :8080
  2. 本机防火墙放行了吗? → sudo ufw status / firewall-cmd --list-all
  3. 云服务器安全组放行了吗?(最常见的坑,尤其是轻量云)
  4. 路由器端口映射规则写对了吗?内网 IP 是不是变了?
  5. ISP 是不是封了 80/443?(家宽很常见)→ 换端口或用 Cloudflare Tunnel 绕开 [citation:14]

frpc 连不上 frps?

nc -vz <公网IP> 7000        # 先测控制端口通不通
journalctl -u frpc -n 50    # 再看日志

99% 是三个原因:安全组没放行、token 两边不一致、frps 没起来。

打洞总是失败、速度很慢?

说明降级到了中继(Tailscale 的 DERP、ZeroTier 的 planet)。常见原因是对称 NAT 或 CGNAT。可以检查 NAT 类型,或者干脆接受中转——稳定比快重要。

速度跑不满?

先看家宽上行带宽。100M 宽带的上传往往只有 30M 甚至更低,这是物理天花板,换什么方案都没用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。