blue_wonderland

瑞蓝幻境

Homelab 折腾记:内网 HTTPS、监控体系与 Cloudflare 分析

从 Firefox 打不开 Nextcloud Talk 出发,一路搞定内网 HTTPS 受信证书、Grafana 监控全家桶和 Cloudflare Analytics 面板,顺便踩了三个 GraphQL 的坑。


起因:一个打不开的页面

某天,局域网里的 Firefox 和 Safari 都打不开 Nextcloud Talk,而 Chrome 和 Edge 一切正常。

控制台里堆满了 CSP 警告,禁用所有浏览器扩展后依然如故,一度怀疑是 Nextcloud 的配置出了问题。排查到最后,真相和 Nextcloud 一点关系都没有——是浏览器安全模型

根因:安全上下文,浏览器的立场问题

WebRTC(摄像头、麦克风、crypto.subtle 等)只能在安全上下文下工作:HTTPS,或者 localhost。当时访问的是 http://10.10.10.10:10081——既不是 HTTPS 也不是 localhost,Firefox 和 Safari 严格遵守规范,直接禁用。

Chrome/Edge(Chromium 系)则有一个刻意的放宽:把非公网 IP(10.x、192.168.x 这类 RFC1918 地址)也判定为可信任来源——理由是内网 IP 无法被公网中间人攻击。

浏览器http://10.10.10.10 的判定Talk 表现
Chrome / Edge视为可信任(Chromium 放宽)✅ 正常
Firefox / Safari非安全上下文(严格)❌ 无法显示

结论:Firefox 在内网 HTTP 下让 Talk 正常工作,浏览器层面无解。 media.navigator.mediadevices.insecure.enabled 只放宽了 mediaDevices,改不了 window.isSecureContext——这不是 bug,是立场。

解法:内网专用域名 + 受信证书

既然必须 HTTPS,那就给内网访问配上 HTTPS。设计上有一条铁律:绝不动公网域名——next.iamalex.blue 走 Cloudflare 隧道原样保留,另起一个内网专用子域 nc.iamalex.blue

CF DNS      nc.iamalex.blue → 10.10.10.10(A 记录,不代理)
Let's Encrypt  DNS-01 验证签发受信证书(无需公网端口)
Caddy(mac mini) https://nc.iamalex.blue → 127.0.0.1:10081
局域网设备      解析 nc.iamalex.blue → 内网 IP → 直连

为什么这个组合成立

三个机制各司其职,缺一不可:

  1. DNS 只是一张”域名 → IP”对照表。IP 填内网地址完全合法。Cloudflare 权威 DNS 让所有设备——包括 iPhone,不用改 hosts——自动把 nc.iamalex.blue 解析到 10.10.10.10,局域网内直连,不绕公网。

  2. DNS-01 验证只需要操控 DNS 记录。CA 查询 _acme-challenge TXT 记录确认域名归属,完全不要求服务器公网可达。所以”域名指向内网 IP”和”签发受信证书”毫不冲突——这就是内网 HTTPS 的核心魔法。

  3. 证书绑定域名,不绑定 IP。TLS 握手验证的是”域名匹配 + CA 受信”,IP 是什么根本不参与。浏览器看到 https://nc.iamalex.blue 就是安全上下文,WebRTC 全部解锁。

证书用 acme.sh + DNS-01 签发(Let’s Encrypt,90 天有效期),cron 每天检查、到期前约 30 天自动续期。顺手把 Glance 仪表盘也挂了个 home.iamalex.blue,一套体系两个域名。

顺手的收益:内网域名体系

域名指向用途
nc.iamalex.blueNextcloudFirefox/Safari 内网用 Talk
home.iamalex.blueGlance仪表盘内网访问
next.iamalex.blue公网隧道外网访问,原样不变

内网域名不走公网,不受运营商 CGNAT 影响,速度快还稳定。

从零搭起 Grafana 监控体系

这台 Mac mini M4(16GB)跑着整套 CasaOS 虚拟机(Debian),Grafana 装了很久却一直没配置。这次正好一起办了。

Prometheus(虚拟机,systemd)
 ├─ node_exporter    虚拟机 9100 + mac mini 19100
 ├─ cAdvisor         8081(容器级指标)
 ├─ cloudflared      metrics 127.0.0.1:20241(隧道指标)
 └─ postgres_exporter  9187
Grafana   Prometheus 数据源 + 官方仪表盘(1860 / 14282)

几个值得一提的点

两个小坑

  1. OrbStack 会把虚拟机监听的端口自动发布到 mac mini 宿主,导致 mac 上的 node_exporter 和虚拟机端口冲突(9100)——差点把虚拟机的数据当成 mac 的。换个端口(19100)解决,数据来源一目了然。

  2. Grafana provisioning 导入的 dashboard 修改后必须递增 version,否则 Grafana 认为配置没变直接跳过更新。“改了没用”的假象就是这么来的,卡了我好久。

Cloudflare Analytics:与配额斗智斗勇

Cloudflare 的 GraphQL Analytics API 功能强大,但免费套餐的规则相当阴:

  1. httpRequestsAdaptiveGroups(支持按路径/状态码/国家分组)查询窗口最长 1 天——想查 30 天直接报 time range wider than 1d
  2. adaptive 的 filter 里不能放 zoneTag——它在 zones(filter:) 外层;
  3. date 参数只接受纯日期 YYYY-MM-DD(不接受 ISO 时间戳),状态码维度叫 edgeResponseStatus 而不是 responseStatus

于是拆成两个仪表盘,各司其职:

认证的取巧之路

Infinity 数据源的认证配置在 3.11 有 bug(UI 里 Bearer Token 选项点了直接跳 about:blank)。干脆釜底抽薪:在 mac mini 上用 Caddy 起一个本地代理(:8801),自动注入 Authorization 头再转发给 CF API——认证逻辑从数据源里完全剥离,谁来访问都带 token。

清理:给服务器减负

顺手把积压的垃圾清了一轮:

写在最后

这一轮从”一个浏览器打不开的页面”出发,最后落成了一套完整的 homelab 基础设施:

最大的体会:浏览器安全模型不是 bug,是特性。与其和它对抗(改 hosts、关安全设置),不如顺着它把基础设施做对——内网也上 HTTPS,一劳永逸。