Skip to content

feat(node-setup): #23 向导降权 (sudo finalize-helper) + finalize 加固 - #181

Merged
jhfnetboy merged 2 commits into
mainfrom
fix/23-wizard-drop-root
Jul 17, 2026
Merged

jhfnetboy merged 2 commits into
mainfrom
fix/23-wizard-drop-root

Conversation

@jhfnetboy

Copy link
Copy Markdown
Member

承接 #23(向导拆非 root)。向后兼容 + opt-in:root 默认部署行为不变,非 root 是可选启用、需真板验证。

改动

  • finalize-helper.sh(新):finalize 里唯一需 root 的动作(关 provisioning gate + 重启 kms-api/dvt + disable 向导)单列成特权 helper。
  • aastar-node-setup.sudoers(新):只 NOPASSWD 白名单这一个绝对路径 → 降权后向导即便被攻破,拿到的也只是"跑这条固定收尾脚本",不是整机 root。
  • setup-server.py finalize():优先 sudo -n helper(捕获 stderr 成 warnings),失败回落直接 systemctl → root 部署行为完全不变。
  • .service:文档化非 root 启用(User=/Group= + ⚠️ NoNewPrivileges=no 否则 sudo 被挡)。
  • 顺手修隐患:ProtectSystem=strict 下 finalize 删 /etc/systemd/.../prov.conf 会被沙箱拦 → provisioning gate 关不掉。把该 drop-in 目录加进 ReadWritePaths(- 前缀可选)。
  • 删文件头过时注释「生产前需加认证」——正是它误导了 feat(node-setup): #19 模型A 一键链上注册闭环 (registerWithProof + KMS /pop) #179 reviewer(认证 feat(onboarding): 社区自助上手程序 + Phase 1 web 向导骨架 #156 已在位);step4 不再标 [Phase 1 stub]。

为什么安全合入

未做非 root provisioning 时,sudo -n helper 失败即回落原直接 systemctl 逻辑,与之前逐字一致。非 root 路径 opt-in,systemd 沙箱/sudo 交互本地测不了 → 真板首启验证后再切。

测试

python3 -m py_compile setup-server.py ✅ · bash -n finalize-helper.sh ✅ · AST 解析 ✅

诚实说明

非 root 实跑(User= + sudo helper + NoNewPrivileges=no + 目录 ownership)未在真板验证——需首启实测。本 PR 提供机制 + 文档 + 向后兼容回落,root 路径零回归。

https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5

…finalize hardening

向导降权(opt-in,向后兼容):
- finalize-helper.sh: finalize 里唯一需 root 的动作(关 provisioning gate + 重启 kms-api/dvt
  + disable 向导)单列成特权 helper,经 sudoers NOPASSWD 白名单被非 root 向导调用。
- aastar-node-setup.sudoers: 只白名单这一个绝对路径,收窄降权后向导的 root 面。
- setup-server.py finalize(): 优先 `sudo -n helper`(捕获 stderr 成 warnings),失败回落
  直接 systemctl → root 部署行为不变,非 root 是 opt-in。
- .service: 文档化非 root 启用(User=/Group= + ⚠️NoNewPrivileges=no 否则 sudo 被挡);
  修既有隐患:ProtectSystem=strict 下 finalize 删 prov.conf 被沙箱拦→gate 关不掉,
  把该 drop-in 目录加进 ReadWritePaths(`-`前缀可选)。

顺手:删文件头过时注释「生产前需加认证」(误导 reviewer;认证 #156 已在位),step4 不再标 stub。

非 root 路径需真板首启验证后再切(systemd 沙箱/sudo 交互本地测不了);root 默认路径不变。
py_compile / bash -n 通过。

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
@github-actions

Copy link
Copy Markdown

Security Audit Report

Date: Fri Jul 17 10:39:02 UTC 2026
Commit: f044bf8

Cargo Audit Results

�[1m�[33mwarning�[0m: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   /home/runner/work/AirAccount/AirAccount/kms/host/Cargo.toml
workspace: /home/runner/work/AirAccount/AirAccount/Cargo.toml
�[1m�[92m    Updating�[0m crates.io index
�[1m�[92m     Locking�[0m 1 package to latest compatible version
�[1m�[92m    Updating�[0m kms v0.28.1 (/home/runner/work/AirAccount/AirAccount/kms/host) -> v0.29.0
�[1m�[92mnote�[0m: pass `--verbose` to see 52 unchanged dependencies behind latest
�[0m�[0m�[1m�[32m    Fetching�[0m advisory database from `https://github.com/RustSec/advisory-db.git`
�[0m�[0m�[1m�[32m      Loaded�[0m 1160 security advisories (from /home/runner/.cargo/advisory-db)
�[0m�[0m�[1m�[32m    Updating�[0m crates.io index
�[0m�[0m�[1m�[31merror:�[0m not found: Couldn't load Cargo.lock
Caused by:
  -

... [查看完整报告](artifacts)

@clestons clestons left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — 向导降权设计扎实、向后兼容零回归。一条 sudoers 硬化建议。

核过(正确)

  • sudoers 严格:aastar-setup ALL=(root) NOPASSWD: /opt/aastar/node-setup/finalize-helper.sh —— 绝对路径、无参数、无通配、单命令。
  • helper 安全:不读 $@(无参数注入面)、固定动作(rm -f prov.conf + 3 条 systemctl)、幂等(|| true)、set -u。攻破向导最多拿到「跑这条固定收尾脚本」。
  • 回落向后兼容:sudo -n helper 非 0/异常即回落原直接 systemctl+rm,root 部署逐字不变。subprocess.run(["sudo","-n",helper]) 数组参数无 shell 注入。
  • ReadWritePaths 修得准:加 -/etc/systemd/system/kms-api.service.d。关键点抓对了 —— ProtectSystem=strict 靠 mount namespace 把 /etc 设只读,即便 sudo 到 root,helper 仍是 unit 的子进程、共享该只读命名空间,不列进 RW 就删不掉 gate。- 前缀容忍目录不存在。✓
  • 顺手删了文件头「生产前需加认证」过时注释(正是它误导了我 #179 的判断)+ step4 去掉 [Phase 1 stub]。

🟠 硬化建议:把「父目录也不可写」写进安装步骤

sudoers 白名单一个脚本路径,安全性完全系于该脚本及其所在目录 aastar-setup 均不可改。README step2 已要求 finalize-helper.sh 装成 0755 root:root(文件本身堵住了 ✓),但没写死父目录 /opt/aastar/node-setup/ 的属主/权限。若该目录可被 aastar-setup 写(如运维图省事 chown -R aastar-setup /opt/aastar),攻击者能 rm 掉再重建同名脚本 —— 目录写权限允许 unlink+create,绕过文件 0755。这正是 sudoers-脚本-白名单的经典逃逸向量。建议 step2 明确:/opt/aastar/node-setup/ 也须 root:root 且 group/world 不可写(chmod 755),并可加一句「helper 及其目录任一可被向导用户写 = 提权」。

非 root 路径你已诚实标注需真板首启验证。机制 + 向后兼容回落到位,可合。

…sudoers escape)

review Low(#181):helper root:root 还不够。父目录链(/opt /opt/aastar /opt/aastar/node-setup)
必须 root 拥有、aastar-setup 不可写,否则目录可写=删脚本重建恶意同名文件借 NOPASSWD 白名单
提权(sudoers 脚本白名单经典逃逸)。README 降权启用 + sudoers 注释均补上。

Claude-Session: https://claude.ai/code/session_015cWRdv3oPjoQ21PEjwo9m5
@jhfnetboy
jhfnetboy merged commit 2d269dd into main Jul 17, 2026
7 checks passed
@jhfnetboy
jhfnetboy deleted the fix/23-wizard-drop-root branch July 17, 2026 13:43
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants