不暴露端口的内网穿透:Cloudflare Tunnel 实战记录

局域网服务器要暴露到公网,传统做法是路由器端口映射、frp 或者 DDNS,但这几个方案都有硬伤:要开入站端口(NAT 暴露、公网扫描、被爆破风险高),还依赖公网 IP。这次用 Cloudflare Tunnel 做了一套「零入站端口」的内网穿透,记录一下关键操作和坑。

一、为什么选 Cloudflare Tunnel

  • 不开入站端口:cloudflared 主动向外发起连接,公网根本扫不到你的服务器,隐蔽性极好;
  • 免费:免费套餐就够个人用,支持 HTTP/TCP/SSH;
  • 自带 HTTPS + CDN:CF 代理自动给子域名签证书,源站 IP 隐藏;
  • 配置即代码:隧道配置可以在云端(Dashboard)或本地 config 维护,可批量加域名。

二、架构

公网用户 → https://napcat1.zain-dev.top
              ↓ Cloudflare 边缘 (proxy, TLS)
       cloudflared 进程(本机出站连接)
              ↓ 127.0.0.1:6099
       内网服务(NapCat WebUI 等)

每一组「子域名 → 本地端口」就是一条 ingress 规则,改配置后隧道会在几秒内热同步,不需要重启。

三、关键步骤

1. 安装并登录

# 装 cloudflared(Linux amd64 示例)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared

# 用 API Token 而非 OAuth 登录(适合无人值守服务器)
export CF_API_TOKEN="cfat_xxx"

2. 创建/复用隧道

# 已有隧道直接用它的 token 跑
cloudflared tunnel run --token-file /etc/cloudflared/token

# 云端配置模式下,隧道配置在 CF 账号里,本地只需要 token 文件

3. 加域名(两种方式)

方式 A:CF API 写隧道配置 + DNS(适合批量)

# 更新隧道 ingress(新增 napcat1~12 → 6099~6110)
PUT /accounts/{ACCT}/cfd_tunnel/{TUN}/configurations
# 给每个子域名建 DNS CNAME → {隧道ID}.cfargotunnel.com

方式 B:Dashboard 手动加 Public Hostname,选服务类型 HTTP 填 localhost:端口即可。

4. 批量加 12 个子域名的心得

把「预留端口」和「子域名」一一对应提前配好,后面新增实例直接对号入座,不用再动隧道。实测未监听端口返回 502,监听正常返回 301/200——用这个就能判断隧道通没通、实例起没起。

四、踩过的坑

  1. Token 权限分读写:一个只读 Token 能查 zone/tunnel,但 PUT configurations 直接 1001 Not authorized、建 DNS 10000 Authentication error。要写操作得给 Token 加 Account → Cloudflare Tunnel → Edit 和 Zone → DNS → Edit。
  2. 远程配置模式 vs 本地配置:tunnel run --token 是云端配置;想本地维护 ingress 要写 config.yml + credentials。混用会一脸懵,明确你用的哪种。
  3. 容器里的 localhost 不是宿主机:隧道转发到容器的服务时,ingress 的 service 写 127.0.0.1:port 指的是 cloudflared 所在宿主机,容器要开对应端口映射。

五、安全性

隧道本身不开任何入站端口,比 frp/端口映射安全一个量级。但应用层防护不能省:管理面板要设强密码/Token、保持更新,敏感服务建议再套一层 Cloudflare Access(Zero Trust)做二次认证。

六、小结

Cloudflare Tunnel 适合「一台无公网 IPv4 的服务器 + 一堆内网服务」的场景,配置即代码、免费、还能隐藏源站。强烈建议把 frp 换成它。