Cloudflare Tunnels——无需端口转发即可公开访问

问题所在

我家 ISP 的路由器有一个 Web 管理界面,可以配置防火墙规则和其他设置,但端口转发只对商业套餐开放。不管怎么 Google、怎么打客服电话,都改变不了这一点。没有端口转发,我笔记本上跑的任何东西,都无法从家庭网络外部访问。

作为一个开发者,我在工作中一直用 Microsoft Dev Tunnels —— 开发期间给本地服务提供临时公网 URL。我需要的是同样的东西,但要永久可用:自定义域名、以守护进程方式运行、而且免费。于是,我找到了 Cloudflare Tunnels。

什么是 Cloudflare Tunnel?

Cloudflare Tunnel 会从你的服务器到 Cloudflare 边缘网络建立一条仅出站的连接。流量走向是这样的:

用户 → Cloudflare 边缘 → 隧道 → 你的服务器

你的服务器主动拨出到 Cloudflare —— 不需要任何入站端口。NAT 和端口转发都不再重要,只要服务器能向 Cloudflare 发出站连接就行(严格防火墙需要放行到 Cloudflare 的出站访问,包括隧道运行所需的 7844 端口)。只要你的机器能发 HTTPS 请求,它就能对外提供服务。

这实际上比传统端口转发更安全,因为:

  • 路由器上不暴露任何端口
  • 不需要静态 IP
  • Cloudflare 负责 DDoS 防护、SSL/TLS 和缓存
  • 流量在传输中加密(注意:Cloudflare 在其边缘终止 TLS,因此它能以明文看到你的流量 —— 这个取舍后面再细说)

前置条件

  • 一个托管在 Cloudflare 的域名(免费方案即可)
  • 一个启用了 Zero Trust 的 Cloudflare 账号(最多 50 用户也是免费)
  • 服务器上运行 cloudflared(Docker 或原生安装)

开始配置

1. 创建隧道

进入 Cloudflare Zero Trust 控制台 → Networks → Tunnels → Create a tunnel。

给它起个名字(比如 homeserver),Cloudflare 会生成一个隧道 token。这个 token 就是你的服务器用来认证的凭证。

Cloudflare 控制台中的隧道创建和 token 生成界面
在 Cloudflare Zero Trust 中创建隧道 —— 生成的 token 就是服务器需要的全部内容。(截图里 token 在 docker 命令的右侧)

2. 运行 cloudflared

最简单的方式 —— 一个 Docker 容器:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: always
    command: tunnel run
    environment:
      - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
    networks:
      - homeserver

在 .env 文件里设置你的 token:

CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiYWJjZGVm...

就这样。docker compose up -d,隧道就上线了。

3. 添加公网主机名

回到 Cloudflare 控制台,为每个服务添加路由。每条路由把一个子域名映射到一个内部服务:

公网主机名服务URL
landing.yourdomain.comHTTPhttp://homepage:3000
books.yourdomain.comHTTPhttp://kavita:5000
ai.yourdomain.comHTTPhttp://open-webui:8080

或者,你也可以把一个泛域名子域指向一个反向代理(比如 Nginx),由它根据主机名做路由。我就是这么做的 —— 所有流量都进 cloudflared,再转发给 Nginx,Nginx 路由到正确的容器。这部分后面再讲。

Cloudflare 控制台中的公网主机名配置界面
每个子域名都路由到反向代理 —— 剩下的交给 Nginx。

架构总览

完整流程是这样的:

互联网
    │
    ▼
Cloudflare Edge(SSL 终止、DDoS 防护)
    │
    ▼(加密隧道,仅出站)
cloudflared 容器
    │
    ▼
reverse-proxy(Nginx,端口 8000)
    │
    ├── landing.* → homepage:3000
    ├── books.* → kavita:5000
    ├── ai.* → open-webui:8080
    ├── pdf.* → stirling-pdf:8080
    └── ...(12+ 个服务)

所有容器都在同一个 Docker 网络(homeserver)里,所以 Nginx 可以通过容器名访问它们。

对于没有自带认证的服务(比如我的主页仪表盘和开发工具),我用 Cloudflare Access 策略。这会在隧道到达你服务器之前,先加一道登录关卡:

  • 进入 Zero Trust → Access → Applications
  • 添加一个 Self-hosted application
  • 设置域名(比如 landing.yourdomain.com)
  • 添加策略:允许匹配你自己邮箱的邮件,一次性 PIN 验证

这样一来,即使有人猜到了你的子域名,也会在触达任何东西之前先撞上 Cloudflare 的登录界面。

Cloudflare Access 策略配置
Cloudflare Access 加了一道登录关卡 —— 加载任何内容之前,先给邮箱发一次性 PIN。
把 Cloudflare Access 策略分配给应用
把 Cloudflare Access 策略分配给应用 —— 确保只有授权用户才能访问该服务。

成本

  • Cloudflare 账号:免费
  • 隧道:免费(不计量带宽,不过 Cloudflare 免费方案条款不鼓励大量视频/文件下载)
  • 域名:约 10 美元/年
  • Zero Trust(最多 50 用户):免费

总计:一个域名的钱。

为什么不用 Tailscale?

这个问题经常被提出来。Tailscale 是另一个不用端口转发就能访问家庭服务的流行方案。Tailscale 在你的设备之间建立一个私有的、基于 WireGuard 的网状网络,所以你不需要公开暴露服务,也不需要跑公网反向代理。

Tailscale 很适合这些情况:

  • 你主要需要从自己的设备访问(设备间的网状 VPN)
  • 你想要一个配置最少的私有网络
  • 你不需要自定义域名(Tailscale Funnel 给你的是 *.ts.net URL,不是你自己的域名,而且只限于 443、8443、10000 端口,仅 TLS,带宽限制不可配置)

我选 Cloudflare Tunnels 是因为:

  • 我想要带自定义子域名的公网 URL —— 分享我的 Kavita 书库链接,或者让别人不用装任何东西就能访问某个服务
  • Cloudflare 免费负责 SSL、DDoS 防护和缓存
  • Zero Trust 访问策略让我能把特定服务挡在基于邮箱的登录后面(访问设备上不需要装 VPN 客户端)
  • 它和我其他一切在用的同一个域名配合

取舍很清楚:Tailscale 更简单、更私密;Cloudflare Tunnels 给你带精细访问控制的公网服务。如果你不需要公网访问,Tailscale 是更省事的答案。我同时需要公网和私有两种访问模式,所以 Cloudflare 更合适。

你也可以两个都用 —— Tailscale 做管理访问(SSH、MeshCentral),Cloudflare 做面向用户的服务。很多家庭实验室玩家 apparently 就是这么干的。

前后对比

用 Cloudflare Tunnel 之前:

  • 服务只能在家庭 WiFi 里访问

之后:

  • 每个服务都能从任何地方用干净的 URL 访问
  • SSL/TLS 自动处理
  • 不暴露端口,不需要静态 IP
  • 暴露一个新服务只要 30 秒

接下来

在我实际的折腾过程中,接下来配置的是 MeshCentral 用于远程访问(因为当时我每次还得插显示器)。但在这个系列里,我会按逻辑顺序来 —— 一层一层搭建基础设施,让你不用经历我做过的那些重构和调整。

THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容