feat: Phase 2 — systemd sandboxing, Bitcoin RPC localhost binding, Tailscale deprivilege

- Service runs as unprivileged `archipelago` user instead of root
- Added systemd sandboxing: ProtectSystem=strict, NoNewPrivileges, PrivateTmp,
  MemoryDenyWriteExecute, RestrictNamespaces, SystemCallFilter
- Bitcoin RPC rpcallowip restricted to localhost + Podman subnet (10.88.0.0/16)
- Tailscale container: removed --privileged, uses cap-drop ALL + cap-add NET_ADMIN/NET_RAW

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-18 00:42:29 +00:00
parent 909ad5f019
commit 430d174389
7 changed files with 51 additions and 15 deletions

View File

@@ -112,14 +112,14 @@
> backend can only do what it needs to do — like giving a bank teller access to the cash drawer but
> not the vault, the CEO's office, or the security cameras.
- [ ] **Create unprivileged archipelago user for backend**: SSH to 192.168.1.198:
- [x] **Create unprivileged archipelago user for backend**: SSH to 192.168.1.198:
1. Check if user exists: `id archipelago`. If it's the login user (UID 1000), create a separate service user: `sudo useradd -r -s /usr/sbin/nologin -d /var/lib/archipelago archipelago-svc` (UID will be in the system range).
2. Actually — the `archipelago` user already exists as UID 1000 (the login user). The backend should run as this user, NOT root. Change `/etc/systemd/system/archipelago.service` to use `User=archipelago` instead of `User=root`.
3. Fix file ownership: `sudo chown -R archipelago:archipelago /var/lib/archipelago/`.
4. The backend needs to talk to Podman. Since Podman is rootless for UID 1000, this should work. Test: `sudo -u archipelago podman ps`.
5. If Podman needs root for some operations, use `sudo` with specific commands only via sudoers — NOT running the entire backend as root.
- [ ] **Add systemd sandboxing to archipelago.service**: Edit `image-recipe/configs/archipelago.service`. Add these directives under `[Service]`:
- [x] **Add systemd sandboxing to archipelago.service**: Edit `image-recipe/configs/archipelago.service`. Add these directives under `[Service]`:
```ini
# Filesystem protection
ProtectSystem=strict
@@ -154,7 +154,7 @@
Deploy the service file to the server: `scp image-recipe/configs/archipelago.service archipelago@192.168.1.198:/tmp/ && ssh archipelago@192.168.1.198 'sudo cp /tmp/archipelago.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl restart archipelago'`.
Watch the journal for errors: `ssh archipelago@192.168.1.198 'sudo journalctl -u archipelago -n 50 --no-pager'`. If the service fails to start due to a denied syscall or path, adjust the sandboxing (e.g., add the path to `ReadWritePaths` or the syscall group to `SystemCallFilter`). Iterate until the service starts cleanly.
- [ ] **Bind Bitcoin RPC to localhost only**: SSH to 192.168.1.198. Edit the bitcoin-knots container's start command:
- [x] **Bind Bitcoin RPC to localhost only**: SSH to 192.168.1.198. Edit the bitcoin-knots container's start command:
1. Find where bitcoin-knots is started (in `scripts/first-boot-containers.sh` or via `podman inspect bitcoin-knots`).
2. Change `-rpcbind=0.0.0.0:8332` to `-rpcbind=127.0.0.1:8332 -rpcbind=::1:8332`.
3. Change `-rpcallowip=0.0.0.0/0` to `-rpcallowip=127.0.0.1/32 -rpcallowip=10.88.0.0/16` (the 10.88.x.x is Podman's default network — containers need to reach Bitcoin RPC).
@@ -162,7 +162,7 @@
5. Verify containers on the Podman network can still reach it: `sudo podman exec lnd bitcoin-cli -rpcconnect=bitcoin-knots -rpcuser=... getblockchaininfo`.
6. Verify external access is blocked: from another machine on the LAN, `curl http://192.168.1.198:8332` should fail/timeout.
- [ ] **Reduce Tailscale container privileges**: In `scripts/first-boot-containers.sh`, find the Tailscale container creation (line ~460). Replace `--privileged` with:
- [x] **Reduce Tailscale container privileges**: In `scripts/first-boot-containers.sh`, find the Tailscale container creation (line ~460). Replace `--privileged` with:
```bash
--cap-drop=ALL \
--cap-add=NET_ADMIN \
@@ -174,7 +174,7 @@
```
Recreate the Tailscale container on the server. Verify Tailscale still works: `sudo podman exec tailscale tailscale status`.
- [ ] **Verify Phase 2 — Systemd hardening active**: Run these checks:
- [x] **Verify Phase 2 — Systemd hardening active**: Run these checks:
1. `sudo systemctl show archipelago | grep -E "ProtectSystem|NoNewPrivileges|PrivateTmp"` — should show `strict`, `yes`, `yes`.
2. `sudo systemctl status archipelago` — should be active and running.
3. `ss -tlnp | grep 8332` — Bitcoin RPC should show `127.0.0.1:8332`, NOT `0.0.0.0:8332`.