内网穿透自建:FRP隧道、子域名与安全收口

把家里或机房那台内网机器上的服务暴露到公网,是每个自建站的人都会遇到的需求:家里的 NAS 想在外网访问,办公室的开发环境要让同事联调,树莓派上的监控面板想在手机上打开。但家宽和多数办公网络都没有公网 IP,甚至连运营商都给你套了二层 NAT,端口映射这条路根本走不通。

内网穿透就是绕开这个限制的标准解法。它不神秘:一台有公网 IP 的服务器当入口,内网机器主动向它建立一条长连接,公网访问进来时,服务端顺着这条连接把流量转回去。难点不在能不能通,而在通得稳、通得安全、出了事能查。这篇把这几件事从头讲清楚。

一、先把三条路分清:端口映射、反向隧道、组网

很多人上来就问用哪个工具,其实先该问我想要的是哪种模型。内网穿透这一类需求,背后有三种完全不同的实现,选错了后面所有配置都在白费。

第一种是端口映射,也叫 NAT 穿透的传统做法:在路由器上把外网某个端口 DNAT 到内网某台机器的某个端口。它要求你有公网 IP,而且运营商最近几年大量回收家宽公网地址、把用户塞进运营商级 NAT(CGNAT),所以这条路在新装宽带上大约八成走不通。判断方法很简单:在路由器上看到的 WAN 口地址如果是 100.64.0.0/10 这一段,就是 CGNAT,映射出去也没用。

第二种是反向隧道,也就是狭义的内网穿透:内网机器主动连接公网服务器,维持一条持久连接,服务端把进来的请求通过这条连接转发。这条主动建立的连接方向很关键,因为它不受 NAT 限制,家宽也能用。FRP、rathole、ngrok、Cloudflare Tunnel 都属于这一类。

第三种是组网,也就是虚拟局域网方案:把分散在几地的机器放进同一个虚拟网段,彼此像在同一个交换机下一样互通,不针对某个端口做映射。WireGuard、Tailscale、ZeroTier 属于这一类。

三者的取舍可以这么看:只需要暴露一两个 HTTP 服务给公众访问,用反向隧道;只有自己和几个同事要访问整个内网的多台机器、多个端口,用组网;有公网 IP 且完全不介意把端口直接开放,才考虑端口映射。现实中经常混用:组网解决运维通道,反向隧道解决对外服务。

二、FRP 的四个角色与最小可跑配置

FRP 是这类工具里部署最广的一个,结构清晰:服务端 frps 跑在有公网 IP 的机器上,客户端 frpc 跑在内网机器上,两者靠一条由客户端主动发起的控制连接维持,实际数据走另外的工作连接。配置文件里最容易搞混的是三类端口。

第一类是绑定端口 bindPort,默认 7000,是 frpc 连 frps 的控制通道端口,必须在公网服务器上开放,且只对你的固定来源开放。第二类是 vhost 端口,通常用 80 或 443,用来接收公众 HTTP 访问。第三类是为每个隧道单独申请的远程端口。最小配置大致长这样:

[common]
bind_port = 7000
token = 换成一串足够长的随机字符串
vhost_http_port = 80
subdomain_host = tunnel.example.com
max_pool_count = 5
tls_only = true

[web]
type = http
local_ip = 127.0.0.1
local_port = 8080
subdomain = nas

客户端那边把 server_addr 指向公网服务器、server_port 填 7000、token 与服务端一致,再把同样的 web 段落抄过来即可。跑起来后访问 nas.tunnel.example.com 就应该能到内网的 8080。

这里有个容易忽略的点:local_ip 一律写 127.0.0.1,别写内网 IP。看起来写 0.0.0.0 或本机内网地址更通用,但一旦穿透正常了,那个内网端口同时也会被同网段的其他设备访问到,等于顺手把服务暴露给了整个局域网。写回环地址,服务就只在穿透这一条路径上可见。

三、用子域名而不是一堆端口

新手常见的做法是给每个服务开一个远程端口:7001 给 NAS,7002 给监控面板,7003 给开发环境。这套做法能跑,但很快会出问题。

端口方案的问题有三个。一是记忆负担,端口号毫无语义,过两个月自己都要翻配置。二是拦截问题,很多公司的出口防火墙和公共 WiFi 会拦非常规高端口,而 443 基本不会被拦。三是证书与 HTTPS,每个端口一张证书或者干脆用自签证书,浏览器天天给你弹警告。

子域名方案把这件事收干净:所有服务都跑在同一个 443 端口上,靠域名区分。frps 的 vhost_https_port 配合 subdomain_host,客户端每个隧道写不同的 subdomain,访问时用不同的二级域名,服务端根据 Host 头分发。这样带来的直接好处是:一张泛域名证书(例如星号.tunnel.example.com 这种形式)就能覆盖全部服务,HTTPS 配置写一次,全站生效。

证书用 acme.sh 或 certbot 的 DNS 验证方式签发泛域名证书。DNS 验证的好处是不需要在服务器上开放 80 端口给 ACME 校验,也不会因为 CDN 或防火墙规则挡住验证而续期失败。续期脚本记得加一条 reload frps 的动作,否则证书文件更新了、进程还握着旧的,浏览器看到的仍是过期证书。

四、安全:暴露到公网的那一刻就开始被扫

这一节是本文最想强调的部分。内网穿透最坏的结果不是打不开,而是打开了但没设防。一个只暴露了控制面板的服务,在公网上被测到、被爆破、被接管,可能就是一两天内的事。

第一件事是 token 必须足够长。FRP 的 token 不是密码,它长期有效并且明文写在配置文件里,短 token 一旦被暴力猜测或被日志泄露就全线失守。生成方式用 openssl rand 出至少 32 字节,不要自己手编。较新版本的 frp 支持 token 的 sha256 校验形式,服务端只存哈希,配置泄露时也不会直接暴露明文。

第二件事是不要把管理面板与业务端口混在一起。frps 的 dashboard 默认监听 7500,很多人图方便直接把它也开在公网上。正确做法是 dashboard 只绑定 127.0.0.1,然后通过 SSH 隧道或者另一个更严格的隧道去看。真要在公网开,至少套一层认证加 IP 白名单:

location / {
allow 1.2.3.4;
deny all;
auth_basic "admin";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:7500;

}

第三件事是控制端口只对固定来源开放。bindPort 只允许你自己那些内网机器所在的出口 IP 访问,用 nftables 或安全组限制。这条规则的价值在于:即便 token 泄露,攻击者也得先能连上控制端口才能利用。

第四件事是给每个隧道加最小限度的防刷。穿透出去的服务同样会挨爬虫和扫描器,Nginx 侧的 limit_req、limit_conn 该加还是要加,别以为反正在内网机器上、量不大就跳过。

五、稳定性:断线、重连与心跳

内网穿透最常见的使用体验是昨天还好好的、今天打不开了。这类问题九成出在连接维持上,而不是配置本身。

先看心跳。frpc 与 frps 之间靠心跳包判断对方是否存活,默认间隔 30 秒、超时 90 秒。家宽线路经常出现连接没断但也不通的假死状态,表现为 frpc 还在跑、日志没报错,但公网访问超时。这时候调小心跳间隔(比如 10 秒)往往比查网络更快见效。同时给 frpc 配 systemd 服务并设置自动重启策略,进程真挂了能自动拉起。

再看出口 IP 变动。家宽的 IP 是动态的,如果控制端口做了 IP 白名单,一次重拨号就会把自己锁在外面。更稳的做法是给白名单留出定期更新的机制,或者干脆用证书双向认证代替 IP 白名单——frp 支持 TLS 双向认证,客户端证书泄露的概率远低于 IP 变动带来的运维负担。

第三个是连接池。frpc 的连接池参数决定预先建立多少条工作连接,默认值偏小,在高并发访问下会频繁新建连接,表现为首字节时间忽高忽低。把它调到 5 到 10 之间是个合理的起点,但这只解决了建连延迟,不解决带宽瓶颈。

第四个是带宽。所有流量都要绕经公网服务器,服务器的带宽就是你的天花板。一台 1Mbps 的入门云主机,穿透一个图片站基本没法用。选服务器时按峰值流量的两倍倒推带宽,别只看配置单上的 CPU 和内存。

六、出问题时按这个顺序查

排查要按数据流的反方向走,一层层定位,不要一上来就重装。

第一步,确认 frpc 到 frps 的控制连接在不在。在 frps 机器上执行 ss -tnp 看 7000 端口有没有来自客户端 IP 的已建立连接。没有,说明是客户端侧问题:网络不通、token 不对、frpc 没启动。看 frpc 的日志,它启动失败时通常写得很清楚。

第二步,看隧道注册成功没有。frps 日志会打印每条隧道的新增与关闭记录。如果隧道反复增删,通常是名称冲突——同一个 subdomain 被两条隧道申请了。

第三步,确认请求到了 frps 但没有转发。这时用 curl 直连服务器本地端口,看返回什么。如果本地正常、域名访问报 502,问题在 vhost 路由或证书;如果本地也超时,问题在客户端到本地服务这一段。

第四步,在客户端机器上直接 curl 本地端口。这一步排除应用本身的问题。内网穿透的复杂之处在于链路太长,任何一环出问题表现都一样,所以只能从两端往中间夹。

第五步,看一眼 MTU。大文件传输时卡住、小请求正常,典型是 MTU 不匹配导致分片被丢。用带不分片标记的 ping 逐步试出路径 MTU,再在隧道配置或网卡上把 MTU 调小到合适值。这个坑在 IPv6 和部分运营商线路上特别常见。

七、组网方案的适用边界

如果需求从暴露一个服务变成让几台机器互相访问,反向隧道就不再合适了。给每台机器、每个端口都配一条隧道,配置量和维护成本都会爆炸。这时该换 WireGuard 或基于它的 Tailscale。

WireGuard 的特点是配置极简、内核态实现、性能好,但它需要一个节点有公网 IP 做中继或直接点对点。Tailscale 这类方案则用中继服务解决了双方都在 NAT 后面的问题,代价是要依赖第三方的协调服务,并把网络拓扑信息交给它。自己完全掌控的方案是自建 headscale,配合自己的中继服务器。

判断标准可以简化成一句话:受众是公众,用反向隧道;受众是少数指定的人,用组网。既给公众又要给团队运维,就两条链路并行——对外走 frp 加 Nginx,对内走组网,运维通道不经过任何公网端口,这是目前比较稳妥的组合。

八、小结

内网穿透的技术含量不在配置,而在于你对自己暴露了什么心里有数。三条底线建议写进自己的运维笔记:控制端口只对固定来源开放、token 长度不低于 32 字节且定期轮换、管理面板永不明文暴露在公网。做到这三条,剩下的都是调优;做不到,配置写得再漂亮也只是把内网直接挂到了互联网上。

Last modification:November 4th, 2026 at 01:31 pm

Leave a Comment