Editing Privileged Configs From a Web UI, Safely
The station console can view, edit, back up, and restore the station's root-owned config files from a browser. Doing that without handing a web app root came down to one small helper and a tight sudo rule.
Overview
The console edits the station’s configuration files from the browser: view, edit, save, back up, restore, across the handful of files that drive the radio services. Those files are owned by root. A web app that can rewrite root-owned service configs is a serious attack surface, so the point of this piece is how that was done without giving the web app any real power.
The problem
The web app runs as an unprivileged service user. The config files it needs to write are root-owned and control running services. The naive version, running the web app as root or making the configs writable by the service user, turns a web bug into a root compromise. That was not acceptable.
The approach
All privileged file work goes through a small, fixed, root-owned helper program. The web app never writes a protected file itself. It stages a change and asks the helper to apply it.
The pieces that make that safe:
- The helper is root-owned and lives outside the web app’s writable tree, so the service user cannot rewrite the thing that runs as root.
- One scoped sudo rule lets the service user run only that one helper, nothing else.
- The helper owns the policy, not the caller. It holds the allowlist of which files may be touched, refuses anything outside it, and refuses a staged file that is not owned by the right user. No path traversal, no writing arbitrary files.
- It snapshots before every write and keeps a history, so any change can be rolled back.
- Owner and mode are preserved on write, so a config does not change hands.
Results
The console gets full view, edit, save, back up, and restore across the station’s configs, plus a one-file backup bundle of all of them, with a snapshot taken before every write. The web app still has no standing privilege. The only thing it can do with elevation is ask one fixed helper to apply an allowlisted, ownership- checked change.
Lessons
- Put the privilege in a small fixed helper, not the big app. The attack surface becomes one short program you can read in full, not a web stack.
- The helper owns the policy. Allowlist, ownership checks, and snapshots live where the caller cannot bypass them.
- Snapshot before every write. It is cheap, and it is the difference between a mistake and an outage.