Skip to content

feat(linux): 提权 helper(systemd + 降权拉核 root 受管核)根治 TUN/更新反复输密码#247

Merged
Sway-Chan merged 2 commits into
dododook:mainfrom
Sway-Chan:feat/linux-privileged-helper
Jul 2, 2026
Merged

feat(linux): 提权 helper(systemd + 降权拉核 root 受管核)根治 TUN/更新反复输密码#247
Sway-Chan merged 2 commits into
dododook:mainfrom
Sway-Chan:feat/linux-privileged-helper

Conversation

@Sway-Chan

Copy link
Copy Markdown
Collaborator

问题

Linux TUN 模式此前每次开 TUN、每次软件更新后开 TUN 都弹 pkexec 授权框

根因:ensureCapabilitiessetcapCAP_NET_ADMIN 挂在 sing-box 二进制 inode 的 security.capability xattr 上,而换核 / 软件更新(ensureWritableCorerename 覆盖)换了 inode → caps 蒸发 → 必须重新提权。能力绑在“会被替换的文件”上是病根。

方案

对齐 macOS(launchd daemon) / Windows(Service) 的常驻 helper 模型,把能力从“易变 inode”搬到“常驻进程 + 稳定 root 目录”:

  • helper-linux/(Go,纯标准库):root systemd system service。SO_PEERCRED 鉴权 + 授权 uid 列表;startsetuid登录用户 + AmbientCapabilities=CAP_NET_ADMIN(+RAW/+BIND) 拉核 —— 能力挂进程 ambient set 不挂文件,换核/软件更新天然免密;核仍以登录用户跑,userData 的 config/cache/log 属主天然对。
  • root 受管核/usr/local/lib/flowz/core):安装播种 + install-core(sha256 校验、socket 免密)更新,helper 路径锁只跑锁定核 → 不可篡改、一份共享、根除“借 helper 给任意自有二进制赋 cap”的提权面。与 macOS 受保护目录同构。
  • 接入LinuxServiceHelper implements IPrivilegedHelper(socket 客户端 + pkexec 一次授权装/卸 systemd 服务 + installCore);ProxyManager 启/停接入 helper 分支,未装 helper 回退现有 setcap+pkexec,零回归CoreUpdateService install-core 泛化 Linux;ResourceManager root 受管核路径优先;设置页 helper 卡片 + promptHelperGate Linux 分支 + i18n 5 语。

安全

SO_PEERCRED 为唯一鉴权(内核背书 uid)+ 授权 uid 列表(堵任意本地用户白拿 cap);config 属主必须 == 对端 uid;freeport/cleanup 限对端 uid(不跨用户杀);IP 转发随核 start/stop 复位;SIGTERM 收割等在途(无孤儿);setuid 保留补充组。helper 本体 CapabilityBoundingSet 收窄留后续(P2)。

验证

  • gate 全绿tsc 0 错 / eslint 0 问题 / go build+vet+test / jest 2352 通过。
  • 真机全链路(Ubuntu 26.04 VM):非 root 用户经 helper setuid+ambientCapAmb=cap_net_admin,cap_net_raw,cap_net_bind_service)起核 → auto_route 接管出网 → install-core 运行中原子热换(sha 匹配)→ 停核路由复位 → 卸载零残留。

待办(不阻塞本 PR 的运行时正确性)

  • 桌面 pkexec 图形授权框 / 设置卡片 UX 手动点验(GUI 层,headless 未覆盖)。
  • 多用户 A/B 并发授权(单用户机制已验,并发未专测)。
  • helper 本体 CapabilityBoundingSet/NoNewPrivileges 收窄(P2)。

Sway-Chan added 2 commits July 2, 2026 09:51
Linux TUN 此前每次开 TUN、每次软件更新后开 TUN 都弹 pkexec。根因:setcap 把
CAP_NET_ADMIN 挂在会被换核/更新替换的二进制 inode 上(security.capability xattr
随 inode 蒸发),故换核必重新提权。

方案(对齐 macOS launchd / Windows Service 的常驻 helper 模型):
- helper-linux/:root systemd system service。SO_PEERCRED 鉴权 + 授权 uid 列表;
  start 时 setuid 回登录用户 + AmbientCaps=CAP_NET_ADMIN(+RAW/+BIND) 拉核 —— 能力挂
  进程 ambient set 不挂文件,换核/软件更新天然免密。纯标准库。
- 核放 root 受管目录(/usr/local/lib/flowz/core),安装播种 + install-core(sha256
  校验、socket 免密)更新,路径锁只跑锁定核 —— 不可篡改、一份共享、根除“任意自有
  二进制白拿 CAP_NET_ADMIN”提权面。
- LinuxServiceHelper 实现 IPrivilegedHelper(socket 客户端 + pkexec 一次授权装/卸
  systemd 服务 + installCore);ProxyManager 启/停接入 helper 分支(未装回退现有
  setcap,零回归);CoreUpdateService install-core 泛化 Linux;ResourceManager root
  受管核路径优先;设置页卡片 + promptHelperGate Linux 分支 + i18n 5 语。
- 安全:SO_PEERCRED 唯一鉴权 + 授权列表;config 属主==对端 uid;freeport/cleanup 限
  对端 uid;IP 转发随核复位;SIGTERM 收割等在途;setuid 保留补充组。

验证:Ubuntu 26.04 VM 真机全链路通过 —— 非 root 经 helper setuid+ambient
(CapAmb=cap_net_admin,cap_net_raw,cap_net_bind_service) 起核、auto_route 接管出网、
install-core 运行中原子热换、停核路由复位、卸载零残留。gate 全绿(tsc / eslint /
go build+vet+test / jest 2352)。
承 root 受管核模型补齐两处(原始需求「避免软件更新反复输密码」的软件更新侧):

1. 应用更新后受管核自愈(§5 最低版本守卫接通 Linux root 核模型):
   ensureWritableCore(force) Linux 分支在「helper 就绪 + 受管核在位」时经
   LinuxServiceHelper.installCore 免密刷新 root 受管核(镜像 darwin 受保护目录腿);
   无 helper / 受管核不在位 → 退化刷 userData 兜底核(setcap 零回归);失败 warn 不抛
   (版本闸门兜底)。修「探测 userData 核、实跑受管核」错位:getSingBoxPath / 刷新腿 /
   路径锁三点共用 linuxManagedCoreUsable() 谓词,Linux 返回值恒解析 getSingBoxPath(),
   使 §5 探测 / 诚实校验 / 实跑对准同一核(否则 install-core 失败会谎报成功、换核后版本记录读旧值)。

2. deb 更新一次授权说明框:deb 装 /opt 需 root、pkexec 弹 polkit 通用框(文案不可改)
   → installUpdate 顶部(停代理之前 = 取消真 no-op)对 deb 形态先弹 app 内说明框
   (confirmDebElevation + i18n 五语),讲清「装软件一次授权 ≠ 每次开 TUN;helper 装一次
   永久免密;想更新也免密改用 AppImage」。仅 deb 形态,AppImage / mac / win 零影响。

单测:core-reseed-linux-helper(受管核刷新 5 例)+ update-deb-confirm(门控/映射/取消 no-op 7 例)
+ linux-service-helper installCore 协议。gate 全绿(tsc 0 / eslint 0 / jest 2365 / go 未改)。
@Sway-Chan Sway-Chan merged commit 2312113 into dododook:main Jul 2, 2026
2 checks passed
@Sway-Chan Sway-Chan deleted the feat/linux-privileged-helper branch July 3, 2026 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant