fix(update): 更新前自动备份以 drouter 身份跑时写不进备份目录
真机报错:更新功能「备份失败,已中止更新」—— [Errno 13] Permission denied: /opt/drouter/backups/xxx.part。 根因:/opt/drouter/backups 是 root:root drwxr-xr-x,而「更新前备份」这条链 由 drouter-web(drouter 用户)spawn drouter-backupd.py --run-now,backupd 的 call() 用 subprocess 直接跑 helper、没提权 → helper 继承 drouter 身份 → 写备份包 .part 被拒。定时器那条链是 root 跑的,所以平时备份正常, 只有「更新触发的备份」踩雷。 修法:backupd 的 call() 里 helper 调用加 sudo -n 前缀。sudoers 已放行 drouter 免密跑 helper 白名单,root(定时器)/ drouter(更新)两种身份都通。 判据:t-update.py 新增「backupd call() 用 sudo -n 提权」(查命令数组语义), t-update-inject.py 加对应反向验证注入(34 例 0 异常)。 真机验收:sudo -u drouter backupd --run-now → BK_CREATED files=21 size=23767。
This commit is contained in:
1 parent
b7ca9c89e8
commit
8303fa3359
3 files changed
+30
-1
No files matched your search
@@ -98,6 +98,14 @@ CASES = [
|
||||
" import drouter_update as U",
|
||||
'web.py 用 importlib'),
|
||||
|
||||
# backupd call() 去掉 sudo 前缀:drouter 身份(更新前备份)写不进
|
||||
# /opt/drouter/backups → [Errno 13] Permission denied(2026-10-10 真机修)。
|
||||
('backupd call() 去掉 sudo(drouter 身份写不进备份目录)',
|
||||
'backend/drouter-backupd.py',
|
||||
" ['sudo', '-n', '/usr/bin/python3', HELPER, action,",
|
||||
" ['/usr/bin/python3', HELPER, action,",
|
||||
'backupd call() 用 sudo -n 提权'),
|
||||
|
||||
('/api/update 路由被摘掉',
|
||||
'backend/drouter-web.py',
|
||||
" if p.startswith('/api/update'):",
|
||||
|
||||
@@ -792,6 +792,15 @@ ck('_do_backup 校验 BK_CREATED(不只看 returncode)',
|
||||
'(returncode==0 在「自动备份未开启」时也会出现,'
|
||||
'只看它会把「什么都没备份」当成「备份成功」)')
|
||||
ck('_do_backup 用 --run-now 调用', "'--run-now'" in uc)
|
||||
# ⚠️ backupd 的 call() 必须用 sudo -n 提权(2026-10-10 真机修的 bug):
|
||||
# 「更新前备份」由 drouter-web(drouter 用户)spawn backupd,这条链不带 sudo
|
||||
# 时 helper 继承 drouter 身份,写 /opt/drouter/backups(root:root)直接
|
||||
# [Errno 13] Permission denied,更新被卡死在备份这步。sudoers 已放行
|
||||
# drouter 免密跑 helper 白名单,所以 root/drouter 两种身份都通。
|
||||
# 判据查**语义**(subprocess 命令数组里真有 sudo 前缀),不能只查「出现 sudo 这个词」。
|
||||
_bkd = read(P('backend', 'drouter-backupd.py'))
|
||||
ck('backupd call() 用 sudo -n 提权(否则 drouter 身份写不进备份目录)',
|
||||
re.search(r"\['sudo',\s*'-n',\s*'/usr/bin/python3',\s*HELPER", _bkd) is not None)
|
||||
|
||||
# ④ P0-4:apply 必须有白名单且在写库前
|
||||
ck('apply_module 有模块白名单', 'APPLY_MODULES' in w)
|
||||
|
||||
@@ -74,9 +74,21 @@ def emit(line):
|
||||
|
||||
|
||||
def call(action, payload=None, timeout=CALL_TIMEOUT):
|
||||
"""调用 helper 的备份能力。
|
||||
|
||||
⚠️ 这里用 sudo -n 提权(2026-10-10 修):
|
||||
helper 写备份包要进 /opt/drouter/backups(root:root drwxr-xr-x),
|
||||
而 backupd 有两种运行身份 ——
|
||||
· 定时器(systemd timer)→ root,本来就能写;
|
||||
· 「更新前自动备份」→ 由 drouter-web(drouter 用户)spawn,
|
||||
这条链不带 sudo 时 helper 也继承 drouter 身份,写 .part 直接
|
||||
[Errno 13] Permission denied,更新被卡在备份这步。
|
||||
sudoers 已放行 drouter ALL=(root) NOPASSWD: python3 .../drouter-helper.py,
|
||||
所以 root 跑 sudo -n 免密、drouter 跑也走白名单提权,两种身份都通。
|
||||
"""
|
||||
try:
|
||||
p = subprocess.run(
|
||||
['/usr/bin/python3', HELPER, action,
|
||||
['sudo', '-n', '/usr/bin/python3', HELPER, action,
|
||||
json.dumps(payload or {}, ensure_ascii=False)],
|
||||
capture_output=True, text=True, timeout=timeout, errors='replace')
|
||||
return json.loads(p.stdout or '{}')
|
||||
|
||||
Reference in new issue
Block a user