refactor: introduce two-user daemon architecture with socket-based communication
- Add daemon/ module with aiohttp server, sync client, and handler registry - Add daemon/handlers/ for privileged operations (acme, dnsmasq, firewall, logs, nginx, wireguard) - Add system/acme-deploy.py, vacuum-walld sudoers and systemd service - Update API routes to use daemon client instead of lib/ directly - Update lib/, tests/, and webui/ for new architecture - Update docs and deployment scripts
This commit is contained in:
+19
-14
@@ -51,6 +51,7 @@ All settings that can be passed as an environment variable also have a CLI flag
|
||||
| `--dev` | -- | No | Development mode: auto-detects repo owner as service user, skips safety warning. |
|
||||
| `--wan-iface` | `WAN_IFACE` | No | WAN interface name. Auto-detected from default gateway. |
|
||||
| `--lan-ifaces` | `LAN_IFACES` | No | LAN interface names, comma-separated. Auto-detected from non-loopback, non-WAN interfaces. |
|
||||
| `--force-venv` | — | No | Force recreation of the Python virtual environment. |
|
||||
|
||||
Run `./install.sh --help` for full usage.
|
||||
|
||||
@@ -75,13 +76,15 @@ The systemd service unit files and sudoers whitelist are rendered from Jinja2 te
|
||||
|
||||
The installer performs the following steps automatically:
|
||||
|
||||
- **Package installation**: Installs firewalld, nginx, dnsmasq, avahi-daemon, wireguard-tools, python3, Flask, pip, jq, curl, iptables, nftables, and apache2-utils.
|
||||
- **System user creation**: Creates a dedicated system user (default: `vacuum-wall`, configurable via `USER_NAME`) with a nologin shell that owns the project data and runs the WebUI service.
|
||||
- **Python venv**: Creates or recreates the Python virtual environment and installs project dependencies.
|
||||
- **Package installation**: Installs firewalld, nginx, dnsmasq, avahi-daemon, wireguard-tools, python3, python3-pip, jq, curl, iptables, nftables, and apache2-utils.
|
||||
- **Shared group creation**: Creates a shared system group (`vacuum-wall`) both service users belong to.
|
||||
- **Daemon user creation**: Creates `vacuum-walld` (derived from WebUI user name) — a system user with `NOPASSWD` sudo access for privileged operations. Owns the project directory and daemon socket.
|
||||
- **WebUI user creation**: Creates a dedicated system user (default: `vacuum-wall`, configurable via `USER_NAME`) with zero sudo access. Communicates with the daemon via Unix socket.
|
||||
- **Python venv**: Creates the Python virtual environment and installs project dependencies. Skips if already present (use `--force-venv` to recreate).
|
||||
- **acme.sh installation**: Copies the vendored acme.sh client to the data directory for ACME certificate management. Skips if already installed.
|
||||
- **Directory setup**: Creates config directories under `config/` for each subsystem's declarative JSON, and data directories under `data/` for generated files (nginx sites, dnsmasq fragments, firewall backup, WireGuard config).
|
||||
- **Template rendering**: Renders system template files (`systemd/*.service`, `sudoers.d/`) via Jinja2, substituting `USER_NAME`, `INSTALL_DIR`, and `ACME_HOME`. Installed systemd and sudoers files contain no hardcoded values.
|
||||
- **Sudoers whitelist**: Installs a restrictive sudoers file at `/etc/sudoers.d/vacuum-wall` allowing the configured user to run only the specific privileged commands needed for firewall, nginx, and dnsmasq management. Validates syntax with `visudo -cf`.
|
||||
- **Sudoers whitelist**: Installs a restrictive sudoers file at `/etc/sudoers.d/vacuum-walld` granting the daemon user `NOPASSWD` sudo for only the specific privileged commands needed for firewall, nginx, dnsmasq, and acme.sh management. Validates syntax with `visudo -cf`. The WebUI user's sudoers file (`/etc/sudoers.d/vacuum-wall`) is empty — it has no sudo access.
|
||||
- **IP forwarding**: Enables `net.ipv4.ip_forward=1` in sysctl.conf and applies it at runtime, required for routing traffic between zones. Appends only if not already present.
|
||||
- **Firewalld initialization**: Starts and enables firewalld. Opens HTTP, HTTPS, and SSH services on the public zone for management access.
|
||||
- **Dnsmasq initialization**: Starts and enables dnsmasq for future DHCP/DNS serving on internal interfaces.
|
||||
@@ -91,22 +94,23 @@ The installer performs the following steps automatically:
|
||||
- **Credentials**: Generates an htpasswd file using `apache2-utils` (with a Python fallback) for the management proxy's basic auth. Updates existing file if already present.
|
||||
- **Initial nginx config**: Writes `$PROJECT_DIR/config/nginx/config.json` with the management domain and auth settings pre-configured. Skips if the file already exists (preserves user-customized config).
|
||||
- **Initial firewall config**: Writes `$PROJECT_DIR/config/firewall/config.json` with auto-detected WAN/LAN interfaces. Skips if the file already exists.
|
||||
- **Systemd units**: Installs three units (rendered from Jinja2 templates):
|
||||
- **Systemd units**: Installs four units (rendered from Jinja2 templates):
|
||||
- `vacuum-walld.service` — the privileged background daemon (aiohttp, daemon socket).
|
||||
- `vacuum-wall.service` — the Flask WebUI backend.
|
||||
- `vacuum-wall-acme.service` — the certificate renewal oneshot.
|
||||
- `vacuum-wall-acme.timer` — periodic timer that triggers cert renewals.
|
||||
- **Firewalld zones**: Creates initial zones:
|
||||
- `internal` — trusted LAN zone with DHCP, DNS, and NTP services allowed.
|
||||
- `vpn` — WireGuard tunnel zone.
|
||||
- **Service startup**: Enables and starts/restarts nginx and the WebUI service, and enables the ACME renewal timer. nginx is reloaded (or restarted) to pick up any config changes.
|
||||
- **Service startup**: Enables and starts/restarts nginx, the daemon (`vacuum-walld`), the WebUI (`vacuum-wall`), and the ACME renewal timer. nginx is reloaded (or restarted) to pick up any config changes.
|
||||
- **ACME registration**: Registers the ACME account with the provided email via acme.sh.
|
||||
|
||||
### Idempotent Re-Runs
|
||||
|
||||
`install.sh` is fully idempotent and safe to run multiple times. Re-running the script:
|
||||
|
||||
- Rebuilds the Python venv and reinstalls dependencies
|
||||
- Restarts `vacuum-wall` and reloads `nginx` to pick up changes
|
||||
- Skips the Python venv (use `--force-venv` to rebuild)
|
||||
- Restarts `vacuum-walld`, `vacuum-wall`, and reloads `nginx` to pick up changes
|
||||
- Preserves existing SSL certificates (skips self-signed generation if a cert exists)
|
||||
- Preserves existing `config.json` files (skips initial write if file exists)
|
||||
- Safely updates `htpasswd` (uses update mode instead of create mode)
|
||||
@@ -122,7 +126,7 @@ This makes it safe for development workflows: simply run `bash install.sh` again
|
||||
After the installer completes, confirm all services are running:
|
||||
|
||||
```bash
|
||||
systemctl status vacuum-wall nginx firewalld dnsmasq
|
||||
systemctl status vacuum-walld vacuum-wall nginx firewalld dnsmasq
|
||||
```
|
||||
|
||||
Each should be active (running). The `vacuum-wall-acme.timer` should also be active (waiting).
|
||||
@@ -249,8 +253,8 @@ Vacuum Wall includes integrated WireGuard server support for VPN access.
|
||||
Check service logs and configuration:
|
||||
|
||||
```bash
|
||||
journalctl -u vacuum-walld --no-pager -n 50
|
||||
journalctl -u vacuum-wall --no-pager -n 50
|
||||
journalctl -u nginx --no-pager -n 50
|
||||
nginx -t
|
||||
```
|
||||
|
||||
@@ -268,7 +272,7 @@ systemctl status firewalld
|
||||
If firewalld is not running, start it with `systemctl start firewalld`. Check that the sudoers whitelist is valid:
|
||||
|
||||
```bash
|
||||
visudo -cf /etc/sudoers.d/vacuum-wall
|
||||
visudo -cf /etc/sudoers.d/vacuum-walld
|
||||
```
|
||||
|
||||
### Certificate Issuance Fails
|
||||
@@ -280,7 +284,7 @@ ACME validation via the ACME provider requires:
|
||||
- The ACME email was registered correctly. Check with:
|
||||
|
||||
```bash
|
||||
su -s /bin/bash "$USER_NAME" -c "~/.acme.sh/acme.sh --list"
|
||||
su -s /bin/bash "$USER_DAEMON_NAME" -c "~/data/acme/acme.sh --list"
|
||||
```
|
||||
|
||||
If port 80 is blocked or the DNS record hasn't propagated yet, wait and retry. The ACME timer will also attempt renewal automatically.
|
||||
@@ -308,10 +312,11 @@ Verify that:
|
||||
|
||||
| Component | Service | Config Location |
|
||||
|---|---|---|
|
||||
| Daemon (privileged) | `vacuum-walld.service` | `daemon/` |
|
||||
| WebUI backend | `vacuum-wall.service` | `webui/` |
|
||||
| Reverse proxy | `nginx` | `/etc/nginx/conf.d/vacuum-wall-mgmt.conf` |
|
||||
| Firewall | `firewalld` | Managed via WebUI and `firewall-cmd` |
|
||||
| DHCP/DNS | `dnsmasq` | `config/dnsmasq/` |
|
||||
| VPN | wireguard-tools | `config/wireguard/` |
|
||||
| Certificates | `vacuum-wall-acme.timer` | `~/.acme.sh/` |
|
||||
| Sudoers | — | `/etc/sudoers.d/vacuum-wall` |
|
||||
| Certificates | `vacuum-wall-acme.timer` | `$PROJECT_DIR/data/acme/` |
|
||||
| Sudoers (daemon) | — | `/etc/sudoers.d/vacuum-walld` |
|
||||
Reference in New Issue
Block a user