Give the unit each managed target's roots; record host preprovisioning

ProtectSystem=full makes /etc read-only, so Idunn could not create Heimdall's
runtime bundle under /etc/gamecult/heimdall/runtime. A target whose roots are
not in ReadWritePaths is a target Idunn cannot deploy. Every migration adds
exactly three paths -- runtime root, state root, release root -- which keeps
the write set enumerated and is the honest blast radius of running as root.

Also records the per-target preprovisioning Idunn requires rather than
creates, each discovered by a failed deployment: the state root must be
root:<state_group> mode 2770 so the workload's dynamic user inherits group
access through setgid; the runner cache must be 0700 owned by the runner's
container uid, not the source identity, with a root-owned parent (/srv/build
is idunn:idunn and fails); the frozen source stage must be root-owned,
because source is frozen as idunn and then copied into a root-owned
actuation stage; and the binding's docker network must exist.

None of these are Idunn being fussy. Each is a check that a binding cannot
widen access by naming a path someone else can write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-06 19:52:56 +02:00
commit 5a12ec2af8
2 changed files with 24 additions and 0 deletions

View file

@ -71,6 +71,10 @@ ReadWritePaths=/etc/nginx/idunn-stream-routes
ReadWritePaths=/etc/systemd/system
ReadWritePaths=/var/lib/gamecult/idunn /var/lib/gamecult/idunn-authority
ReadWritePaths=/srv/build
# One entry per managed target: its runtime root, state root and release root.
# A target Idunn cannot write is a target it cannot deploy, so this list grows
# by exactly three paths per migration and is the honest blast radius.
ReadWritePaths=/etc/gamecult/heimdall /var/lib/gamecult/heimdall /srv/heimdall/app
# Odin's directory is a managed target's, and is read-only on purpose: Idunn
# consumes Odin's published topology correlation to gate *deployment* promotion
# and nothing else. Continuity never consults it -- see