#!/command/with-contenv sh # shellcheck shell=sh # Make supervise/ trees for ALL declared s6 services queryable and # controllable by the unprivileged hermes user (UID 10000). # # Background (PR #30136 review item I4): the entire s6 lifecycle # (s6-svc, s6-svstat, s6-svwait) is dispatched as the hermes user # inside the container (every Hermes runtime path runs under # ``s6-setuidgid hermes``). But s6-supervise creates each service's # ``supervise/`` and top-level ``event/`` directory with mode 0700 # owned by its effective UID — which is root, because s6-supervise # is spawned by s6-svscan running as PID 1. So unprivileged clients # get EACCES on every probe / control call against the slot. # # Two fixes, one in each registration path: # # 1. For RUNTIME-registered profile gateways (created via the s6 # runtime register hooks in profiles.py): the Python helper # ``_seed_supervise_skeleton`` pre-creates supervise/ + event/ + # supervise/control owned by hermes BEFORE s6-svscanctl -a fires. # s6-supervise's mkdir/mkfifo are EEXIST-safe, so it inherits our # ownership and never tries to chown back to root. # # 2. For STATIC s6-rc services (dashboard, main-hermes) declared at # image-build time under /etc/s6-overlay/s6-rc.d/*: these are # compiled by s6-rc at boot, and s6-supervise spawns BEFORE # cont-init.d gets to run — so by the time we're here, the # supervise/ tree is already there as root:root 0700. We chown # it here. s6-supervise will keep using the same files; it never # re-asserts ownership on a running service. # # This script runs as root after 01-hermes-setup but before # 02-reconcile-profiles, so the chowns are settled before the # Python reconciler walks the scandir. Lexicographic ordering # guarantees this — the suffix is unusual because we want to slot # in between 01 and the existing 02-reconcile-profiles without # renumbering both (which would be a churn-noise patch on its own). set -eu # /run/s6-rc/servicedirs holds the live, compiled service directories # for every static (s6-rc) service. Symlinks under /run/service/* # point here. Per-service supervise/ + event/ both need hermes # ownership for s6-svstat etc. to work as hermes. SVC_ROOT=/run/s6-rc/servicedirs if [ ! -d "$SVC_ROOT" ]; then echo "[supervise-perms] $SVC_ROOT not present; skipping" exit 0 fi for svc in "$SVC_ROOT"/*; do [ -d "$svc" ] || continue name=$(basename "$svc") # Skip s6-overlay-internal services (they need to stay root-only; # the s6rc-* helpers manage the supervision tree itself). case "$name" in s6rc-*|s6-linux-*) continue ;; esac # supervise/ tree — needed by s6-svstat / s6-svc. if [ -d "$svc/supervise" ]; then chown -R hermes:hermes "$svc/supervise" 2>/dev/null || \ echo "[supervise-perms] could not chown $svc/supervise" # 0710 = group searchable. ``s6-svstat`` only needs to openat # status, not list the dir, but giving the hermes group +x is # the minimum that lets group members access the contents. chmod 0710 "$svc/supervise" 2>/dev/null || true # supervise/control is a FIFO that s6-svc writes commands # into; the hermes user needs +w. Owner is already hermes # after the recursive chown above; widen perms to 0660 so # ``s6-svc`` works for any member of the hermes group too. if [ -p "$svc/supervise/control" ]; then chmod 0660 "$svc/supervise/control" 2>/dev/null || true fi fi # Top-level event/ dir — s6-svlisten1 / s6-svwait subscribe here. if [ -d "$svc/event" ]; then chown hermes:hermes "$svc/event" 2>/dev/null || \ echo "[supervise-perms] could not chown $svc/event" # Preserve s6's 03730 mode (setgid + g+rwx + sticky). chmod 03730 "$svc/event" 2>/dev/null || true fi done echo "[supervise-perms] chowned supervise/ trees for static s6-rc services"