fix: 管理入口改为「填链接」而非绑定特定隧道方案,并修掉配置接口的密钥泄露
用户反馈:「你不需要帮我处理 tunnel 这部分,也许我不是通过 tunnel 开放内网呢? 你只需要给我一个填写开放的管理端链接的地方。」 之前把「Cloudflare Tunnel」当成既定方案写进了配置与文档,这是替用户做了假设。 现在改为:项目只提供一个填地址的地方,怎么暴露完全由使用者决定。 - 删除 edge/cloudflare/tunnel.yml 与全部 tunnel 相关文档 - 新增 settings.adminUrl:管理界面里填一个地址,公开面板页脚出现「管理」按钮 指向它;留空则完全不显示(默认,避免在公网面板暴露管理面位置) - 只接受 http/https,javascript: 等协议被服务端拒绝(会被渲染成 <a href>) - 设置落在 local.monitors.json(私有层),只有自己的面板会出现该按钮 - 面板启动时额外拉 /api/monitors 合并 settings:原先只读静态 monitors.json (公开层),导致私有层的设置面板看不到;纯静态部署无该接口时沿用公开配置 顺带修掉一个既存漏洞(与本轮改动无关,是引入私有层叠加机制时埋下的): /api/monitors 无鉴权却回吐合并后的完整配置,包含私有层的 UptimeRobot API Key —— 只要配了 key,任何访客 curl /api/monitors 就能拿到。现在两侧(Node 与 Worker) 都对对外响应剥离 uptimeRobot.apiKey,保留 enabled 等非敏感字段供面板读取; 探针内部同步仍用完整配置,剥离只作用于 HTTP 出口。 测试:verify-worker 36→40 项(新增 [E] 配置接口密钥泄露 4 条)、 verify-security 20→25 项(新增 [D2] 密钥泄露 5 条)。全项目 8 套 314 项通过。
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
# Cloudflare Tunnel:让管理端从外网访问
|
||||
#
|
||||
# 场景:探针跑在内网/家用小主机上(没有公网入口),但你需要在手机或外网笔记本上
|
||||
# 打开 /admin 做管理操作。
|
||||
#
|
||||
# 原理:cloudflared 在你的机器上**主动向外**建立到 Cloudflare 的隧道(出站连接,
|
||||
# 不需要公网 IP、不需要开放任何入站端口),Cloudflare 再把指定子域名回源到它。
|
||||
# 因此内网无需任何端口映射。
|
||||
#
|
||||
# 用法(命名隧道,推荐):
|
||||
# 1. 安装 cloudflared
|
||||
# 2. 在 Cloudflare Zero Trust 后台创建命名隧道,取它的 token
|
||||
# 3. 打开探针的远程管理开关(默认关闭):
|
||||
# 在 local.monitors.json 里加 "settings": { "admin": { "allowRemote": true } }
|
||||
# 4. 跑:cloudflared tunnel --no-autoupdate run --token <你的 token>
|
||||
#
|
||||
# 一次性(快速隧道)命令,仅适合临时验证:
|
||||
# cloudflared tunnel --url http://localhost:8787
|
||||
# 它会打印一个随机 trycloudflare.com 地址;不要用于正式环境(无鉴权、无 SLA)。
|
||||
#
|
||||
# 前提:探针必须处于「已绑定 2FA」的状态。Tunnel 只是把端口暴露出去,
|
||||
# 真正拦住未授权访问的是管理端的 TOTP + 会话 + 失败限流。
|
||||
|
||||
tunnel: <你的隧道 ID 或名字>
|
||||
credentials-file: /etc/cloudflared/<TUNNEL_ID>.json
|
||||
|
||||
# Tunnel 只回源管理端这一条路径,面板与公开 API 不通过隧道暴露。
|
||||
# ingress 的第一条是 catch-all,必须放在最后。
|
||||
ingress:
|
||||
# 管理后台页面与接口:都指向同一个本地端口(管理端自身会校验 2FA 与会话)
|
||||
- hostname: admin.example.com
|
||||
path: ^/(admin|api/admin/.*|assets/.*)$
|
||||
service: http://localhost:8787
|
||||
|
||||
# 其余一律拒绝:不要把公开面板与 /api/status 一起暴露出去
|
||||
- service: http_status:404
|
||||
|
||||
# 探针侧的探测与通知日志仍打在 stdout,不受隧道影响。
|
||||
# 若用 systemd 常驻,建议:
|
||||
# ExecStart=/usr/local/bin/cloudflared tunnel run --no-autoupdate --token-file /etc/cloudflared/token
|
||||
# Restart=on-failure
|
||||
# RestartSec=5s
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
async fetch(request, env, ctx) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === '/api/status') return json(await getStatus(env, ctx));
|
||||
if (url.pathname === '/api/monitors') return json(await getMonitors(env, request.url));
|
||||
if (url.pathname === '/api/monitors') return json(stripSecrets(await getMonitors(env, request.url)));
|
||||
if (url.pathname === '/api/refresh') {
|
||||
// 公网上的强制探测必须显式开启,避免被任意访客当作「探测放大器」使用
|
||||
const gate = refreshGate(env, request);
|
||||
@@ -110,6 +110,14 @@ async function getMonitors(env, baseUrl) {
|
||||
return { settings: {}, monitors: [] };
|
||||
}
|
||||
|
||||
// 对外提供配置时必须剥掉密钥:/api/monitors 无鉴权,而配置里可能含 UptimeRobot API Key
|
||||
// (该 Key 以 secret 形式传入 env,不写进 monitors.json,但手动写进 KV 的配置可能带)
|
||||
function stripSecrets(config) {
|
||||
const ur = config?.settings?.uptimeRobot;
|
||||
if (!ur) return config;
|
||||
return { ...config, settings: { ...config.settings, uptimeRobot: { ...ur, apiKey: undefined } } };
|
||||
}
|
||||
|
||||
async function getStatus(env, ctx) {
|
||||
const state = (await env.MONITOR_KV.get('state', 'json')) ?? { monitors: {}, generatedAt: null };
|
||||
if (!state.generatedAt) ctx.waitUntil(runChecks(env));
|
||||
|
||||
Reference in New Issue
Block a user