Runbook — the unconfined admin-glue instance (card #106)¶
The second of the two ductile roles (PrivSec ADR data-plane/admin split). Pairs with the enforced data-plane gateway in privsep-thinkpad-enforce.md. Template unit:
deploy/systemd/ductile-admin.service.
Why a second instance¶
Some automation legitimately needs privilege an unprivileged account uid cannot hold, so it cannot
live behind the privsep wall — but it's coupled to ductile's scheduling + pipeline + *_notify chain,
so it shouldn't leave ductile either. The answer is a separate, unconfined instance:
enforced data-plane (ductile.service) |
admin-glue (ductile-admin.service) |
|
|---|---|---|
| runs as | ductile (984) + CAP_SETUID/SETGID |
a privileged user in the docker group |
| posture | enforce (drops plugins to accounts) | unconfined (hygiene-only, ADR Layer 1a) |
| config | /etc/ductile |
/etc/ductile-admin |
| listen | :8081 |
a different port, e.g. :8092 |
| plugins | confinable data plugins | the unconfinable set ↓ |
Admin-instance members (move these OFF the enforced gateway): astro_rebuild_staging (docker
compose), apt_security_check, stopwatch_perf_daily, file_handler (reads /home), fabric
(bound to ~/.config/fabric), and the *_notify plugins triggered by them (notify-locality: a notify
lives on the same instance as its trigger). Each *_notify keeps reading the discord webhook from its
config/env as today — unconfined, so the home .env is readable.
Stand-up (on the host, as root)¶
# 1. a privileged service user in the docker group (host-specific; e.g.)
sudo useradd --system --home /var/lib/ductile-admin --shell /usr/sbin/nologin ductile-admin
sudo usermod -aG docker ductile-admin
sudo install -d -o ductile-admin -g ductile-admin -m0750 /etc/ductile-admin /var/lib/ductile-admin
# 2. its config: the unconfinable plugins, NO accounts: map (→ unconfined), its own port.
# Copy the relevant plugin blocks from the legacy config; keep their home/docker paths
# (this instance CAN read them). Set api.listen to a free port (e.g. localhost:8092).
sudoedit /etc/ductile-admin/config.yaml
# 3. install the template unit (edit User=/paths/port first) + start
sudo install -m0644 deploy/systemd/ductile-admin.service /etc/systemd/system/ductile-admin.service
sudo systemctl daemon-reload && sudo systemctl enable --now ductile-admin
journalctl -u ductile-admin -n 30 --no-pager # expect "privsep posture: UNCONFINED ..." (by design)
Notes¶
- It is correct for this instance to log UNCONFINED at boot (the #101 posture line) — that's the
intended hygiene-only state for trusted, privileged admin glue. Don't add an
accounts:map here. - Once both instances are green and the old
--user ductile-localis fully superseded, decommission it. - Confinable data plugins do NOT belong here — they go on the enforced gateway. This instance is strictly the privileged-by-necessity automation.