原文:Cloudflare Argo Tunnel with Rust+Raspberry Pi,作者 Steven Pack
家里有一块吃灰的树莓派,一直想用来跑点什么,但总绕不开两个问题:家庭宽带没有固定 IP,路由器也不想随便开放端口。
这篇文章记录的,就是 Cloudflare 一位工程师的实验:把树莓派、Rust 异步 Web 框架和 Cloudflare Tunnel 三者结合起来,在完全不动路由器、不配 DNS 记录的前提下,把树莓派上的 Web 服务暴露到公网。
整个过程出乎意料地顺滑。
问题在哪里
想让家里的机器对外提供服务,传统方式大概有这几条路:
- 动态域名解析(DDNS):家庭宽带 IP 会变,得在路由器上跑一个 DDNS 客户端,不稳定,配置也烦琐
- 端口映射:在路由器上开放指定端口转发到内网机器,等于把内网暴露了一个口子,有安全风险
- 租云服务器:花钱,而且家里的树莓派就成了摆设
Cloudflare Tunnel(原名 Argo Tunnel)的思路完全不同:让内网机器主动向外建立一条持久连接,流量通过这条连接进来,不需要任何入站规则。
原理:反向隧道
整个架构用一句话描述就是:
树莓派上的
cloudflared客户端,向最近的 Cloudflare 节点建立一条出站的 HTTP/2 长连接;当外网用户请求你的域名时,Cloudflare 把请求通过这条连接反向推送给树莓派,树莓派处理后再原路返回。
几个关键点:
- 流量方向是"出站"的。树莓派只需要能访问外网,不需要任何端口对外开放
- 安全性由证书保证 。
cloudflared登录时会生成一个证书文件(cert.pem),隧道连接用它来做身份验证 - Cloudflare 负责域名解析和 HTTPS。你只需要有一个托管在 Cloudflare 上的域名,其余的它帮你搞定
准备工作
需要以下几样东西:
- 一块树莓派(作者用的是 Raspberry Pi 3 Model B),已安装 Raspbian
- 一个托管在 Cloudflare 的域名
- 树莓派能正常访问外网
确认树莓派联网正常,最简单的方法是 curl 一下:
bash
curl -I https://www.cloudflare.com
看到 HTTP/2 200 就说明出站连接没问题。
第一步:安装 cloudflared
cloudflared 是 Cloudflare 提供的隧道客户端。树莓派是 ARM 架构,需要下载对应版本:
bash
wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-arm.tgz
mkdir argo-tunnel
tar -xvzf cloudflared-stable-linux-arm.tgz -C ./argo-tunnel
cd argo-tunnel
./cloudflared --version
如果看到版本号输出,就说明可以正常运行了。
第二步:登录并授权域名
运行登录命令:
bash
./cloudflared login
它会输出一个 URL,让你在浏览器里打开(因为树莓派是无头环境,复制到自己的电脑浏览器里打开就行)。登录 Cloudflare 账号后,选择你想用来建隧道的域名,点击授权。
授权成功后,树莓派控制台会显示:
You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
/home/pi/.cloudflared/cert.pem
这个 cert.pem 就是隧道的凭证文件,后续都会用到它。
第三步:用内置 Hello World 验证隧道
在正式部署自己的服务之前,先用 cloudflared 自带的 Hello World 模式验证隧道是否通:
bash
./cloudflared --hostname tunnel.yourdomain.com --hello-world
把 tunnel.yourdomain.com 换成你自己的子域名。启动后会看到类似这样的日志:
INFO[0005] Connected to LAX
INFO[0010] Connected to SFO-DOG
这说明客户端已经连上了 Cloudflare 节点。此时在浏览器里访问 https://tunnel.yourdomain.com,应该能看到一个 Hello World 页面。
路由器没动,防火墙没动,DNS 没手动配置,就这样通了。
第四步:用 Rust 跑一个真正的 Web 服务
Hello World 验证完隧道没问题,下一步换成一个真实的 Rust Web 服务。原博客作者选择了 Gotham,一个基于异步 I/O 的 Rust Web 框架。
首先安装 Rust 工具链:
bash
curl https://sh.rustup.rs -sSf | sh
# 安装完成后刷新环境变量
source $HOME/.cargo/env
克隆 Gotham 并编译 hello_world 示例:
bash
git clone https://github.com/gotham-rs/gotham
cd gotham/examples/hello_world
cargo build
树莓派编译比较慢,cargo build 花了将近 8 分钟。编译完成后运行:
bash
cd ../../target/debug
./gotham_examples_hello_world
# Listening for requests at http://127.0.0.1:7878
Web 服务现在监听在本地 7878 端口。把隧道指向它:
bash
./cloudflared --hostname gotham.yourdomain.com http://127.0.0.1:7878
访问 https://gotham.yourdomain.com,Rust 服务的响应就出来了。
第五步:配置开机自启
每次 SSH 进去手动启动太麻烦,需要让两个进程在系统启动时自动运行。
配置 cloudflared 自启
先把证书和配置文件放到系统目录:
bash
sudo cp ~/.cloudflared/cert.pem /etc/cloudflared
sudo nano /etc/cloudflared/config.yml
config.yml 内容:
yaml
hostname: gotham.yourdomain.com
url: http://127.0.0.1:7878
然后安装为系统服务:
bash
sudo ./cloudflared service install
cloudflared 会自动注册成 systemd 服务,开机自启。
配置 Rust Web 服务自启
把编译好的二进制文件复制到一个稳定的位置:
bash
cp target/debug/gotham_examples_hello_world /home/pi/argo-tunnel/server/bin/
编辑 /etc/rc.local,在 exit 0 之前加一行:
bash
/home/pi/argo-tunnel/server/bin/gotham_examples_hello_world &
重启树莓派,SSH 回来后检查两个进程是否都在运行:
bash
sudo ps -aux | grep cloudflared
sudo ps -aux | grep gotham
看到进程就说明配置成功了。
这件事有意思在哪
纯粹的技术实现层面,这个方案并不复杂,每一步都很直白。但把这几样东西组合在一起,解决的问题其实很实际:
家宽没有固定 IP 不再是障碍。 Cloudflare Tunnel 的连接是从内网发起的,IP 怎么变都无所谓,隧道会自动重连。
不需要开防火墙端口。 对家庭网络来说,不在路由器上开洞,安全性本质上好了一个层次。所有流量都经过 Cloudflare,还能顺带享受 DDoS 防护和 HTTPS。
Rust 在资源受限设备上表现不错。 Gotham 是异步框架,内存占用低,这一点在只有 1GB 内存的树莓派上很重要。编译慢是确实慢,但一次编译、长期运行,是嵌入式类场景的合理取舍。
当然,这个方案也有局限。Cloudflare Tunnel 需要你有一个托管在 Cloudflare 上的域名,流量也会经过 Cloudflare 的节点,如果对数据经过第三方有顾虑,就需要另想办法。
小结
这个实验的路径是这样的:
- 树莓派联网,安装
cloudflared cloudflared login授权域名,获取证书- 先用内置 Hello World 验证隧道通路
- 安装 Rust,编译 Gotham 示例,把隧道指向本地 Web 服务
- 配置 systemd 和 rc.local 实现两个进程的开机自启
整个过程没有动过路由器,没有手动配过 DNS,家里的树莓派就这样对外提供了一个有正经域名和 HTTPS 的 Web 服务。
对于那些一直想折腾树莓派但被网络配置挡在门外的人来说,这条路值得一试。