利用 Cloudflare Tunnel 与 WebSocket 入站实现内网服务的零公网 IP 暴露

举报
yd_242888859 发表于 2026/10/04 14:57:39 2026/10/04
【摘要】 整套方案零公网 IP、零端口暴露,所有流量都走 TLS 加密的 Cloudflare 网络,适合自托管面板、家庭 NAS 服务等场景。最大的心得是:**"进程在跑"不等于"服务可用",端口监听验证和分层 curl 探测是排障的两个基本功**。希望这篇分享能帮你少踩几个坑。

## 前言

在日常开发和自托管场景中,我们经常遇到这样的问题:手头有一台没有公网 IP 的机器(家宽 NAS、校园网服务器、云上容器等),想把它上面的服务安全地暴露出去,又不想在路由器上开端口转发。本文分享我实践过的一套方案:**Cloudflare Tunnel(Argo)+ WebSocket 类入站代理**,实现零公网 IP、零端口开放的服务暴露,并记录了排查"隧道通但业务不通"问题的完整思路。

## 一、整体架构

```
用户客户端
   │  TLS
   ▼
Cloudflare Edge(域名解析,代理开启)
   │  WSS 加密隧道(cloudflared 主动外连,无需入站端口)
   ▼
本机 cloudflared 进程
   │  HTTP 回源 localhost:PORT
   ▼
服务端应用(支持 WebSocket 路由分发)
```

核心思想:**让云端主动"出站"连接 Cloudflare**。cloudflared 与边缘节点建立长连接后,所有访问指定域名的流量都会通过这条隧道回源到本机,防火墙只需要允许出站 7844 端口即可。

## 二、实施步骤

### 1. 创建隧道

在 Cloudflare Zero Trust 控制台创建 Tunnel,得到 token(或 TunnelSecret JSON),在服务器上运行:

```bash
cloudflared tunnel --no-autoupdate --protocol http2 run --token <TOKEN>
```

然后把域名(如 `app.example.com`)绑定到该隧道,回源地址填 `http://localhost:8001`。

### 2. 服务端多协议复用一个端口

为了在一台机器、一个隧道端口上承载多种业务路由,我用了一个带 fallback 的入站设计:

- 主入站监听 `8001`,按路径分发:
  - `/vless-argo` → 内部 3002(WebSocket)
  - `/vmess-argo` → 内部 3003(WebSocket)
  - `/trojan-argo` → 内部 3004(WebSocket)
  - 其他流量 → 直接回落 3001

这样 Cloudflare 回源只打一个端口,内部路由互不干扰,运维上非常清爽。

### 3. 客户端配置要点

以 vmess + ws + tls 为例,客户端关键参数:

| 参数 | 值 |
|------|-----|
| 地址 | 优选域名/IP(如 saas 类优选域名) |
| 端口 | 443 |
| 传输 | WebSocket |
| path | `/vmess-argo?ed=2560`(ed 参数启用 0-RTT) |
| Host/SNI | 隧道域名 |

**注意**:优选域名解析到的是 Cloudflare 边缘节点,不是你的服务器;真正的回源靠 SNI + Host 匹配到隧道。

## 三、踩坑记录:隧道通、节点不通

这是最有价值的一部分。我遇到过控制台显示连接器在线、`curl https://隧道域名/` 也有响应,但实际业务握手失败的情况。排查思路如下:

### 1. 先分层定位

```bash
# ① 隧道层:本地回源是否正常
curl -i -H "Upgrade: websocket" -H "Connection: Upgrade" \
     -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
     -H "Sec-WebSocket-Version: 13" http://127.0.0.1:8001/vmess-argo
# 期望 101

# ② 公网层:走域名测试
curl -i --http1.1 -H "Upgrade: websocket" ... https://app.example.com/vmess-argo
```

- ① 通、②不通 → 问题在 Cloudflare 回源配置(回源端口、ingress 规则)
- ① 就不通 → 问题在本机服务(大概率是入站没起来)

### 2. 本机入站"静默失败"的经典陷阱

服务端二进制用 `nohup app -c config.json >/dev/null 2>&1 &` 启动,**如果 8001 被其他进程占用,xray/核心会启动失败,但日志被吞掉了**,主进程还活着,看起来一切正常。

解决:启动后必须验证端口真的在监听:

```bash
ss -tlnp | grep -E ':(8001|3001|3002|3003|3004)'
```

我在脚本里加了预检逻辑:启动前用 Node.js 逐端口尝试绑定,被占用直接报错提示换端口;启动后轮询探测端口进入 LISTEN 状态才继续。自此再没被这个问题坑过。

### 3. 530 vs 502 的含义

- **530**:隧道本身没连上(cloudflared 进程死了或 token 失效)→ 查 cloudflared
- **502**:隧道通,但回源失败(服务没监听 / 端口不对)→ 查本机服务和回源配置

记住这两个码,排查速度能快一倍。

## 四、稳定性建议

1. **固定隧道 + 固定回源端口**:临时隧道(trycloudflare.com)域名每次重启都变,订阅类场景务必用固定隧道。
2. **子进程看护**:cloudflared 和业务核心用 systemd 或 pm2 托管,并配置崩溃自动重启。
3. **高位端口防冲突**:回源端口选 50000+ 的高位端口,避开容器环境常见的 8000/8080。
4. **WebSocket 路径加随机化**:路径里带一段随机串,减少被扫描器探测的概率。

## 结语

整套方案零公网 IP、零端口暴露,所有流量都走 TLS 加密的 Cloudflare 网络,适合自托管面板、家庭 NAS 服务等场景。最大的心得是:**"进程在跑"不等于"服务可用",端口监听验证和分层 curl 探测是排障的两个基本功**。希望这篇分享能帮你少踩几个坑。

---
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。