欢迎光临

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

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

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

什么是 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)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址