fix(s6): make profile gateway log parent writable (#46291)

* fix(gateway): chown logs/gateways parent so late-added profiles can log

The per-profile log service script created $HERMES_HOME/logs/gateways/
via 'mkdir -p' but only chowned the leaf logs/gateways/<profile>. When
the first log service boots in root context, the gateways/ parent stays
root:root; every profile registered later runs its log service as the
dropped hermes user, 'mkdir -p' fails with EACCES, and s6-log enters a
sub-second fatal crash-loop flooding the container log. The stage2
recursive heal does not catch it either: it is gated on needs_chown,
which is false when the top-level $HERMES_HOME is already hermes-owned.

Two complementary fixes:

- service_manager._render_log_run: chown the gateways/ parent
  (non-recursively) before the leaf chown. Runs on every root-context
  boot, so it also heals volumes already poisoned by older images.
- docker/stage2-hook.sh: seed logs/gateways in the as_hermes mkdir -p
  block; cont-init runs before any service starts, so the parent
  already exists hermes-owned when the first log/run does 'mkdir -p'.

The needs_chown repair loop needs no twin entry: it already chowns
logs/ recursively, which covers logs/gateways.

Fixes #45258

* chore(release): map salvaged contributor

---------

Co-authored-by: tangtaizhong666 <tangtaizhong792@gmail.com>
This commit is contained in:
Teknium
2026-06-15 13:47:05 +10:00
committed by GitHub
co-authored by tangtaizhong666
parent f795513782
commit 61ee2dbfdb
5 changed files with 106 additions and 0 deletions
+35
View File
@@ -950,3 +950,38 @@ def test_s6_stop_tolerates_marker_write_failure(monkeypatch, s6_scandir):
mgr.stop("gateway-coder") # must not raise
assert any(cmd[0] == "s6-svc" and "-d" in cmd for cmd in svc_calls)
def test_s6_log_run_chowns_gateways_parent(s6_scandir, fake_subprocess_run) -> None:
"""The log/run script must chown the logs/gateways/ parent, not just the leaf.
Regression guard for #45258: `mkdir -p` creates the gateways/ parent
root-owned on a root-context boot, and a leaf-only chown leaves it that
way. Every profile registered later then runs its log service as the
dropped hermes user and s6-log crash-loops on `mkdir: Permission denied`.
"""
mgr = S6ServiceManager(scandir=s6_scandir)
mgr.register_profile_gateway("coder")
log_text = (s6_scandir / "gateway-coder" / "log" / "run").read_text()
parent_chown = 'chown hermes:hermes "$HERMES_HOME/logs/gateways"'
assert parent_chown in log_text, (
"log/run must chown the logs/gateways parent so profiles added "
f"after a root-context boot can create their leaf dirs. Saw: {log_text!r}"
)
# Non-recursive on purpose: sibling profile leaf dirs are each managed
# by their own log/run; a recursive parent chown would race them.
assert 'chown -R hermes:hermes "$HERMES_HOME/logs/gateways"' not in log_text
# Ordering: mkdir creates the parent, then the parent chown repairs its
# ownership, then the leaf chown — all before s6-log execs.
mkdir_idx = log_text.index('mkdir -p "$log_dir"')
parent_idx = log_text.index(parent_chown)
leaf_idx = log_text.index('chown -R hermes:hermes "$log_dir"')
exec_idx = log_text.index("s6-log 1 ")
assert mkdir_idx < parent_idx < leaf_idx < exec_idx
# The parent path must be a runtime env expansion, never a baked-in
# absolute path (same contract as the log_dir itself).
assert '/opt/data/logs/gateways"' not in log_text