# -*- coding: utf-8 -*- r'''给 /rescue/ 加上管理鉴权。 == 问题 == 我把 /rescue/ 路由插在 do_GET 的第 1531 行,而管理页的登录校验 (self._admin_gate())在第 1723 行 —— **路由在鉴权之前**。 也就是说:任何人只要知道 https://relay.xxmxxs.top/rescue/20261008-173000.txt 就能直接下载诊断报告。那里面有什么: * 内网 IP(192.168.2.x)、主机名 * 进程列表、内存占用 * 中转和隧道的日志尾部 * MTU / 路由 / iptables 规则 这些是**运维信息**,不该公开。对比一下:/download/ 是给用户的 安装包,公开是对的;/rescue/ 是给自己看的,必须登录。 (同样的问题 /shot/ 也有 —— 那个是屏幕共享截图,本来就该拦。 这次先修 /rescue/,顺手看一眼 /shot/。) == 改法 == 在 /rescue/ 分支开头加一句 self._admin_gate(), 和 /v1/admin/* 那些接口一样。do_GET 和 do_HEAD 都要加。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent RELAY = ROOT / "relay" / "relay.py" # do_GET 里的(有 body 的那份) GET_OLD = ''' if path.startswith("/rescue/"): # 抢救报告下载。**单独一条路由,不发缓存头** —— # 内容会变,而且不该让 Cloudflare 边缘留着。 # (和 /download/ 分成两套是有意的:那个是给用户的安装包, # 文件名带版本号、内容永不改变,所以敢发 604800 长缓存。) wanted = urllib.parse.unquote(path[len("/rescue/"):])''' GET_NEW = ''' if path.startswith("/rescue/"): # 抢救报告下载。**单独一条路由,不发缓存头** —— # 内容会变,而且不该让 Cloudflare 边缘留着。 # (和 /download/ 分成两套是有意的:那个是给用户的安装包, # 文件名带版本号、内容永不改变,所以敢发 604800 长缓存。) # # ⚠ 必须登录才能下。报告里有内网 IP、进程表、日志尾部、 # MTU/路由/iptables —— 是运维信息,不是给所有人看的。 # 第一版把这条路由插在 _admin_gate() **之前**, # 等于匿名可下,实测踩到。 if not self._admin_gate(): return wanted = urllib.parse.unquote(path[len("/rescue/"):])''' # do_HEAD 里的(只有 headers) HEAD_OLD = ''' if path.startswith("/rescue/"): # 抢救报告。HEAD 只回长度和类型,**不写 body** —— # 这条路由同时存在于 do_GET(真正下载)和 do_HEAD(探测)。 wanted = urllib.parse.unquote(path[len("/rescue/"):])''' HEAD_NEW = ''' if path.startswith("/rescue/"): # 抢救报告。HEAD 只回长度和类型,**不写 body** —— # 这条路由同时存在于 do_GET(真正下载)和 do_HEAD(探测)。 # 同样要登录(见 do_GET 那段注释)。 if not self._admin_gate(): return no() wanted = urllib.parse.unquote(path[len("/rescue/"):])''' def main() -> int: raw = RELAY.read_bytes() bom = raw.startswith(b"\xef\xbb\xbf") text = raw.decode("utf-8-sig") changed = [] for old, new, what in ((GET_OLD, GET_NEW, "do_GET"), (HEAD_OLD, HEAD_NEW, "do_HEAD")): if new.split("\n")[0] in text and "_admin_gate" in text.split(old)[0][-600:]: changed.append(("·", what, "好像已经加过了")) continue if old not in text: changed.append(("✗", what, "找不到锚点")) continue text = text.replace(old, new, 1) changed.append(("✓", what, "已加鉴权")) print(" ── 结果 ──") for m, w, why in changed: print(" %s %-10s %s" % (m, w, why)) if any(m == "✗" for m, _w, _y in changed): return 1 RELAY.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) print(" 已写回 relay.py") return 0 if __name__ == "__main__": sys.exit(main())