坑边闲话:中国的网络建设人为地施加了很多限制,比如没有公网 IP,比如封锁 80、443、445 等常用端口,比如将上行限制到一个低到离谱的速率。别问,问就说是防止电信诈骗!然而,在某些正经需求面前,端口被封锁还真的玩不转。何解?且看本文!

1. 中国网络的局限性·

在传统的存储、网络、计算三元组中,存储和计算一般都是在内网中部署,网络可分为内网和外网两个部分,彼此的架构完全不同。外网一般看重的是

  • 路由全球可达性
  • 上下行速率
  • 1000 以下的端口可用性

很遗憾,中国在这三个方面可谓是遥遥落后:

  • 我们无法访问国际互联网,国际互联网要想访问中文互联网的资源也面临相当的门槛;
  • 中国民用宽带上行速率极差,千兆宽带配 30Mbps 上行非常普遍;
  • 常规端口被封锁,导致无法发布 web 服务。

如果想要托管 web 服务器如私人 blog,则只能选择 GitHub pages 静态发布或购买昂贵的云服务器。然而,赛博菩萨 Cloudflare 提供了一种神奇的解决方案,可以让你在没有 web 端口甚至是没有公网 IP 的情况下,自由发布自己的 web 服务。

2 Cloudflare Tunnel 服务简介·

2.1 Cloudflare Tunnel 工作原理·

图 1. Cloudflare Tunnel 服务发布之后的访问原理图。可见,用户的服务通过隧道,与 Cloudflare 的云服务器建立了连接,此外用户需要将 DNS 解析到 Cloudflare.

如图 1 所示,服务的用户在访问 Cloudflare Tunnel 后面的服务时,经历了如下过程:

  • 首先服务的客户端对服务的域名进行 DNS 解析,由于服务的域名注册在 Cloudflare,因此所得到的地址是 Cloudflare CDN,两者之间通信延迟极低;
  • 用户发送 HTTP 请求给 CDN,CDN 发现该域名已经注册到了 Tunnel 解析,因此 Cloudflare 会主动通过 Tunnel 与服务的托管主机进行沟通,获取服务。
    • 注意:如图 2 所示,在 Cloudflare 的域名解析系统里,用户的域名可以解析到 A、AAAA、CNAME 等常规记录,也可解析到 Cloudflare Tunnel. 当域名解析到某个 Tunnel(以哈希值表征,如 66b2558c-3424-47e2-9680-95b0979985f7.cfargotunnel.com)之后,才进行资源获取。
    • 注意:Tunnel 是资源托管服务器主动与 Cloudflare 建立的,因为托管服务器没有公网可达性,因此 Cloudflare 不能主动进行资源获取。由此可见,当一个服务器连接到 Tunnel 时,自己首先必须要有网络连接能力。
  • Cloudflare 通过隧道获取 HTTP 资源后,通过 Cloudflare 内部的多节点网络回传到用户连接到的 CDN,最终完成资源提供。

图 2. 在 Cloudflare Tunnel 里注册服务之后,对应的一级域名解析记录里会出现对应的不常见解析结果,此即隧道类型解析。

2.2 Cloudflare Tunnel 的优势与本质·

Cloudflare Tunnel 是一种将 CDN 技术与隧道技术结合起来的伟大发明,它有以下好处:

  • Cloudflare 只需要进行转发,不需要像阿里云、腾讯云服务器那样托管用户的服务,因此 Cloudflare 这项服务可以免除存储、计算成本,惠及普通用户
  • 降低服务的维护成本。服务器后端不再需要公网 IP,可以节约一部分宽带费用;或者将原有的这部分成本支付给更高级的 Tunnel 服务;
  • 服务器后端不用再关心 DDoS 等攻击。这部分防护工作全部交给 Cloudflare 即可;
  • Cloudflare 可以提供 Let’s Encrypt 证书。由于 HTTP 服务的特性,可以被中间添加 HTTPS 加密层,所以 Cloudflare 的这项特性使得用户免于手动申请证书。毕竟短期 Let’s Encrypt 证书也仅仅是校验 DNS 解析可用性,Cloudflare 本身就是 DNS 解析提供商,自己给自己校验自然是轻松愉快;
  • Cloudflare 的 CDN 技术能缓存相当多的资源,使得静态服务的响应变得前所未有的快!

现在我们简单复盘一下,Cloudflare Tunnel 本质上就是一种将外部网络与其他两项(存储、计算)解耦的技术。原先三元组必须要集中在一个机房(或者通过某些专线技术聚合在一起的分布式机房),而现在,存储、计算与外部网络可以分离开。现如今这种趋势非常明显,多年前以 AWS S3 为代表的 OSS (Object Storage Service) 等对象存储服务已经能够将部分的存储占用及存储获取流量转移给专业的服务商。

简而言之,专业的人,做专业的事。

3 Cloudflare Tunnel 服务的搭建·

首先,用户需要花点钱买个 Cloudflare 运营的域名,或者将原有的域名迁移到 Cloudflare. 毕竟 CDN 与域名解析是分不开的。注意,购买 Cloudflare 域名需要使用国际信用卡。

其次,用户要在 Cloudflare 的零信任服务中添加一个 Tunnel 并让对应的服务器连接到 Tunnel,随后通过下列命令安装客户端并建立连接:

1
2
3
4
5
6
7
# 安装 cloudflared 客户端
cd ~
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo apt install ./cloudflared-linux-amd64.deb

# 连接到在 Cloudflare Dash 创建的隧道,记得将 <SECRET> 替换
sudo cloudflared service install <SECRET>

随后,检查 Cloudflare Dash,查看是否有一个新的 connector 连接到了 tunnel. 如果有,就说明这个 connector 已经成功添加到了隧道服务,该 connector 的服务可以被配置到公网。

最后,在 Dash 页面,配置合适的服务,即可将服务发布出去。不过要注意,只有 HTTP/HTTPS 服务可以较为简单地发布,如果要使用 TCP 服务(如远程桌面、ssh 等),则需要做更多的设置。由于笔者暂时不需要 TCP 服务,所以这里仅介绍 HTTP/HTTPS.

图 3. 设置合适的 hostname 之后,如图 2 所示,新的解析记录将自动被添加。同时,服务的用户也可以通过这里的 public hostname 进行服务获取。

图 4. 在每一个 Public Hostname 详情中,可设置被外部用户访问的 URL(即服务的域名,如 service.cftunnel.com)和内网地址(即 connector 所能访问到的地址,如 http://localhost:8080/ 或者 http://192.168.80.1:8080/)

特别注意:如果内网服务是 HTTPS 服务,而且这项服务没有合规的证书(如仅有自签名证书),那么就需要在 Additional application settings 进行额外设置,如图 5 所示:

图 5. 内网中的服务一般是 HTTP 明文,但是也有少数服务仅支持 HTTPS,如果不愿意修改服务配置,则需要使能 No TLS Verify 选项。

如此,即可实现 HTTP/HTTPS 服务的对外发布!

4. 微服务背景下的设置·

现在微服务容器化运维已经无孔不入,因此常规部署方案并不适合微服务模型。为此我们引入微服务背景下的 Cloudflare Tunnel 设置。

可参考如下 docker-compose.yml 配置搭建 cloudflared 容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Author: Peng Liu
# Email: [email protected]
# Created: 2024. May. 20
# Updated: 2024. Jul. 16
# Official Release: https://hub.docker.com/r/cloudflare/cloudflared

services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
hostname: cloudflared
stdin_open: true
tty: true
command:
[
"tunnel",
"--no-autoupdate",
"--edge-ip-version", "6",
"--protocol", "http2",
"run",
"--token", $TUNNEL_TOKEN
]
restart: unless-stopped
networks:
- reverse_proxy

networks:
reverse_proxy:
name: reverse_proxy
external: true

注意

务必将上述 Cloudflare Tunnel Token,即 $TUNNEL_TOKEN 改成自己的真实 token. 笔者建议将该 token 写到 .env 文件里并将 .env 文件添加到 .gitignore 中,防止 secret 被泄露

由于我所有的容器服务都处于 reverse_proxy 下,所以将 cloudflared 容器也跑在这个网络下,即可实现通过容器名解析内网地址,彻底告别繁琐的 192.168.0.0/16 内网地址记忆。

以上配置算是老生常谈,区别只是少一个 nginx 服务,多一个 cloudflared 服务,最终效果是一样的。

最佳实践

尽管使用 cloudflared 可以免去连接外网的 Nginx,但是内网使用时再绕一遍 Cloudflare CDN 明显不理智,所以内网独立一套域名解析并解析到内网服务地址是比较明智的。这样一套域名即可完成内外网无缝衔接。

不过我不确定 Cloudflare Tunnel 类型的 DNS 解析能否被 Let’s Encrypt Bot 支持。

总结·

本文详细介绍了 Cloudflare Tunnel 的原理、部署方法以及微服务背景下的最佳实践。

后续有需要,还会介绍 tcp tunnel 服务和 K8S 背景下的 cloudflared.