内网穿透的应用场景
内网穿透大家都不陌生,zerotier,Tailscale等都用过,但缺点就是不能开箱即用,往往需要安装专用的客户端,一对一登录授权,不够“便携性”,流量中转的服务器需要花钱花精力维护,又或者客户端故障。特别是跨国内网的情况下,中转链路就变得更为复杂。而cloudflared的场景更适合中心化的穿透场景,在一个内网部署cloudflared,就可以便携地在任意终端快速通过域名连接你的应用,而你不用关心链路的问题————当然,在境内的速度可用性会降低,但对于个人小应用也是足够的,后面会讲到代理的问题。
下载安装地址
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/#linux
Cloudflared提供了直接的二进制,可以直接下载运行,虽然二进制有点大38M左右,对嵌入式设备不太友好,好在不用配置什么软件源。
AMD64/x86
ARM64
Cloudflared也提供了简单的更新命令:执行cloudflared update就会更新自己。
当然,官方也提供了docker镜像,方便后续维护升级(升级就是更新docker pull镜像就可以)。
docker compose示例:
如果你想用软件源安装方便后续更新,可以使用 https://pkg.cloudflare.com/index.html ,但只支持有限的几种发行版。
场景1 把内网的web服务暴露到公网
适合家里没有公网IP,或者买了个无公网IP的nat鸡,想把自己的web服务放到公网直接浏览器在任意地方都可以访问的。
你只需要在内网任意一台设备上运行部署一次cloudflared,你就可以在Cloudflare后台随意设置暴露哪些内网服务到公网,即使服务不是运行在那台设备上而是在局域网的其他设备上,相当于你的内网和cloudfalre打通了。
前提条件:Cloudflare账号,一个托管在Cloudflare的域名。
推荐上面的docker compose部署。
传输路径:安装了Cloudflared的设备-> Cloudflare Tunnels -> Cloudfare EDGE(用户浏览器)
步骤1 首先添加Tunnels
界面可能随时间变化,所以不截图了,找不到可以点左侧顶部的的菜单搜索:
仪表板后台: https://dash.cloudflare.com/
不要点进去Zero Trust,那里面给的东西和命令提示都是大坑,忽悠你装各种图形One客户端,都是一些用不到的功能和迷惑你。
直接在左侧分类Protect & connect(保护和连接),点击展开左侧菜单Networking(联网),点进去Tunnels
可以查看和创建Tunnel,点右上角Create Tunnel(创建隧道)
输入Tunnel名字,比如Myhome
确认名字后会给出连接Tunnel的token,直接用token就可以连接,命令行如下:
| |
当你连上了之后,就可以确认并创建tunnel。
点击你的tunnel的名字,就可以看到状态是否健康和uptime,点击Connectors的ID,就可以看到它目前连接了哪些数据中心(一般会同时连接一个地域里面四个数据中心),源IP,版本等。
步骤2 Routes发布Web应用
这是最轻松的一集,只要tunnel建好了,后续就简单了,随时随地在后台调整你需要发布的Web应用。
切换到【路由】Routes标签页,点击右上角的【添加路由】Add route,选择【添加已发布的应用程序】Published application,输入一个【子域名】Subdomain,然后在【服务URL】Service URL那里直接输入你内网的URL即可,创建完成你就可以直接用你的子域名访问你的内网Web服务了,还能随时随地在Cloudflare后台任意映射任意修改。
另外关于https证书,Cloudflare会自动生成,对于完全托管在Cloudflare的域名来说无需手动干预,但如果你是通过某种早期的歪门邪道的cname方式接入,那么可能每三个月需要手动改一下dns的txt记录来验证证书,这个Cloudflare会给你发邮件提示你添加什么样的txt记录,因为Cloudflare是用的DNS验证而不是http挑战,虽然不知道为啥不采用http验证,大概也是没考虑到这些歪门邪道的用户或者DNS验证实施起来更简单。
题外话:多个副本(Replicas)
副本 ID=Replica ID=Connector ID(Zero Trust界面的叫法),中文界面就显示副本 ID,英文界面就显示Replica ID,隧道 ID=Tunnel ID
你还注意到你的Tunnel页面有显示副本(Replicas),还有Add a replica(添加副本)的按钮。不得不说Cloudflare的概念太迷惑,这个在Zero Trust里面叫Connector,场景是这样的:
场景:你在 服务器 A 和 服务器 B 上用同一个 Tunnel Token 启动了两个 cloudflared。
效果:如果 服务器 A 突然断网或挂掉,Cloudflare 边缘节点会秒级自动把流量切到 服务器 B 的 Connector(replica) 上,访问者完全感觉不到服务中断。具体的一种应用场景:你可以用同一个token起两个进程,一个直连,一个走代理,代价是cloudflare并没有确定的算法一定分配到哪个副本,一般优先转发到 用户到Cloudflare节点地理上最近 的而不是延迟最低的,比如大多数境内访问默认直连是LAX,即使你代理是HKG,特别是电信用户,大概率可能走绕地球两圈的LAX。
- 什么是 Replica?
添加副本(Replica)的按钮,其实就是给你重新复制一遍那个一模一样的Token,让你在别的机器上跑多一个实例。
- Replica = 同一个 Tunnel UUID 下运行的多个
cloudflared实例(connector) - 用途:高可用、故障切换、无停机更新配置
- 每个 Replica 会向 Cloudflare 建立 4 条出站连接(通常分布在至少两个不同数据中心)
- 所有 Replica 共享相同的路由和配置
- 多 Replica 时如何选择?
- 不支持 流量调度算法(round-robin、hash、加权等)
- 请求到达 Cloudflare Edge 后,优先转发到 地理上最近 的 Replica
- 如果距离计算失败或连接不可用,会重试其他可用连接
- 没有保证最终一定选到某个特定 Replica
官方原文:
By design, replicas do not offer any level of traffic steering (random, hash, or round-robin). Instead, when a request arrives to Cloudflare, it will be forwarded to the replica that is geographically closest. If that distance calculation is unsuccessful or the connection fails, we will retry others, but there is no guarantee about which connection is chosen.
- 数量上限
| 项目 | 限制 | 说明 |
|---|---|---|
| Active Replicas / Tunnel | 25 | 软限制,企业可申请提高 |
| 连接数 / Tunnel | 100 | 25 replicas × 4 连接 |
| Tunnels / Account | 1000 | 软限制 |
- 限制只针对 当前 active(已连接) 的 Replica
- 历史断开的 Connector ID 不占用名额
- Replica / Connector ID 生成规则
- 每次启动
cloudflared进程都会 重新生成一个唯一的 Connector ID(UUID) - 生成依据:cloudflared 版本 + 操作系统/架构 + 功能标志 + 随机 nonce
- 查看方式:
- 仪表盘 → Networking → Tunnels → 选择 Tunnel → Connectors 列表
- 命令:
cloudflared tunnel info <NAME_OR_UUID>
- 重启、出口 IP 变化的影响
| 场景 | 结果 |
|---|---|
| Token 不变 | 正常认证到同一个 Tunnel |
| 出口 IP 变化 | 无影响,只是新的出站连接 |
| 进程重启 / 崩溃重启 / 服务重启 | 生成全新 Connector ID,旧 ID 变为 inactive |
| 同时在线实例 ≤ 25 | 可无限次重启,不会因 ID 累积而无法加入 |
Token 不变 + IP 变化 + 频繁重启 → 完全没问题。上限只看当前 active 数量。
场景2 把内网的tcp端口“暴露”到公网
把这个暴露打引号是有原因的,因为这个暴露需要运行一下客户端来连接,有点类似frp。
那不得不说Cloudflare的界面依然混乱了,这个菜单要从Zero Trust里面翻出来。
而有的账户点进去Zero Trust要你先添加银行卡付款方式,比较早期的账户不用,所以这里只简单介绍下:
点进去Zero Trust,点Tunnels & Mesh,点进去你的tunnel,点Published application routes,点Add a published application route,你可以看到这里的service多了type的选项,可以选tcp,url就填你的局域网,比如192.168.1.123:22,子域名依然自己填一个,比如ssh.myhome.com,保存了之后你就可以这样在任意地方连接你的192.168.1.123:22:
场景? 把ssh/rdp等服务运行在web里
这个是真的要添加支付方式,放弃
穿透速度与限制
Cloudflared的内网穿透基于quic,用户端基于http,端口传输则是走http的ws。在实测中发现,Cloudflared对于用户浏览器则的下行没怎么限速,也就是tunnel端的上传比较好,但对于用户则的上传有明显的限制,突发大带宽的情况下甚至会掐断:
Cloudflare 对过大的 WebSocket 二进制帧存在截断或重置行为。通过实测得出:
- 256 KiB 的大帧会导致 Cloudflare 在两个方向上截断/重置隧道;
- 32 KiB 的帧可以稳定传输,完整性没有问题。
Cloudflare Tunnel 对 WebSocket request-body 方向(client → origin) 存在明显的字节速率限制,实测限速时约为 ~1 Mbit/s;而 response-body 方向(origin → client) 不受此限制,可以跑满。
如果想点对点组跨国内网,最好两边都起一个tunnel然后写一个程序实现全双工通信,经过实测可行,速度两边都能打满,点对点Cloudflared官方推荐用那个非常难用的图形客户端one来连接,个人感觉没有tunnel默认会建立4 条 HA 连接到 Cloudflare edge稳定。当然这是题外话,有需要的可以自行摸索实现。
关于代理cloudflared和Tunnel连接性问题
对客户端来说,大概率不用关心连接性和代理的问题,因为客户端走的公网域名连接,代理起来也简单。关键是服务端怎么连上tunnel,或者更快地连上tunnel。
cloudflared官方项目里面有不少issue请求支持走代理连接,但无一例外都被close,官方的意思很明确,不支持代理是有意为之,cloudflared主要使用quic进行传输,http2是降级路径,而SOCKS5代理对QUIC并不适用(当然是从标准程序的实现来说,被大家玩出花的代理链路不能比,若必须支持 QUIC over SOCKS5,则需要深度改造 UDP 路径)
此外,Cloudflared在连接过程中会依赖DNS的SRV记录,如果你是专用虚拟机或者docker运行,如果你有搭建内网DNS服务器的话,建议配置专用公共的DNS避免过滤和干扰(如1.1.1.1)。如果你局域网有FakeIP,需要把以下域名后缀加入非FakeIP解析,或者嗅探流量的名单:
部分请求会使用以上域名包装数据内容,但使用的子域名的解析记录本身是不存在的,因此不要尝试嗅探这些域名的流量。
只要满足了正常的DNS解析记录,大概率裸连是没有问题的,如果是普通地把家里的服务暴露到公网,也足够。当然,如果想进行跨国的传输,那就要考虑对cloudflared进行代理:
前提条件:你的节点支持代理udp。
基本思路就是单独开机一个虚拟机或者独立IP的macvlan的docker容器,然后把他的网关直接写成透明代理的网关,DNS设置成1.1.1.1即可。
当你满足了所有链接条件,Cloudflared运行日志会输出如下:
| |
PaoPaoGateway集成的Cloudflared
最新版本的PaoPaoGateway集成了Cloudflared功能,只需要配置token即可开箱即用,简化了代理的问题,相关使用和选项可以参考GitHub说明。