fix(desktop): hand helper-created runtime files to the GUI owner - #220
Merged
Conversation
The privileged Linux data-path helper (root or caps-only) writes its run-dir state, generated engine configs, core logs and its own log as root. Sweep those known files back to the GUI owner uid after each file-creating request so a future unprivileged in-process owner can operate over the same paths without silent EACCES. The hand-off is a fixed list derived from DesktopPaths/BackendPaths fields (plus the test core's per-request log path), chowned only when an owner uid is set and differs from the helper's own euid, so a caps-only wrapper already running as the user stays a no-op. Ownership only, exact regular-file paths, never a directory.
This was referenced Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On Linux the privileged data-path helper (pkexec root, or the packaged caps wrapper) creates runtime files that end up root-owned: the ephemeral run-dir state (
core.pid,service-state,tun-engine, …), the generated engine configs (hev.yml,tun2socks.yml,singbox-bridge.json), the core logs under datadir (xray.log,singbox.log,tun-engine.log), the helper's own log, and per-test-core logs. Hand their ownership to the unprivileged GUI user after every file-creating request.This is standalone hardening (the GUI user can rotate/remove its own logs and state) and the precondition for the follow-up that skips the helper entirely in the non-tun proxy modes: an unprivileged in-process data-path owner must be able to read and replace these files without silent EACCES.
Mechanics:
Server::dispatchclassifies each request (Handoff::for_request) before consuming it, and afterStartDataPath/StopDataPathchowns the fixed path list to the owner; afterSpawnTestCoreit chowns just that request's log (its port is dynamic).DesktopPaths(helper_owned_files) — every entry derived from a paths field, no hand-built strings; GUI-written inputs (engine, the built core configs,daemon.log) are excluded and the exclusion is test-pinned.run_diris created root-side at boot.helper_owned_dirshands over exactly two inodes:run_diritself and itskasumi-proxynamespace parent (only when it is ours by name). Nothing beneath them is swept; datadir's inode is never touched (the GUI creates it).owner_uidis unset or equals the helper's own euid (hand_off_target) — the packaged caps-only wrapper already runs as the GUI user, so it never chowns needlessly.--owner-uid, used to chown the control socket); no new plumbing.%LOCALAPPDATA%inherit ACLs from the parent; there is no service-path gating plan). All sweep machinery iscfg(target_os = "linux").Affected layer
frontend/— React Web UIcrates/·src-tauri/— Rust core / backend / Tauri desktopmodule/— Android installable zip (thin launcher over the Rust daemon)scripts/— build / release helpers.github/Verification
Rust (
crates/·src-tauri/):cargo fmt --all --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace— one failure:net::tests::tcp_ping_fails_on_unreachable_host, the known environment flake (an active VPN tun on the dev machine captures the TEST-NET-1 connect); fails identically onmainNew tests: the hand-off list contains exactly the helper-written names and none of the GUI-written ones, plus the directory list (run_dir + namespace parent, never datadir);
hand_off_targetno-ops for a same-user run;Handoff::for_requestclassifies the file-creating requests.Checklist
module/bin/<abi>/,geoip/geosite, builtmodule/webroot/,src-tauri/gen/— all gitignored on purpose)frontend/src/generated/was regenerated from Rust, not hand-edited (untouched)i18n/en.tsand every locale file updated (no user-visible strings changed)kasumi-proxy,Kasumi Proxy, camelCase)Notes for reviewers
hand_files_to_ownerre-resolvesDesktopPathsper Start/Stop (low frequency); the per-test-core path deliberately skips that resolve.HELPER_LOG_FILEconst so the writer (hlog) and the sweep can't drift apart.