第19章 内网穿透与隧道:frp / gost / SSH 隧道 / ngrok
学习目标
- 理解内网穿透的核心难题(NAT 后无法被动连入)与"反向隧道"解法
- 掌握 SSH 隧道三式:
-L(本地转发)、-R(远程转发=穿透)、-D(SOCKS) - 会用 frp 把内网服务安全暴露到公网,理解 frps/frpc 架构
- 分清"隧道"与"代理"的关系,及穿透的安全风险
前置知识
原理
核心难题:NAT 后的服务无法被动连入
内网服务(家里的 NAS、公司内网的 Web、开发机的本地服务)通常没有公网 IP,躲在 NAT 后面。NAT 的规则是"内可主动出、外不可主动入"——所以外部根本无法主动连到它。
公网 Client ──想访问──▶ ❌ [NAT/防火墙] ╳ 内网服务(无公网IP,连不进来)
解法:反向隧道(内网主动连出,建立通路)
既然"外不可入",那就让内网主动连出到一台有公网 IP 的中转服务器,建立一条隧道;之后外部请求打到中转服务器,再经隧道反向送回内网:
① 内网 frpc ──主动连出──▶ [公网中转 frps] ◀──② 外部请求── 公网 Client
│
③ 请求经隧道反向回内网 ◀───┘
内网服务 ◀── frpc 转发 ──┘
这就是一切内网穿透工具的共同骨架:内网主动建隧道 + 公网中转 + 反向转发。它和 第01章 的"隧道"一脉相承——只是方向反过来用。
SSH 隧道三式(自带、最方便)
SSH 是最唾手可得的隧道工具,三种转发方向要分清:
-L 本地转发:把"本地端口"的访问,经 SSH 转到"远端可达的目标"
ssh -L 8080:db.internal:5432 user@jump → 访问本地 8080 = 访问 jump 能看到的 db:5432
用途:从本地访问远端内网服务
-R 远程转发:把"远端端口"的访问,经 SSH 转回"本地可达的目标" ← 这就是内网穿透!
ssh -R 9000:localhost:3000 user@public → 访问 public:9000 = 访问你本地的 3000
用途:把本地/内网服务暴露到公网中转机
-D 动态转发:在本地起一个 SOCKS5 代理(第06章)
ssh -D 1080 user@jump → 本地 1080 成为 SOCKS5 出口
用途:通用代理
记忆法:
-L是"拉"(把远端服务拉到本地端口),-R是"推"(把本地服务推到远端端口),-R才是穿透。
frp:专业内网穿透
SSH -R 够用但功能有限(暴露多服务、HTTP 虚拟主机、UDP 都不方便)。frp 是专业方案,国内最常用:
- 架构:
frps(公网服务端)+frpc(内网客户端),frpc 主动连 frps 建隧道 - 协议:支持 TCP/UDP/HTTP/HTTPS/STCP(点对点加密)/XTCP(P2P 打洞)
- 能力:多服务复用一个 frps、HTTP 按域名路由、加密、鉴权、Web 管理面板、带宽限制
公网 内网
┌──────────────┐ ┌──────────────────┐
│ frps :7000 │◀──控制连接──│ frpc │
│ frps :80(http)│ │ → localhost:8080│ 内网 Web
│ frps :6000(tcp)│ │ → localhost:22 │ 内网 SSH
└──────────────┘ └──────────────────┘
外部访问 frps:80 → 隧道 → 内网 8080
gost:隧道/代理瑞士军刀
gost 是多功能的代理/隧道工具,支持几乎所有协议(HTTP/SOCKS/SS/relay/tunnel)和转发链(forward chain)——可把多个代理串成一条链,每跳用不同协议。适合复杂的代理组合场景。
ngrok:SaaS 化穿透
ngrok 把穿透做成了云服务:你不用自己搭公网中转,一行命令就把本地服务暴露到 ngrok 分配的公网域名——特别适合调试 Webhook、临时演示:
ngrok http 8080 # 立刻得到一个 https://xxxx.ngrok.io 指向你本地 8080
代价是依赖第三方服务、免费版有限制。
隧道 vs 代理:到底什么关系
- 代理(前 18 章主角):代收代发,可能理解/改写内容(第01章)
- 隧道:在两点间打通一条透明通路,把流量原样运过去(第04章 CONNECT)
- 内网穿透 = 反向使用的隧道:方向是"从内向外建、从外向内用"
三者技术同源,常组合:穿透隧道里可以跑代理协议(如 frp 隧道里暴露一个 SOCKS5)。
⚠️ 穿透的安全风险
把内网服务暴露到公网 = 主动打开一个攻击面:
- 暴露的服务必须有认证(别把无密码的内网后台直接暴露)
- 隧道本身要加密(SSH 自带加密;frp 用
tls/stcp) - 中转服务器是单点,被攻破则隧道尽失
- 企业环境中,未经授权的穿透是绕过出网管控的后门,安全团队会重点监控
️ 实现 / 命令
实验一:SSH -R 把本地服务暴露到公网中转机
# 本地跑了个服务在 3000,想让外部通过公网机 public 的 9000 访问它
ssh -N -R 9000:localhost:3000 user@public
# 注意:frps 端 sshd 要开 GatewayPorts yes,9000 才能对外(否则只绑 127.0.0.1)
# 在任意能访问 public 的机器上:
curl http://public:9000/ # = 访问你本地的 3000
实验二:frp 暴露内网 Web 与 SSH
# frps.toml(公网服务器)
bindPort = 7000
vhostHTTPPort = 80
webServer = { addr = "0.0.0.0", port = 7500, user = "admin", password = "admin" }
# frpc.toml(内网客户端)
serverAddr = "1.2.3.4"
serverPort = 7000
[[proxies]]
name = "web"
type = "http"
localPort = 8080
customDomains = ["app.example.com"] # 访问 http://app.example.com → 内网 8080
[[proxies]]
name = "ssh"
type = "tcp"
localPort = 22
remotePort = 6000 # 访问 frps:6000 → 内网 22
# 公网启 frps,内网启 frpc
./frps -c frps.toml &
./frpc -c frpc.toml &
ssh -p 6000 user@1.2.3.4 # 经 frp 隧道 SSH 进内网机
实验三:ngrok 一行暴露(调试 Webhook 神器)
ngrok http 8080
# Forwarding https://a1b2.ngrok.io -> http://localhost:8080
# 把 a1b2.ngrok.io 填到第三方 Webhook 配置里,本地就能收到回调
实验四:gost 转发链
# gost 把流量经两跳代理转发(A→B→目标),每跳协议可不同
gost -L=:8080 -F=socks5://proxyA:1080 -F=http://proxyB:3128
# 本地 8080 收到的流量 → 经 socks5 proxyA → 再经 http proxyB → 出网
排错
| 现象 | 根因 | 解决 |
|---|---|---|
ssh -R 端口只绑 127.0.0.1 | sshd 默认 GatewayPorts no | 服务端开 GatewayPorts yes |
| frpc 连不上 frps | 端口/防火墙/token 不符 | 查 bindPort、安全组、auth.token 两端一致 |
| HTTP 穿透 404/域名不通 | customDomains 与访问域名不符 | 核对域名解析指向 frps 公网 IP |
| 穿透延迟高/断流 | 中转机带宽/网络抖动 | 选近的中转、加 keepalive |
| 暴露后被扫描攻击 | 服务无认证就裸暴露 | 加认证、用 STCP 点对点、限制来源 |
| SSH 隧道频繁断 | 无保活 | 加 ServerAliveInterval/autossh |
本章小结
- 内网穿透的本质是反向隧道:内网主动连出公网中转,外部请求经隧道反向回内网。
- SSH 三式:
-L拉远端到本地、-R推本地到远端(=穿透)、-D起 SOCKS(第06章)。 - frp 是专业穿透(frps+frpc,多协议多服务);gost 是隧道瑞士军刀(转发链);ngrok 是 SaaS 化一行穿透。
- 隧道与代理技术同源、常组合;穿透 = 主动开攻击面,必须认证 + 加密 + 受控。
下一章 第20章 科学上网生态的技术原理,把"加密代理 + 流量混淆 + 智能分流"这套技术从工程角度讲清楚——它和企业代理、CDN、Mesh 在技术上同源。