通过 Cloudflare Tunnel 暴露 Homelab
Source: Dev.to
我热爱自托管。我在自己的服务器上运行博客、仪表盘以及各种随机实验。我不喜欢的则是端口转发、运营商的 NAT、动态 IP,以及把路由器暴露给整个互联网。
幸好,Cloudflare 提供了一个名为 Cloudflare Tunnel 的解决方案。它让我能够在不在路由器上打开任何入站端口的情况下公开暴露服务。本文介绍了我在生产环境中实际使用它的方式。
“传统”自托管的问题
flowchart LR
Internet --> Router
Router --> Server
这意味着:
- 在路由器上打开端口
- 信任防火墙配置
- 希望 IP 不会变化
- 成为软性 DDoS 目标
对于家庭实验室或个人服务器来说,这是一场灾难的配方,也不是可扩展的解决方案。
Cloudflare 隧道方案
使用 Cloudflare Tunnel 时,方向会改变。不是互联网进入我的网络,而是 我的服务器主动发起连接。
flowchart LR
User --> Cloudflare
Cloudflare --> Tunnel
Tunnel --> Server
现在我的服务器仅向 Cloudflare 发起出站连接,Cloudflare 作为公共边缘位于前端。这更加安全:没有开放端口,也不需要公共 IP,从而消除了整类风险。
实际在我的服务器上运行的是什么?
在我的服务器上,我运行一个名为 cloudflared 的小守护进程。
flowchart TB
cloudflared --> CloudflareEdge
CloudflareEdge --> LocalService1
CloudflareEdge --> LocalService2
cloudflared 然后:
- 打开到 Cloudflare 的加密隧道
- 使用我的账户凭证进行身份验证
- 在内部将流量路由到
localhost
因此,我的应用程序从不直接接触公共互联网。
安装 cloudflared
在 Linux 上,安装 cloudflared 大约需要一分钟:
curl -fsSL https://pkg.cloudflare.com/install.sh | sudo bash
sudo apt install cloudflared
快速检查:
cloudflared --version
注意: 通常最好从官方网站下载二进制文件并手动安装;这样可以自行控制更新。
对我的服务器进行身份验证
要对服务器进行身份验证,请运行:
cloudflared tunnel login
浏览器窗口会打开,提示您登录 Cloudflare 账户、选择域名并批准连接。凭证会本地存储在 ~/.cloudflared/cert.pem 中,以供后续隧道使用。
创建隧道
创建隧道的命令:
cloudflared tunnel create
Cloudflare 会返回隧道的 UUID 和一个用于下载配置文件的 URL。将该文件保存为 ~/.cloudflared/.json。此文件是服务器的身份标识。
我的隧道配置(示例)
配置文件的简化版本:
tunnel:
credentials-file: ~/.cloudflared/.json
ingress:
- hostname: blog.example.com
service: http://localhost:8080
- hostname: dashboard.example.com
service: http://localhost:8081
- service: http_status:404
这将创建一个隧道,将多个主机名路由到服务器上的不同服务,并提供安全的回退(http_status:404)。
流量端到端的传输方式
完整示意图:
flowchart LR
User --> DNS
DNS --> Cloudflare
Cloudflare --> Tunnel
Tunnel --> ReverseProxy
ReverseProxy --> App
概念示意图:
sequenceDiagram
participant User
participant Cloudflare
participant Tunnel
participant Server
User->>Cloudflare: HTTPS request
Cloudflare->>Tunnel: Forward request
Tunnel->>Server: Local HTTP
Server-->>Tunnel: Response
Tunnel-->>Cloudflare: Encrypted response
Cloudflare-->>User: HTTPS response
从外部看,它表现得像普通的 HTTPS 连接,但流量是端到端加密的,在服务器上表现为 localhost 流量。
DNS: 无需手动记录
Cloudflare Tunnel 自动创建所需的 DNS 记录。它会添加一个 CNAME,将你的域指向 Cloudflare 的边缘。运行:
cloudflared tunnel route dns
…就这样!其余由 Cloudflare 处理。
永久运行隧道
首先测试隧道:
cloudflared tunnel run
当它工作正常时,将其安装为系统服务:
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
现在它会在启动时自动启动,失败时会重新启动,并以非 root 用户运行。
添加身份验证
对于任何私有的(管理员面板、仪表盘),我会添加 Cloudflare Access 策略,以在请求到达隧道之前要求身份验证。这确保只有授权用户才能访问这些服务。
Cloudflare Access
Which lets me:
- 要求登录后才能访问服务
- 限制电子邮件或身份提供商
- 在不使用 VPN 的情况下保护内部工具
So from the outside, my dashboard looks public, but in reality it’s locked behind authentication.
我的生产环境设置
这是我实际推荐的布局:
flowchart LR
User --> Cloudflare
Cloudflare --> Tunnel
Tunnel --> ReverseProxy
ReverseProxy --> Blog
ReverseProxy --> Dashboard
ReverseProxy --> Admin
为什么它这么好用?
Cloudflare 负责边缘安全,而我的反向代理负责路由。应用保持在内部且“傻瓜化”,并且没有任何入站端口。
我曾犯的常见错误(让你免于重蹈覆辙)
- 直接暴露应用而不是通过反向代理
- 忘记添加 404 ingress 规则
- 在没有 Access 的情况下运行管理面板
- 不必要地将服务绑定到
0.0.0.0而不是localhost
经验法则:
如果不打算公开,就保持在localhost。如果需要公开,则使用 Cloudflare Access.
最终思考
虽然 Cloudflare Tunnel 是一个强大的工具,但它并非万灵药。它只是拼图中的一块。将其与其他安全措施(如 Cloudflare Access)结合使用,以确保服务器安全。
不要将其用于以下服务:
- 对延迟敏感的服务
- 游戏服务器
- 完整网络访问(我使用 WireGuard 实现)
然而,对于 HTTP(S) 应用,它是我的默认选择。对于个人服务器和家庭实验室,这是目前可以运行的最简洁的设置之一。