ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

多设备接入Cloudflare Tunnel实战:从权限排错到容器化部署

2026/10/7 14:30:49 拓冰建站 浏览量
多设备接入Cloudflare Tunnel实战:从权限排错到容器化部署 先说个可能打破你预期的事实我这套内部代号叫“cloudflare-os”的方案并不是Cloudflare官方发布的某个操作系统——至少到今天为止官方还没有这样一个东西。会起这个名字完全是因为我折腾了一圈之后发现用Cloudflare这套云服务把家里的NAS、虚拟机、开发板统一管起来真正的难点几乎全部落在“操作系统”这四个字上。权限模型、文件系统、守护进程、虚拟化、软件源……每一项都比Cloudflare本身的配置门槛高。这篇就围绕“cloudflare-os”这个概念把我在Linux、macOS、飞牛OSfnOS上接入Cloudflare Tunnel和周边工具时踩过的坑、排查链路和最终固化下来的方案完整梳理一遍。如果你正打算让本地多台设备都稳定接入Cloudflare生态这篇文章应该能帮你少走不少弯路。1. 先搞清楚“cloudflare-os”到底是个什么东西1.1 并不是操作系统而是一套本地Agent接入体系Cloudflare真正跑在你本机上的组件远不止“改个DNS”这么轻。我日常用到的其实就三类东西cloudflaredCloudflare Tunnel的客户端一个常驻操作系统的守护进程负责把本地服务通过出站连接暴露到Cloudflare边缘网络Wrangler CLI开发Cloudflare Workers用的本地命令行工具会牵扯到Node/Rust工具链、缓存目录这些OS级的东西域名解析接入把域名NS切到Cloudflare让DNS、代理、安全策略全部交给Cloudflare处理。这三类里最重的是第一个。cloudflared不是一次性命令它需要以一个系统服务的形式长期活着才能让Cloudflare边缘网络随时和你本地的服务建立起安全通道。所以“cloudflare-os”这个代号在我这边的含义就是“以Cloudflare云能力为中心、把各种本地操作系统统一纳管起来的一组Agent和配套配置”。它横跨Linux服务器、macOS开发机、Windows老机器、以及飞牛OS这类NAS系统每一端的适配都跟底层OS深度绑定。1.2 为什么隧道模式反而比端口映射更吃操作系统稳定性很多人一开始的想法是我路由器上做一个端口映射把NAS的Web界面映射出去不就完了我在早期也是这么干的直到被两件事教育了。第一件事是运营商大内网。现在很多宽带的公网IP是假的光猫拨号拿到的IP并不直接暴露在公网上端口映射根本无从谈起。第二件事是安全性。即使你有公网IP直接把端口裸奔在公网等于把NAS的管理界面、FTP、甚至摄像头流媒体接口直接交给全网扫描器日志能给你刷出几千条攻击记录。Cloudflare Tunnel的工作方式和端口映射完全相反cloudflared主动从你本机发出加密连接默认是四条长连接连到Cloudflare边缘网络然后在边缘网络上注册一条路由。公网用户访问你的域名时请求在Cloudflare边缘节点被处理再通过这条已经建立好的隧道转发到本地。整个过程不需要你在路由器上开任何入站端口也不需要公网IP。但代价就藏在这里隧道必须是一个持续存活的服务。一旦cloudflared进程挂掉、被系统杀掉、或者因为底层日志文件把磁盘写满而崩溃整条链路就断了。这时候你拼的不是Cloudflare的控制台配置而是操作系统的服务管理能力——systemd写得好不好、Docker重启策略对不对、日志有没有做轮转限制。我后来遇到的那次“overlay2占满导致cloudflared反复重启”根子就在OS侧而不是Cloudflare侧。1.3 这套环境的整体画像是“云边协同”把“cloudflare-os”整体画出来其实就两层公网侧是Cloudflare的DNS、CDN、Zero Trust访问策略、边缘网络本地侧是你的NAS、服务器、虚拟机、开发板每台设备上跑一个cloudflared客户端通过出站连接和Cloudflare边缘网络保持心跳。剩下的工作全部是在本地操作系统上让这个Agent活得更稳、日志更可控、权限不越界。搞清楚这个框架之后我遇到的第一个硬骨头就来了在macOS上部署cloudflared时直接被一个权限错误拍在了沙滩上。2. 第一道坎权限错误“拒绝访问 (os error 5)”的完整排查链路2.1 报错场景还原我在一台macOS测试机上做cloudflared初始化执行到cloudflared service install或者直接运行cloudflared tunnel run的时候终端里弹出了这么一段error: 拒绝访问。 (os error 5)后面还跟着一句提示大意是“要想在不使用后台服务器的情况下工作请重新运行”。我当时盯着这几行字看了半天完全没搞懂“background server”是什么东西。后来才意识到问题压根就不在Cloudflare的配置上而是操作系统不允许这个进程去创建它需要的后台服务或者去写它需要的配置目录。需要说明的是这类报错并不只出现在cloudflared上。很多Rust写的CLI工具在macOS或者受限的Linux环境里跑起来时都会把底层操作系统的错误码直接打印出来。cloudflared本身就是Rust写的所以它的错误信息格式非常“系统”——直接把errno翻译成系统文本不带任何包装。2.2 os error 5到底是什么在Unix/Linux/macOS里errno 5对应的宏是EACCES含义是“Operation not permitted”也就是权限不足。在Windows里os error 5对应的是ERROR_ACCESS_DENIED同样也是拒绝访问。但“权限不足”这四个字太笼统了。我拆到实际场景里它通常来自三个不同的层面层面典型来源表现文件系统权限当前用户对配置目录没有写权限写/var/lib/cloudflared或/etc/cloudflared失败内核安全模块SELinux或AppArmor拦截RHEL系默认开启SELinux enforcing时常见macOS沙盒/TCC机制首次运行的CLI没有后台运行权限创建系统服务时被系统阻止这三个层面排查起来的路径完全不同我按顺序给出我实际用的排查链路。2.3 完整排查链路可以直接照着抄第一步确认身份和目录权限。先看当前用户是谁再看cloudflared要用的几个标准目录是否存在、是否可写id ls -ld /var/lib/cloudflared /etc/cloudflared ls -ld ~/.cloudflared如果目录不存在先尝试用sudo创建并授权sudo mkdir -p /etc/cloudflared sudo chmod 755 /etc/cloudflared第二步如果是RHEL系CentOS、Rocky、AlmaLinux等查SELinuxgetenforce ausearch -m avc -ts recent如果ausearch输出里有一堆avc denied记录基本就是SELinux在拦截cloudflared访问某个文件或绑定某个端口。可以先临时放进permissive模式验证sudo setenforce 0再执行一次cloudflared命令。如果通了说明就是SELinux策略的问题。注意permissive模式只用来验证千万别在生产环境一直开着。更稳妥的做法是给cloudflared写一条自定义SELinux模块或者直接把可执行文件放在系统预设的允许路径下。第三步macOS上查沙盒和TCC记录。打开“系统设置 → 隐私与安全性”看有没有被阻止的项或者打开“应用程序 → 实用工具 → 控制台”在搜索框里输入cloudflared看有没有类似“deny”的日志。macOS的TCC机制很要命它会在第一次运行某个未签名或签名不完整的CLI工具时默认拦截它创建后台服务的能力。有时候你甚至需要在“隐私与安全性”里手动给终端App授权“完全磁盘访问权限”CLI才能读取它需要的一些系统路径。第四步最常用也最容易被忽略的一招直接加sudo重跑。sudo cloudflared tunnel login sudo cloudflared tunnel run tunnel-name2.4 我的处理结果和一条重要经验我那次的问题根因其实特别基础没加sudo执行配置文件的路径又放在了用户家目录下的自定义位置而cloudflared的service install流程默认去找/etc/cloudflared/写入失败于是整个初始化流程中断抛出了os error 5。把配置挪到标准位置、用sudo重新执行之后一步就过了。一条很重要的经验送给所有刚接触Cloudflare Tunnel的人不要一次性把login、create、route、run、install五步全部连着跑完。每执行一步先单独验证一步的状态。比如login之后马上跑cloudflared tunnel list确认登录态已经在create之后确认json凭据文件有内容route之后确认DNS解析记录出现在Cloudflare Dashboard里。这样出问题时定位范围会小非常多。我那次就是太着急一把梭结果错误一来根本不知道卡在哪一环。3. NAS实战在飞牛OS上跑通Cloudflare Tunnel的完整路线3.1 为什么拿飞牛OS做载体最近飞牛OSfnOS在自建NAS圈子里热度很高热搜里一堆“刷飞牛OS”“虚拟机装飞牛OS”的。我也跟风刷了一台旧主机来试。飞牛OS本质上是一个基于Debian的轻量NAS系统自带硬件监控、应用中心、文件管理、Docker管理器对国内用户很友好。底层既然是Debian系意味着cloudflared的deb包和Docker镜像都直接能用。更重要的是NAS是一个天然的“Cloudflare Tunnel使用场景”。NAS里通常跑着Web管理界面、下载器、媒体服务、FTP、甚至EasyNVR这类流媒体服务。这些服务全都需要远程访问但又全都不能直接裸奔到公网上。用cloudflared统一接入是比端口映射安全得多的方案。3.2 用Docker在飞牛OS上部署cloudflared我的推荐是优先用Docker方式而不是直接在系统层装deb包。原因有两个第一Docker镜像的升级回滚非常干净拉错版本也不会污染系统环境第二Docker可以对日志做精细限制直接避免后面要说的overlay2膨胀问题。部署流程分四步。第一步先把cloudflared在本地环境初始化好并拿到tunnel凭据cloudflared tunnel login cloudflared tunnel create nas-tunnellogin会弹出一个浏览器窗口让你选择要绑定的域名create会生成一个json格式的凭据文件里面是tunnel的ID和账户信息。第二步在飞牛OS上建代码目录把你生成好的tunnel-id.json和主机上的配置文件都放进去mkdir -p /vol1/docker/cloudflared我习惯把凭据json和config.yml放在同一个目录方便后续compose文件直接挂载。第三步写config.yml。这里有一个非常关键的点如果你用host网络模式配置里的localhost指的是NAS本身如果你用默认的bridge网络容器里的localhost就是容器自己。为了省心我直接采用host模式配置文件长这样tunnel: nas-tunnel credentials-file: /etc/cloudflared/tunnel-id.json ingress: - hostname: nas.example.com service: http://localhost:80 - hostname: ftp.example.com service: http://localhost:8080 - hostname: livestream.example.com service: http://localhost:8088 - service: http_status:404第四条service: http_status:404是兜底规则必须写否则cloudflared会因为找不到匹配规则而拒绝启动。我见过很多新手在这里卡住配置文件里只有两条hostname规则没有兜底结果报错。第四步启动容器docker run -d --name cloudflared \ --network host \ --restart always \ -v /vol1/docker/cloudflared:/etc/cloudflared \ cloudflare/cloudflared:latest \ tunnel --config /etc/cloudflared/config.yml run--restart always很重要NAS重启后cloudflared会自动拉起来不需要再手动进终端。启动后查看日志docker logs cloudflared看到Registered tunnel connection字样说明隧道已经通了。这时候在公网访问nas.example.com应该能直接打开NAS的Web界面。3.3 overlay2文件系统占满的真相与清理方案这是热搜里那个“飞牛os overlay2 文件占用大”的问题我也是实打实踩了一遍。症状表现是cloudflared跑了一周左右NAS开始告警磁盘空间不足。查一下/var/lib/docker目录发现overlay2子目录的体积大得离谱。Docker默认的存储驱动就是overlay2镜像层是写时复制结构。每次你拉取一个新的cloudflared镜像旧镜像的层并不会立即消失再加上容器运行期间产生的可写层、以及容器日志的无限增长空间就被慢慢吃完了。尤其像cloudflared这种需要长期运行的守护型容器它的标准输出会不断产生日志而容器日志文件默认是不做任何大小限制的。日志文件在/var/lib/docker/containers/容器ID/目录下文件名以-json.log结尾。先用这条命令看清到底是谁在占用空间docker system df这条命令会输出镜像、容器、本地卷、构建缓存的占用情况。我那次排查下来大头就是两个一个是构建缓存另一个是cloudflared容器的json日志文件。清理和预防分两步走。第一步清理现有垃圾docker system prune -a -f这会清除所有未被使用的镜像、容器、网络和构建缓存是见效最快的一招。注意-a会连所有没在用的镜像层都删掉下次启动时会重新拉取但换来的是干净空间值得。第二步从源头限制日志这是更关键的一步。单容器可以在docker run时加参数docker run ... \ --log-opt max-size5m \ --log-opt max-file3或者修改Docker守护进程的全局配置/etc/docker/daemon.json{ log-driver: json-file, log-opts: { max-size: 5m, max-file: 3 } }改完重启Docker服务会短暂影响所有容器建议在业务低峰期操作。我把cloudflared日志上限设成每个文件5MB、保留3个文件一个月下来日志占用不会超过20MB从此再没出过overlay2被撑爆的问题。3.4 网络唤醒与FTP暴露的安全底线热搜词里还有“HP主机安装飞牛OS如何开启网络唤醒”和“飞牛OS设置FTP”这两个我也一并说说。NAS如果平时处于休眠状态人到了外面想唤醒它光靠cloudflared是不够的因为进程都睡着了。你需要先完成三件事在BIOS/UEFI里开启Wake-on-LAN。不同主板叫法不同常见的有“Power On By PCIE/Onboard LAN”“Wake on LAN”“WOL”等在NAS系统层面确保网卡允许魔术包唤醒。用ethtool eth0 | grep Wake-on查看如果显示Wake-on: g说明支持如果是d需要执行ethtool -s eth0 wol g开启为了让WOL设置持久化建议写一个systemd服务在开机时执行或者直接在飞牛OS的网络设置里确认相关选项。有了WOL之后手机端的唤醒App发一个魔术包NAS开机Docker随系统自启cloudflared连回Cloudflare边缘网络整条链路恢复。再说FTP。飞牛OS自带FTP服务配置起来也确实方便。但我要强调一句FTP是明文传输用户名、密码、数据全部裸奔。哪怕你通过Cloudflare Tunnel转发FTP隧道的公网段是加密的但FTP协议本身和Tunnel的内网段仍然明文。如果非要暴露FTP最低限度要套一层Cloudflare Access做前置身份认证并要求客户端使用FTPS。更推荐的做法是直接改用SFTP基于SSH或者在NAS上开WebDAV并套上HTTPS。总之别图省事把21端口裸奔这是我在安全上最坚持的一条经验。4. 多OS环境绕不开的两个地雷虚拟机拖拽失效和老系统镜像源4044.1 VirtualBox的DnD报错又一个典型的Guest OS问题热搜里有这么一条报错dnd: error: drag and drop to guest not possible -- either the guest os does not support dragdrop or the dnd data format is not accepted这基本是VirtualBox使用者的共同记忆了。我刚在虚拟机里装飞牛OS做测试的时候想从宿主机拖一个安装包进虚拟机结果就弹了这么一段。这个报错通常指向三个原因排查顺序我建议按下面的来第一虚拟机没装Guest Additions或者版本和VirtualBox本体差太多。这其实是最常见的。打开虚拟机的“设备”菜单选择“安装增强功能”挂载光盘后在虚拟机里执行安装脚本。Linux需要先安装编译依赖sudo apt install -y build-essential linux-headers-$(uname -r) sudo mount /dev/cdrom /mnt sudo /mnt/VBoxLinuxAdditions.run第二虚拟机设置里的拖拽方向没打开。在VirtualBox主界面选中虚拟机进入“设置 → 常规 → 高级”把“Drag and Drop”从“Disabled”改成“Bidirectional”。改完要重启虚拟机。第三部分极简发行版我遇到过一些精简版Debian和Arch的最小安装在安装Guest Additions时缺内核头文件脚本明明显示成功实际却没编译出内核模块。这时候还是回到第一步把linux-headers装好重新执行。一个我自己的经验在Linux虚拟机里拖拽功能的效果一直不太稳定。与其死磕拖拽不如直接配一个共享文件夹或者用一个临时HTTP服务把文件传到虚拟机里。如果你当前的目标只是在一个干净的Linux环境里测试cloudflared二进制那直接scp文件进去就行效率高得多。4.2 CentOS 7的仓库源报错Errno -1不是网络问题热搜里这条报错我也很眼熟http://mirrors.aliyun.com/centos/7/os/x86_64/repodata/repomd.xml: [Errno -1]我一开始以为是网络不通。但仔细看这台机器访问其他HTTP地址都正常唯独访问阿里云的这个路径失败。后来查清楚原因CentOS 7在2024年6月30日已经正式停止维护EOL官方把整个7系列的软件仓库从常规目录移到了vault归档目录。你用mirrors.aliyun.com/centos/7这个路径去访问返回的repodata其实是空的或者直接404yum就报了Errno -1。解决办法是把基础源切到vault归档路径。以阿里云为例正确的路径应该是http://mirrors.aliyun.com/centos-vault/7.9.2009/os/x86_64/或者用清华镜像https://mirrors.tuna.tsinghua.edu.cn/centos-vault/7.9.2009/os/x86_64/操作方式很简单编辑/etc/yum.repos.d/CentOS-Base.repo把baseurl里的centos/替换成centos-vault/7.9.2009/把mirrorlist这一行注释掉然后执行yum clean all yum makecache做完这一步老系统又能装包了。接着你就可以按Cloudflare官方文档的步骤添加cloudflared的rpm仓库然后正常安装。这个坑给我的启发是老系统的问题往往不在“装不上某个新工具”而是“整个系统的软件源地基已经改变”。遇到Errno -1、404这类报错先别急着怀疑网络先确认你用的Linux发行版是否已经EOL再确认镜像源路径是否做了归档迁移。这一点放在“多OS环境”里尤其重要因为你手头那台机器可能根本不是你想当然的最新版。5. 把临时调试变成可复现的生产环境5.1 用systemd把cloudflared做成“杀不死”的服务如果你不是用Docker而是直接在Debian系的服务器上装cloudflared那一定要交给systemd管理。我用的unit文件大致是这样的[Unit] DescriptionCloudflare Tunnel Agent Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Usercloudflared Groupcloudflared ExecStart/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run nas-tunnel Restartalways RestartSec5s LimitNOFILE1048576 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target几个要点Restartalways不管因为什么原因被杀掉5秒后自动拉起来是保活的关键Afternetwork-online.target确保网络真正就绪后才启动避免开机时网络还没通导致连接失败LimitNOFILE1048576cloudflared要维持大量出站长连接默认的文件描述符上限可能不够放宽到1024K比较稳。把unit文件放到/etc/systemd/system/cloudflared.service然后sudo systemctl daemon-reload sudo systemctl enable --now cloudflared sudo systemctl status cloudflared这套配置在普通Debian服务器和飞牛OS上都能用——飞牛OS底层是Debiansystemd这一层是完全通用的。5.2 日志和升级策略别让Agent自己吃了自己用systemd管理之后日志会进journald。如果不管/var/log/journal也可能悄悄变大。我一般会在/etc/systemd/journald.conf里设一个总上限SystemMaxUse200M然后重启journaldsudo systemctl restart systemd-journald升级方面我的习惯是固定主版本不要每次手动拉latest。无论是Docker镜像还是deb/rpm包升级之前先看一眼当前版本再拉新版本。用Docker的话docker compose pull docker compose up -d用systemd的话sudo systemctl restart cloudflared升级完一定要看一眼日志确认Registered tunnel connection重新出现再离开终端。5.3 把整套环境固化成一份compose文件我最后的做法是把cloudflared隔离成一个独立的compose服务这样不管换到飞牛OS、Debian服务器还是树莓派只要把目录和文件拷过去一条命令就能复现整套接入环境。最终的docker-compose.yml大概是这个样子services: cloudflared: image: cloudflare/cloudflared:2024.10.0 container_name: cloudflared restart: always network_mode: host command: tunnel --config /etc/cloudflared/config.yml run nas-tunnel volumes: - /vol1/docker/cloudflared:/etc/cloudflared:ro logging: driver: json-file options: max-size: 5m max-file: 3这里有几个点说一下network_mode: host配合config.yml里的localhost使用让容器和宿主机共享网络栈不需要额外暴露端口volumes挂载的是整个cloudflared配置目录用ro只读挂载防止容器内改动配置logging限定了日志大小从源头杜绝overlay2膨胀。这套东西我从临时脚本一路折腾成compose加systemd的固定组合前后踩掉至少两天的坑。最深的体会是Cloudflare这层云服务给了你很大的自由度但真正决定“能不能顺畅用起来”的永远是底下那个操作系统。别急着抱怨CDN配置不对先看看你的OS在权限、日志、文件系统这些基础环节上是不是已经在埋雷了。把这些地基问题解决掉“cloudflare-os”这套多设备接入体系才算真正立得住。