跳到主内容

LYRA

Lyra 正在加载

Cloudflare Tunnel 连接 NAS 笔记

NAS鬼鬼

这篇记录如何在 NAS 上运行 cloudflared,把只监听内网的 Web 服务连接到 Cloudflare,再通过公开主机名和 Access 策略控制访问。

它适合 NAS 管理页、照片服务、下载面板等 HTTP/HTTPS 应用。教程采用 Cloudflare 控制台管理 Tunnel、Docker 运行连接器的方式,不涉及 WARP 私网路由,也不适合直接公开 SMB、数据库等不应暴露到浏览器的服务。

Cloudflare 控制台的菜单名称会随版本调整,操作时应以 Cloudflare Tunnel 官方文档 和当前界面为准。

先理解访问链路

cloudflared 从 NAS 主动向 Cloudflare 建立出站连接,因此路由器不需要转发公网入站端口,家庭宽带没有固定公网 IP 也能使用。Tunnel 本身只负责连接链路;如果没有 Access 策略,公开主机名仍然可以被互联网上的任何人访问。

Cloudflare Tunnel 连接 NAS 的访问链路

这篇笔记使用以下占位值:

公开地址:https://nas.example.com
Tunnel 名称:nas-tunnel
NAS 服务:http://127.0.0.1:5000
Token 文件:/volume1/docker/cloudflared/tunnel-token

example.com 是文档保留域名,实际配置时需要替换成自己的域名。端口 5000 也只是示例,必须以 NAS 上服务的实际监听地址为准。

前提条件

  • 域名已在 Cloudflare 中处于活动状态;
  • NAS 或同一局域网中的 Linux 主机可以运行 Docker;
  • 已确认目标是 HTTP 或 HTTPS Web 服务;
  • 有权限创建 Cloudflare Tunnel、Access 应用和 DNS 记录;
  • NAS 可以主动访问互联网,防火墙允许 Tunnel 所需的出站流量。

SSH 不是 Cloudflare Tunnel 的硬性要求。如果 NAS 的容器管理界面可以设置镜像、挂载文件、网络模式和启动命令,也可以全程通过图形界面操作。下面使用 SSH 和 Docker CLI,只是为了让步骤更容易复现。

1. 找到 NAS 服务的真实地址

先在 NAS 本机确认目标服务是否正常,不要一开始就把问题交给 Cloudflare。

Linux 主机优先使用 ss 查看监听端口:

Terminal window
sudo ss -tlnp

已知端口后直接测试:

Terminal window
curl -v http://127.0.0.1:5000

如果服务使用 HTTPS:

Terminal window
curl -vk https://127.0.0.1:5001

这里需要记录三件事:

  1. 服务使用 HTTP 还是 HTTPS;
  2. 服务实际监听的 IP 和端口;
  3. HTTPS 证书对应的主机名,以及它是否由受信任 CA 签发。

群晖、QNAP 等产品有常见默认端口,但用户可以修改,应用套件也可能使用完全不同的端口。不要把品牌默认值当成实际配置。

2. 先创建 Access 保护

对于 NAS 管理页这类敏感入口,建议先创建 Access 应用,再发布 Tunnel 路由。否则从公开主机名生效到 Access 配置完成之间,服务会短暂暴露在互联网上。

当前控制台大致位于:

Zero Trust
→ Access controls
→ Applications
→ Create new application
→ Self-hosted and private
→ Add public hostname

公开主机名填写 nas.example.com。这一步保护的是浏览器访问的公开域名,不是 localhost、NAS 内网 IP 或 Tunnel 名称。

选择登录方式

新建 Zero Trust 组织通常会提供 Cloudflare 身份提供商。也可以接入 Google、GitHub 等身份提供商并启用 MFA。

如需使用邮箱一次性验证码,应先在身份提供商设置中添加 One-time PIN,再在 Allow 策略中限制到明确的邮箱地址或邮箱域。OTP 是一种登录方式,不等同于第二因素;只把“Login Methods: One-time PIN”作为允许条件,会让任何有效邮箱都有机会通过认证。

一个个人 NAS 的最小策略可以是:

项目 示例
Action Allow
Include 指定邮箱地址
Require 指定登录方式或 MFA
Session duration 按自己的风险接受程度设置

Access 应用默认拒绝未命中 Allow 策略的用户。保存后先确认应用主机名确实是 nas.example.com,避免保护了错误的地址。

3. 创建远程管理 Tunnel

进入 Cloudflare Tunnel 页面:

Zero Trust
→ Networks
→ Connectors
→ Cloudflare Tunnels
→ Create a tunnel

选择 cloudflared,名称填写 nas-tunnel,运行环境选择 Docker。控制台会生成一条带 Tunnel token 的启动命令。

Tunnel token 等同于连接器凭据。拿到 token 的人可以启动这个 Tunnel 的副本,因此不要把它写进文章、截图、Git 仓库、Compose 文件或公开日志。怀疑泄露时应立即在控制台轮换 token。

4. 用 token 文件启动 cloudflared

官方镜像不会在容器内自动更新,因此容器启动参数保留 –no-autoupdate,更新时由 Docker 拉取新镜像并重建容器。

先创建仅管理员可读的 token 文件:

Terminal window
sudo install -d -m 700 /volume1/docker/cloudflared
sudo sh -c 'umask 077; printf "%s" "<YOUR_TUNNEL_TOKEN>" > /volume1/docker/cloudflared/tunnel-token'

<YOUR_TUNNEL_TOKEN> 替换为控制台生成的实际值。不要在多人共享的终端中直接执行带真实 token 的命令,以免进入 shell 历史。

拉取镜像:

Terminal window
sudo docker pull cloudflare/cloudflared:latest

然后启动容器:

Terminal window
sudo docker run -d \
--name cloudflared \
--restart unless-stopped \
--network host \
--mount type=bind,src=/volume1/docker/cloudflared/tunnel-token,dst=/run/secrets/tunnel-token,readonly \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate run --token-file /run/secrets/tunnel-token

为什么示例使用 host 网络

在普通 Docker bridge 网络中,容器里的 127.0.0.1 指向容器自己,不是 NAS 主机。示例使用 –network host,是为了让 cloudflared 可以访问 NAS 主机上的 127.0.0.1:5000

host 网络会扩大容器可见的主机网络范围,并不是所有 NAS 平台都支持。若不使用 host 网络,可以把路由目标改成 NAS 的局域网地址,或者在平台支持时加入 host.docker.internal 映射。无论采用哪种方式,都要从 cloudflared 容器所在网络实际测试,不能默认 localhost 一定正确。

5. 确认 Tunnel 已连接

查看容器状态和日志:

Terminal window
sudo docker ps --filter name=cloudflared
sudo docker logs cloudflared --tail 100

控制台中 Tunnel 应显示为 Healthy。日志格式会随 cloudflared 版本变化,不需要匹配某一整段固定文本;重点是没有持续出现 DNS、QUIC、HTTP/2 或认证错误。

Tunnel 使用出站连接访问 Cloudflare。防火墙受限时,需要允许 cloudflared 通过 TCP 和 UDP 端口 7844 连接 Cloudflare Tunnel 端点。QUIC 使用 UDP,HTTP/2 使用 TCP;默认配置会根据网络情况选择可用协议。

较新的版本还可以执行连接诊断:

Terminal window
sudo docker exec cloudflared cloudflared tunnel diag

如果当前镜像不支持该命令,直接查看连接器日志和控制台状态即可。

6. 添加 Published application route

回到 nas-tunnel,打开 Published application routes 并添加公开主机名:

字段 示例
Subdomain nas
Domain example.com
Service type HTTP
Service URL 127.0.0.1:5000

NAS Tunnel 的推荐配置顺序

路由协议必须与 NAS 服务实际协议一致:

本地是 http://127.0.0.1:5000 → 路由选择 HTTP
本地是 https://127.0.0.1:5001 → 路由选择 HTTPS

保存公开主机名后,Cloudflare 全量 DNS 托管通常会自动创建指向 Tunnel 的代理记录。DNS 页面中可以看到对应主机名;不要再手工添加一条指向家庭公网 IP 的 A 记录。

7. HTTPS 源站与证书验证

浏览器到 Cloudflare 边缘始终使用公开地址的 HTTPS。这里讨论的 HTTP/HTTPS,指的是 cloudflared 到 NAS 本地服务这一段。

如果 NAS 本地 HTTPS 证书有效,优先保持证书验证,并在需要时设置 Origin Server Name,让 cloudflared 使用证书中的主机名验证源站。

如果使用自签名证书,更稳妥的处理顺序是:

  1. cloudflared 提供签发该证书的 CA;
  2. 设置正确的 Origin Server Name;
  3. 只在无法完成前两项时临时启用 No TLS Verify。

No TLS Verify 会关闭 cloudflared 到源站这一段的证书校验,不应被当成默认修复方案。如果 NAS 与 cloudflared 位于同一受控主机,且本地服务本来就提供 HTTP,也可以直接使用 HTTP,避免无意义的自签名 TLS 排错。

8. Host 头只解决虚拟主机问题

HTTP Host Header 用来告诉源站“用户访问的是哪个主机名”。只有 NAS 的反向代理根据 Host 选择站点,或者默认主机返回了错误页面时,才需要在 Additional application settings 中覆盖 Host 头。

例如源站只接受 nas.internal.example

HTTP Host Header: nas.internal.example

不要把它统一填写成 localhost:端口。Host 头不能修复协议选错、端口错误、源站未运行或证书校验失败,也不会阻止正常的 HTTP 重定向。

9. 验证 Access 和 DNS

使用未登录 Cloudflare Access 的浏览器打开:

https://nas.example.com

正确流程应当是:

  1. 先出现 Access 登录页;
  2. 只有命中 Allow 策略的身份才能继续;
  3. 认证成功后才显示 NAS 自身登录页;
  4. NAS 仍然保留自己的账号、密码和 MFA。

Access 不是 NAS 登录的替代品。两层认证使用不同密码或不同身份因子,可以减少单一凭据泄露带来的风险。

还可以在 Tunnel 路由中启用 Protect with Access,让 cloudflared 在转发前验证 Access JWT。官方也建议源站验证 Access token,以防其他错误路由绕过入口策略。

10. 按错误层级排查

Cloudflare Tunnel 常见错误判断流程

1033:Cloudflare 找不到健康连接器

这表示问题在 Cloudflare 与 cloudflared 之间。检查:

  • 容器是否正在运行;
  • Tunnel token 是否有效;
  • NAS DNS 是否能解析 Tunnel 端点;
  • 出站 TCP/UDP 7844 是否被防火墙拦截;
  • 控制台状态是 Inactive、Down 还是 Degraded。

502:连接器无法访问 NAS 服务

Tunnel 已经连接,但 cloudflared 到本地服务失败。检查:

Terminal window
sudo docker logs cloudflared --tail 100
curl -v http://127.0.0.1:5000
sudo ss -tlnp | grep 5000

常见原因是服务未启动、端口不对、容器网络看不到主机,或者路由把 HTTP 与 HTTPS 写反。

TLS 握手或 x509 错误

first record does not look like a TLS handshake 通常表示把 HTTP 服务按 HTTPS 连接;x509 主机名或 CA 错误则表示协议已经是 HTTPS,但证书验证失败。先核对协议,再处理 Origin Server Name 或 CA,不要直接把所有 TLS 校验关闭。

重定向次数过多

curl -vkL 观察每一步 Location。检查 NAS 是否强制跳转到另一个端口、反向代理是否又跳回公开地址,以及 Origin Server Name 是否与证书匹配。Host 头只是其中一个可能因素。

Access 页面没有出现

立即检查 Access 应用绑定的是否为同一个公开主机名。公开路由存在而 Access 应用主机名写错时,服务可能处于未保护状态,应先暂停路由或停止连接器,再修正策略。

Docker 镜像更新

容器中的 cloudflared 不会自行替换镜像。更新前先阅读版本说明,再执行:

Terminal window
sudo docker pull cloudflare/cloudflared:latest
sudo docker rm -f cloudflared

然后使用原来的挂载、网络和启动参数重新创建容器。删除容器不会删除控制台中的 Tunnel,也不会删除主机上的 token 文件。

不建议教程直接覆盖 /etc/docker/daemon.json 来设置第三方镜像加速。不同 NAS 对 Docker 配置的管理方式不同,覆盖文件可能删除原有日志、存储或网络设置;镜像站本身也有可用性和供应链风险。确实无法访问 Docker Hub 时,应优先使用 NAS 厂商支持的代理配置或可信的内部镜像仓库。

安全检查

  • 路由器没有为 NAS 管理端口保留公网端口转发;
  • 公开主机名已经绑定 Access 应用和严格的 Allow 策略;
  • 没有使用 Everyone 或仅 Login Methods 作为放行条件;
  • NAS 自身账号启用了强密码和 MFA;
  • 默认管理员账户已经停用或限制;
  • Tunnel token 不在命令历史、截图、Git 和 Compose 文件中;
  • NAS 系统、应用和 cloudflared 镜像定期更新;
  • 不需要的厂商远程访问入口已经关闭;
  • 对外只发布必要的 Web 服务,不发布 SMB、数据库或 Docker API;
  • 定期检查 Access 审计日志和 Tunnel 连接器状态。

常用命令

Terminal window
# 查看连接器
sudo docker ps --filter name=cloudflared
# 查看最近日志
sudo docker logs cloudflared --tail 100
# 持续查看日志
sudo docker logs -f cloudflared
# 重启连接器
sudo docker restart cloudflared
# 查看监听端口
sudo ss -tlnp
# 测试 HTTP 源站
curl -v http://127.0.0.1:5000
# 测试 HTTPS 源站及跳转
curl -vkL https://127.0.0.1:5001
# 查询公开主机名
dig nas.example.com

参考资料

小结

Cloudflare Tunnel 解决的是“如何从内网主动连接 Cloudflare”,Access 解决的是“谁可以通过公开主机名继续访问”。两者需要一起配置,NAS 自身认证也不能省略。

实际排错时先分清三段链路:浏览器到 Cloudflare、Cloudflare 到 cloudflaredcloudflared 到 NAS 服务。1033、502、TLS 和 Access 错误分别属于不同层级,按层判断会比反复修改 Host 头或关闭证书验证更有效。

版权许可

本文采用 CC BY-SA 4.0 协议授权

保留署名与相同方式共享即可转载、引用或再创作。转载时请注明出处并附上本文链接。

评论

留言系统基于 Disqus,发言前可查看 评论规则。部分网络环境可能无法正常加载。