fix: two-user model bug fixes and docs
This commit is contained in:
+5
-5
@@ -2,22 +2,22 @@
|
||||
|
||||
## Privilege Model
|
||||
|
||||
Vacuum Wall uses two distinct system users bridged by a shared group (`vacuum-wall`):
|
||||
Vacuum Wall uses two distinct system users bridged by a shared group (the WebUI user's primary group):
|
||||
|
||||
- **`vacuum-walld`** (daemon user): Runs the `vacuum-walld` background daemon, which is the only process with sudo access. The daemon communicates with the WebUI over a Unix socket at `data/daemon.sock`. All privileged operations — firewall rule changes, nginx reloads, dnsmasq config writes, WireGuard tunnel management — are executed by the daemon through a restricted sudo whitelist at `/etc/sudoers.d/vacuum-walld`.
|
||||
- **`vacuum-wall`** (WebUI user): Runs the Flask management WebUI. Has **zero** sudo access. If the WebUI process is compromised, an attacker cannot invoke sudo directly — they are confined to the sandboxed Flask process with no privilege escalation path.
|
||||
- **WebUI user** (default: repo owner in `--dev` mode): Runs the Flask management WebUI. Has **zero** sudo access. If the WebUI process is compromised, an attacker cannot invoke sudo directly — they are confined to the sandboxed Flask process with no privilege escalation path.
|
||||
|
||||
ACME certificate operations via `acme.sh` run as the WebUI user (`vacuum-wall`) — not as root, and not as the daemon user. The automated renewal timer (`vacuum-wall-acme.timer`) runs `acme.sh --cron` as `{{ USER_NAME }}`. When triggered from the WebUI or daemon, acme.sh also runs as the non-root process invoking it, using webroot validation that does not require binding to privileged ports.
|
||||
ACME certificate operations via `acme.sh` run as the WebUI user — not as root, and not as the daemon user. The automated renewal timer (`vacuum-wall-acme.timer`) runs `acme.sh --cron` as `{{ USER_NAME }}`. When triggered from the WebUI or daemon, acme.sh also runs as the non-root process invoking it, using webroot validation that does not require binding to privileged ports.
|
||||
|
||||
This design follows the principle of least privilege: only the daemon process holds sudo access, and only for explicitly enumerated commands. The WebUI user is completely isolated from sudo.
|
||||
|
||||
## Communication Between WebUI and Daemon
|
||||
|
||||
The WebUI communicates with the daemon via synchronous HTTP requests over a Unix socket (`data/daemon.sock`), owned by `vacuum-walld:vacuum-wall` with mode `0660`. The shared group membership allows the WebUI user to connect to the socket. The daemon runs an `aiohttp` server that routes requests to handler modules (`daemon/handlers/*.py`), which execute the privileged commands.
|
||||
The WebUI communicates with the daemon via synchronous HTTP requests over a Unix socket (`data/daemon.sock`), owned by `vacuum-walld:<group>` with mode `0660`. The shared group membership allows the WebUI user to connect to the socket. The daemon runs an `aiohttp` server that routes requests to handler modules (`daemon/handlers/*.py`), which execute the privileged commands.
|
||||
|
||||
## Sudo Whitelist
|
||||
|
||||
The file `/etc/sudoers.d/vacuum-walld` grants the daemon user (`vacuum-walld`) passwordless sudo access to a strict set of commands. The WebUI user (`/etc/sudoers.d/vacuum-wall`) has no sudo entries. Each daemon entry is scoped to a single binary with allowed arguments:
|
||||
The file `/etc/sudoers.d/vacuum-walld` grants the daemon user (`vacuum-walld`) passwordless sudo access to a strict set of commands. The WebUI user has no sudo access. Each daemon entry is scoped to a single binary with allowed arguments:
|
||||
|
||||
| Category | Whitelisted Command | Purpose |
|
||||
|---|---|---|
|
||||
|
||||
Reference in New Issue
Block a user