Files
liuzhuohua d0a72bbb5f fix(release): Release 附件版本号单一真源 + 堵住「注释掉整行代码」型注入残留
发 v1.0.9 前发现两个真问题,都会直接影响用户拿到的附件。

一、docker-compose.yml 里的镜像标签是硬编码的 1.0.8

这两个文件是**直接挂到 GitHub Release 上的附件**,用户照着抄命令。
早先它们硬编码具体版本号,每次发版要人工 sed 一遍 —— 这次就漏了:
附件里写着 `image: drouter:1.0.8`,而镜像 tar 加载后的标签是 `drouter:1.0.9`,
`docker compose up` 直接报「找不到镜像」。DOCKER-GUIDE.md 另有 14 处同样问题。

改成 __VERSION__ 模板,由 build-docker.sh 渲染(版本从 packaging/VERSION 读)。
渲染分两步,顺序不能反:先删 TEMPLATE-NOTE 标记块(那是给维护者看的
「这是模板」提示,留着用户会看到「1.0.9 是占位符」这种自相矛盾的话),
再替换占位符。先替换的话,标记块里的 __VERSION__ 也会被换掉。

retag-docker-image.py 同样问题:NEW_TAGS 硬编码。改完参数化。
这个 bug 更隐蔽 —— 拿 1.0.9 的镜像跑重标记会打上 1.0.8 的标签,
用户 load 完看到的标签与附件说明对不上。

t-packaging.py 补 12 条判据卡住这些(4 条已做反向验证:注入即红、还原即绿)。
判据只抓「紧挨 drouter 标识或产物文件名」的版本号 —— 一刀切拦所有 x.y 会
误伤「1.0.1 起 Web 终端绕开 init」这种历史沿革说明和「GPL-3.0」许可证名。

二、t-worktree-clean.py 抓不到「注释掉整行代码」型注入

跑回归时 t-107 红了 1 项,查出来是 t-107-inject-wg.py 把
`'env': _vpn_env(),` 注释掉了没还原(SIGTERM 打断,finally 没执行)——
和 v1.0.8 那次 `/nonexistent` 残留一模一样的坑,但**形态不同**:
不含任何哨兵字样,原判据的 4 个哨兵全都抓不到。

后果很实:_vpn_status 不返回 env,前端拿不到四态诊断,
VPN 页面的三个修复按钮全废。这正是 v1.0.8 声称修好的那个 bug。

补两条判据:
- 扫「被注释掉的整行代码」:去掉 # 后能 ast.parse 成功,且含调用/赋值/return/dict 项
- 扫 CRLF:Windows 工具写文件会整篇改行尾,sync.sh 预检才拦太晚

判据本身踩了三个坑,都记在注释里:
- dict 项 `'env': _vpn_env(),` 单独 ast.parse 会 SyntaxError(没大括号),
  真正的注入反而被放过 → 补大括号再解析
- dict 项的键**通常带引号**,正则只写 ^[A-Za-z_] 认不出来 → 改成同时认引号键
- 分隔线注释 `# ------ radvd` 里的 ----- 会被解析成负号表达式 → 按前缀排掉

双向验证:4 种注入形态(赋值/dict项/bool/列表)全部精确报到行号,
基线零误报(排掉了分隔线注释和「# module: (service, action, 中文名)」这类
长得像 dict 项的文档注释)。

三、_dev/dl-verify.py(新增)

从目标机往 Windows 拉大产物时发现的坑,记成脚本省得下次重踩:
- `ssh host 'cat f' > out` 在 Windows 上做**文本模式换行转换**,二进制被破坏
  (实测 deb 少 4 字节,sha256 直接对不上)→ 走 base64 再本地解码
- Windows Python 看不到 Git Bash 的 /tmp,必须给 C:/ 绝对路径
- scp 传完一定要判 sha256 —— scp/ssh 都可能「返回 0 却落地 0 字节」
- 落地走 .part + os.replace,避免写一半失败留下半个产物

顺带修了 build-docker.sh 里一处路径笔误
(packaging/docker/packaging/docker/DOCKER-GUIDE.md)和步骤编号 1/3→1/4。
2026-10-04 14:05:33 +08:00

106 lines
5.4 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# Drouter —— Docker Compose 编排
#
<!-- TEMPLATE-NOTE:BEGIN -->
# ⚠️ 这是**模板**,__VERSION__ 是占位符,不要直接拿去用。
# 发版时由 packaging/build-docker.sh 渲染成 dist/docker-compose.yml
# (版本号从 packaging/VERSION 读)。改版本只需改那一个文件。
<!-- TEMPLATE-NOTE:END -->
#
# 用法:
# docker load -i drouter-__VERSION__-docker.tar
# docker compose up -d
# 浏览器打开 https://<宿主IP>:8443/
#
# ⚠️ 先读这三条,否则会踩坑:
#
# 1) 必须是 network_mode: host
# 容器要直接读写宿主网络接口和 nftables 规则,bridge 模式下看到的是
# 容器自己的网络命名空间,读不到真实网卡,也管不了宿主的 NAT。
#
# 2) NET_ADMIN 不能省
# 少了它 nft 操作会报 "Operation not permitted",防火墙页直接失效。
# 实测:加上之后 /api/nft 正常返回真实规则集。
#
# 3) 这是「体验 / 调试」形态,不建议当主路由
# 容器里没有 systemd,服务管理页会显示 active: unknown(优雅降级,不是 bug),
# 且无法持久化 systemd 单元。真正长期当路由器跑请用 .deb 装到物理机/虚拟机上。
# =============================================================================
services:
drouter:
# 镜像从 tar 加载后即为这个名字。若你改过标签,这里同步改。
image: drouter:__VERSION__
# 固定容器名,方便 docker exec / logs 直接引用
container_name: drouter
# host 网络是硬要求,见文件头说明
network_mode: host
# ── 权限 ───────────────────────────────────────────────────────────
# 这里**不用** privileged(过度授权)。只给真正需要的能力:
cap_add:
- NET_ADMIN # nft / iptables / 路由表 / 接口配置
- NET_RAW # ping / traceroute 等原始套接字探测
- NET_BIND_SERVICE # 绑定 8443(<1024 的端口才严格需要,这里保守给上)
# 若你想省事、且接受更大权限面,可以注释掉上面三行改用:
# privileged: true
# ── 端口 ───────────────────────────────────────────────────────────
# network_mode: host 下 ports 会被忽略,端口由 /etc/drouter/web-port 决定。
# 这里写出来只是**文档作用**,让 `docker compose config` 能看出监听什么。
expose:
- "8443" # HTTPS 管理面板
- "8080" # 备用 HTTP(默认关闭,可在「系统设置」里开)
# ── 持久化 ─────────────────────────────────────────────────────────
# 这四个目录**必须**挂出来,否则 docker compose down 一次配置就全丢了。
volumes:
# 配置核心:settings、web-port、active-theme、证书索引
- drouter-etc:/etc/drouter
# 运行时数据:SQLite 库、流量统计
- drouter-data:/opt/drouter/data
# 配置快照:出问题时的回滚依据
- drouter-snapshots:/opt/drouter/snapshots
# 日志
- drouter-log:/var/log/drouter
# ── 环境变量 ───────────────────────────────────────────────────────
environment:
TZ: Asia/Shanghai
# Web 端口只在**首次启动**时播种进 /etc/drouter/web-port。
# 之后想改端口,请到「系统设置」界面改 —— 别再改这里的环境变量,
# 否则会出现「界面里改了却没生效」的困惑(环境变量优先级高于配置文件)。
DROUTER_WEB_PORT: "8443"
# ── 启动策略 ───────────────────────────────────────────────────────
# unless-stopped:重启宿主后自动拉起,但用户手动 stop 后不会自己回来
restart: unless-stopped
# ── 健康检查 ───────────────────────────────────────────────────────
# /api/health 是免鉴权接口,适合探活。
# ⚠️ 面板走 HTTPS 且用自签证书,curl 必须加 -k,否则永远 unhealthy。
healthcheck:
test: ["CMD-SHELL", "PORT=$(cat /etc/drouter/web-port 2>/dev/null || echo 8443); curl -fsSk --max-time 4 https://127.0.0.1:${PORT}/api/health >/dev/null 2>&1 || exit 1"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
# ── 日志 ───────────────────────────────────────────────────────────
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# =============================================================================
# 命名卷
# =============================================================================
volumes:
drouter-etc:
drouter-data:
drouter-snapshots:
drouter-log: