欢迎光临

VPS 上用 Cloudflare Tunnel 实现内网穿透:无需公网 IP、零暴露端口的零信任访问方案

在传统的 VPS 运维场景中,我们要把内部服务暴露到公网,通常需要开放端口、配置防火墙规则、申请 SSL 证书、设置反向代理——这一套流程不仅繁琐,而且每多开一个端口就多一份被攻击的风险。如果你家里有 NAS、树莓派,或者公司内网有开发环境需要远程访问,但又不希望把服务直接挂到公网上,Cloudflare Tunnel 就是目前最优雅的解决方案之一。

本文将从零开始,在 VPS 和内网机器上部署 Cloudflare Tunnel,实现 HTTPS 自动配置、多服务路由、访问策略控制和故障恢复,帮助你构建一套生产可用的零信任访问架构。

Cloudflare Tunnel 架构示意

什么是 Cloudflare Tunnel

Cloudflare Tunnel(原名 Argo Tunnel)是 Cloudflare 提供的一种反向隧道技术。它的核心思路是:在你的服务器上运行一个轻量级的守护进程

1
cloudflared

,由这个进程主动连接 Cloudflare 边缘网络,建立一条加密隧道。所有外部请求先到达 Cloudflare 边缘节点,再通过这条隧道转发到你的内部服务。

与传统的端口映射或 frp/ngrok 等内网穿透工具相比,Cloudflare Tunnel 有几个显著优势:

  • 无需开放任何入站端口:服务器不需要监听公网端口,所有连接都是出站的,防火墙可以完全关闭入站
  • 自动 HTTPS:Cloudflare 自动处理证书签发和续期,你不需要配置 Let’s Encrypt 或购买证书
  • 隐藏源站 IP:外部用户看到的是 Cloudflare 的 IP,你的真实服务器 IP 永远不暴露
  • DDoS 防护内置:流量经过 Cloudflare 边缘网络,自动获得企业级 DDoS 防护
  • 访问策略控制:可以基于 IP、邮箱、SSO 身份等条件限制谁能访问

工作原理简述

整个数据流如下:


1
2
3
4
用户浏览器 → Cloudflare 边缘节点(Anycast)
  → 加密隧道(QUIC/HTTP2)
  → 你的服务器上 cloudflared 进程
  → 本地服务(localhost:port)
1
cloudflared

启动后会向 Cloudflare 的 4 个边缘 IP 发起出站连接(默认走 7844 端口的 QUIC 协议),建立多条持久连接以实现负载均衡和故障切换。即使某条连接断开,流量也会自动切换到其他连接,整个过程对用户透明。

环境准备与安装

前提条件

开始之前,你需要准备以下内容:

  • 一个 Cloudflare 账户(免费版即可,Tunnel 功能免费可用)
  • 一个已托管在 Cloudflare 的域名(NS 指向 Cloudflare)
  • 一台 VPS 或内网机器(Linux x86_64 或 ARM)
  • 需要暴露的本地服务(如 Web 应用、SSH、数据库等)

安装 cloudflared

在 Debian/Ubuntu 系统上,使用官方 APT 源安装:


1
2
3
4
5
6
7
8
9
10
11
# 添加 Cloudflare GPG key 和 APT 源
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | \
  sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null

echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | \
  sudo tee /etc/apt/sources.list.d/cloudflared.list

sudo apt-get update && sudo apt-get install -y cloudflared

# 验证安装
cloudflared --version

在 CentOS/RHEL 系统上,使用 yum 源:


1
2
sudo rpm -ivh https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-cloudflare-x86_64.rpm
cloudflared --version

也可以直接下载二进制文件,适合不想加源的场景:


1
2
3
4
5
6
7
# x86_64
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
sudo mv cloudflared-linux-amd64 /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared

# ARM64(树莓派等)
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64

创建隧道并暴露第一个服务

Cloudflare 提供两种方式创建隧道:命令行交互模式和 Dashboard 配置模式。推荐使用命令行模式,更灵活且便于自动化。

登录认证

首先进行认证,将

1
cloudflared

与你的 Cloudflare 账户绑定:


1
cloudflared tunnel login

执行后会输出一个 URL,在浏览器中打开它,选择你要使用的域名并授权。授权成功后,会在

1
~/.cloudflared/

目录下生成一个

1
cert.pem

文件,这是后续创建隧道的凭证。

创建隧道


1
2
3
4
5
# 创建一个名为 my-tunnel 的隧道
cloudflared tunnel create my-tunnel

# 输出示例:
# Created tunnel my-tunnel with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

创建成功后,会生成一个 UUID 格式的隧道 ID,同时在

1
~/.cloudflared/

下生成对应的凭证文件

1
<UUID>.json

。这个文件非常重要,是隧道身份的唯一凭证。

编写配置文件

创建

1
~/.cloudflared/config.yml

配置文件,定义路由规则:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ~/.cloudflared/config.yml
tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /root/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json

ingress:
  # 前端应用
  - hostname: app.example.com
    service: http://localhost:3000

  # API 服务
  - hostname: api.example.com
    service: http://localhost:8080

  # Grafana 监控面板
  - hostname: grafana.example.com
    service: http://localhost:3001

  # 兜底规则(必须放在最后)
  - service: http_status:404

每条 ingress 规则定义了一个域名到本地服务的映射。

1
hostname

是对外暴露的域名,

1
service

是本地服务的监听地址。最后必须有一条没有 hostname 的兜底规则。

配置 DNS 记录

使用以下命令自动在 Cloudflare DNS 中创建 CNAME 记录,指向隧道:


1
2
3
4
# 为每个 hostname 创建 DNS 记录
cloudflared tunnel route dns my-tunnel app.example.com
cloudflared tunnel route dns my-tunnel api.example.com
cloudflared tunnel route dns my-tunnel grafana.example.com

这个命令会自动在 Cloudflare DNS 中创建一条 CNAME 记录,指向

1
<UUID>.cfargotunnel.com

。你不需要手动去 Dashboard 添加 DNS 记录。

启动隧道


1
2
3
4
5
# 前台运行(测试用)
cloudflared tunnel run my-tunnel

# 后台运行
cloudflared tunnel --config ~/.cloudflared/config.yml run my-tunnel &

启动后,访问

1
https://app.example.com

就能看到你的本地服务了,而且自动带有 HTTPS。此时你的服务器没有开放任何入站端口——所有流量都是通过出站隧道进来的。

隧道流量转发示意

使用 Docker Compose 部署

在生产环境中,推荐用 Docker Compose 管理

1
cloudflared

,这样配置版本可控、重启自动恢复。首先准备好配置文件:


1
2
3
4
# 目录结构
mkdir -p ~/cloudflared/config
cp ~/.cloudflared/<UUID>.json ~/cloudflared/<UUID>.json
cp ~/.cloudflared/config.yml ~/cloudflared/config/config.yml

编写

1
docker-compose.yml

:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3.8'
services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    command: tunnel --config /etc/cloudflared/config.yml run
    volumes:
      - ./<UUID>.json:/etc/cloudflared/<UUID>.json:ro
      - ./config/config.yml:/etc/cloudflared/config.yml:ro
    environment:
      - TUNNEL_TOKEN=  # 可选:使用 token 模式
    networks:
      - tunnel-net
    # 如果需要访问宿主机上的服务,使用 host 网络或 extra_hosts
    extra_hosts:
      - "host.docker.internal:host-gateway"

networks:
  tunnel-net:
    driver: bridge

在 Docker 环境中,

1
localhost

指向的是容器内部。如果你要代理的服务也跑在 Docker 里,把它们的 service 名称写进去即可(如

1
http://web:3000

);如果服务跑在宿主机上,配置文件里把

1
localhost

改成

1
host.docker.internal

。

启动:


1
2
docker compose up -d
docker compose logs -f cloudflared

进阶配置:服务发现与负载均衡

Cloudflare Tunnel 支持基于 URL 路径的路由和多个后端实例的负载均衡,适合多节点部署场景。

基于路径的路由


1
2
3
4
5
6
7
8
9
10
ingress:
  - hostname: app.example.com
    path: /api/(.*)
    service: http://localhost:8080

  - hostname: app.example.com
    path: /(.*)
    service: http://localhost:3000

  - service: http_status:404

这样

1
app.example.com/api/*

会转发到 API 服务,其余路径转发到前端应用。注意路径规则按从上到下顺序匹配。

多后端负载均衡


1
2
3
4
5
6
7
8
9
10
11
12
ingress:
  - hostname: app.example.com
    service: http://localhost:3000
    originRequest:
      connectTimeout: 10s
      tlsTimeout: 10s
      tcpKeepAlive: 30s
      keepAliveTimeout: 90s
      http2Origin: true
      noTLSVerify: false

  - service: http_status:404

如果需要真正的多后端负载均衡,可以在同一 hostname 下配置多个相同 service(Cloudflare 会轮询),或者使用 Cloudflare 的 Load Balancer 产品(付费功能)。

零信任访问策略

这是 Cloudflare Tunnel 最有价值的功能之一。你可以不依赖传统 VPN,通过访问策略精确控制谁能访问你的内部服务。

创建 Zero Trust 策略

在 Cloudflare Dashboard 中进入 Zero Trust → Access → Applications,添加一个自托管应用:

  1. 选择你的 Tunnel 域名(如
    1
    grafana.example.com

    )

  2. 配置认证策略,选择身份提供商(支持 Google、GitHub、SAML、OTP 邮箱等)
  3. 定义访问规则,如只允许特定邮箱域名、特定 IP 段访问

一个典型的策略配置:只允许公司邮箱且来自办公网络 IP 的请求:

规则字段 操作符 值
邮箱 ends with @yourcompany.com
IP 地址 in range 203.0.113.0/24
国家 is CN

配置后,访问

1
grafana.example.com

会先跳转到 Cloudflare 的登录页面,验证身份后才能进入。服务本身不需要实现任何认证逻辑。

SSH 服务的隧道代理

Cloudflare Tunnel 也能代理 SSH 流量。在配置文件中添加:


1
2
3
4
5
ingress:
  - hostname: ssh.example.com
    service: ssh://localhost:22

  - service: http_status:404

客户端需要配置

1
cloudflared access ssh

作为 SSH 的 ProxyCommand:


1
2
3
4
# ~/.ssh/config
Host ssh.example.com
  ProxyCommand /usr/local/bin/cloudflared access ssh --hostname %h
  User your-user

这样即使你的 VPS 关闭了 22 端口的公网访问,仍然可以通过 Cloudflare 隧道 SSH 连接,而且可以叠加 Zero Trust 认证策略。

运维监控与故障排查

查看隧道状态


1
2
3
4
5
6
7
8
# 列出所有隧道
cloudflared tunnel list

# 查看隧道连接信息
cloudflared tunnel info my-tunnel

# 查看 cloudflared 进程日志
journalctl -u cloudflared -f --no-pager

常见问题排查

问题 1:DNS 解析不生效

检查 Cloudflare Dashboard 中 DNS 记录是否已创建,确认 CNAME 指向

1
<UUID>.cfargotunnel.com

,并且代理状态为橙色云朵(Proxied)。

问题 2:502 Bad Gateway

这通常意味着

1
cloudflared

能接收请求但无法连接到本地服务。检查配置文件中的

1
service

地址是否正确,以及本地服务是否正常运行:


1
2
3
4
# 在服务器上测试本地服务是否可达
curl -I http://localhost:3000
# 如果返回 200 说明本地服务正常,问题在配置文件
# 如果连接拒绝说明服务没启动或端口不对

问题 3:QUIC 连接超时

某些网络环境会封锁 UDP 7844 端口(QUIC 默认端口),可以强制使用 HTTP/2 回退:


1
2
3
# 在 config.yml 或启动参数中设置
# 启动时加 --protocol http2
cloudflared tunnel --protocol http2 run my-tunnel

或者在 config.yml 中全局设置:


1
protocol: http2

设置为系统服务


1
2
3
4
5
6
7
# 安装为 systemd 服务
sudo cloudflared service install

# 管理服务
sudo systemctl start cloudflared
sudo systemctl enable cloudflared
sudo systemctl status cloudflared

安装为系统服务后,配置文件路径变为

1
/etc/cloudflared/config.yml

,凭证文件也需要复制到

1
/etc/cloudflared/

目录下。

系统监控与运维

安全加固建议

虽然 Cloudflare Tunnel 本身已经提供了相当好的安全基线,但在生产环境中仍有几点值得额外加固:

  • 关闭所有入站端口:既然所有流量都走隧道,防火墙可以只允许出站。用
    1
    ufw default deny incoming && ufw default allow outgoing

    设置

  • 限制 cloudflared 凭证文件权限:
    1
    chmod 600 ~/.cloudflared/*.json

    ,防止其他用户读取

  • 启用 Cloudflare WAF 规则:在 Dashboard 中开启防火墙规则,拦截常见攻击模式
  • 配置速率限制:防止暴力枚举和 CC 攻击,在 Cloudflare 侧配置 Rate Limiting Rules
  • 定期更新 cloudflared:
    1
    cloudflared update

    命令一键更新到最新版本

  • 监控隧道断连:使用 Cloudflare 的 Notification 功能,在隧道离线时发送邮件或 Webhook 告警

成本与限制分析

Cloudflare Tunnel 的免费版对个人用户和中小项目完全够用:

项目 免费版 说明
隧道数量 无限制 可创建任意数量的隧道
带宽 无明确限制 Cloudflare 不会硬性限制,但异常流量可能被审查
DNS 记录数 1000 条 对绝大多数场景绑绑有余
Zero Trust 用户数 50 人 免费版最多 50 个 Access 用户
并发连接 无硬性限制 Cloudflare 建议单隧道不超过 1000 并发

如果需要更高规格的 SLA、更多 Zero Trust 用户、日志审计等功能,可以考虑 Cloudflare 的付费计划。但对于个人博客、小型团队内部工具、自托管服务等场景,免费版完全足够。

与 frp、ngrok 的对比

很多开发者可能已经在用 frp 或 ngrok 做内网穿透,下面做一个横向对比帮助你决定是否迁移:

特性 Cloudflare Tunnel frp ngrok
需要自有服务器 否 是(需公网 VPS) 否(但免费版限制大)
自动 HTTPS 是 否(需自己配证书) 是
隐藏源站 IP 是 否(流量直达 VPS) 是
DDoS 防护 内置企业级 无 无
访问策略控制 内置 Zero Trust 无(需自行实现) 付费版有
免费版带宽 充足 取决于 VPS 受限
自建依赖 无 需维护 frps 服务端 无

总体来说,如果你已经在用 Cloudflare 做 DNS,迁移到 Tunnel 几乎零成本。如果你目前在用 frp 且主要痛点是需要一台公网 VPS 作为中转,Cloudflare Tunnel 可以帮你省掉这台中转机器的费用和维护成本。

总结

Cloudflare Tunnel 把内网穿透、HTTPS 证书、DDoS 防护、访问控制这几件原本需要独立配置的事情整合到了一个方案里,而且核心功能免费。对于 VPS 用户来说,它特别适合以下场景:

  • 家庭实验室或内网服务需要外网访问,但没有公网 IP
  • VPS 上部署了多个服务,希望统一管理入口并隐藏真实 IP
  • 需要给团队成员提供安全的远程访问,但不想搭建传统 VPN
  • 想彻底关闭服务器入站端口,降低攻击面

部署完成后,建议你做一次完整的故障演练:手动停止

1
cloudflared

进程,观察服务是否自动恢复;检查 Cloudflare 是否在隧道离线时发送了告警通知。这些验证能确保在真正的网络故障发生时,你的服务能快速恢复。

如果你有多个地理位置分散的服务节点,还可以结合 Cloudflare 的 Load Balancer 和 Health Check 功能实现跨地域的高可用调度,这会是另一个值得深入探索的方向。

【本站文章皆为原创,未经允许不得转载】:汤不热吧 » VPS 上用 Cloudflare Tunnel 实现内网穿透:无需公网 IP、零暴露端口的零信任访问方案
分享到: 更多 (0)