diff --git a/docs/dress-rehearsal.md b/docs/dress-rehearsal.md index ba0049a..9710ace 100644 --- a/docs/dress-rehearsal.md +++ b/docs/dress-rehearsal.md @@ -41,7 +41,8 @@ candidate as running after the failed attempt. The runner service account has no Proxmox, Stable, production, website, or release credentials and is not a member of privileged container/runtime groups. -Its start hook rejects every repository, workflow, job, and event except the +Its root-owned `runner-job-started.sh` start hook rejects every repository, +workflow, job, and event except the Phase 2 deployment job resulting from successful `CI` for `main`. Ordinary PR workflows do not carry the unique runner label. The runner is registered with `--no-default-labels`, so generic `self-hosted`, OS, or architecture selectors diff --git a/ops/dress-rehearsal/1helm-candidate-install b/ops/dress-rehearsal/1helm-candidate-install index f05b531..ffcdba7 100755 --- a/ops/dress-rehearsal/1helm-candidate-install +++ b/ops/dress-rehearsal/1helm-candidate-install @@ -43,7 +43,8 @@ if [[ "$LOCAL_PROOF" -eq 0 ]]; then fi # Consume the fixed inbox payloads after the root copy. The unprivileged runner # can then create a fresh set without owning retained candidate bytes. -unlink "$INBOX/candidate.json" "$INBOX/candidate.tgz" +unlink "$INBOX/candidate.json" +unlink "$INBOX/candidate.tgz" [[ "$LOCAL_PROOF" -eq 1 ]] || unlink "$INBOX/provenance.bundle.json" validate_args=(validate "$work/candidate.json" "$work/candidate.tgz" "$work/verified.json") diff --git a/ops/dress-rehearsal/runner-job-started b/ops/dress-rehearsal/runner-job-started.sh similarity index 100% rename from ops/dress-rehearsal/runner-job-started rename to ops/dress-rehearsal/runner-job-started.sh diff --git a/ops/dress-rehearsal/runner.service.override.conf b/ops/dress-rehearsal/runner.service.override.conf index 2f71b2d..adf52c7 100644 --- a/ops/dress-rehearsal/runner.service.override.conf +++ b/ops/dress-rehearsal/runner.service.override.conf @@ -1,5 +1,5 @@ [Service] -Environment=ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/lib/1helm-candidate/runner-job-started +Environment=ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/lib/1helm-candidate/runner-job-started.sh NoNewPrivileges=false ProtectSystem=strict ProtectHome=read-only diff --git a/test/phase2-candidate.mjs b/test/phase2-candidate.mjs index 6672d13..47f8581 100644 --- a/test/phase2-candidate.mjs +++ b/test/phase2-candidate.mjs @@ -93,7 +93,7 @@ test("rollback fixtures remain local-only and cannot satisfy normal candidate va test("candidate workflow and guest boundary exclude PR code and broad root access", () => { const workflow = read(".github/workflows/candidate.yml"); const helper = read("ops/dress-rehearsal/1helm-candidate-install"); - const hook = read("ops/dress-rehearsal/runner-job-started"); + const hook = read("ops/dress-rehearsal/runner-job-started.sh"); const sudoersExample = "%actions ALL=(root) NOPASSWD: /usr/local/sbin/1helm-candidate-install \"\"\n"; assert.match(workflow, /workflow_run:[\s\S]*workflows: \[CI\][\s\S]*branches: \[main\]/); assert.match(workflow, /workflow_run\.event == 'push'/); @@ -111,8 +111,11 @@ test("candidate workflow and guest boundary exclude PR code and broad root acces assert.doesNotMatch(helper, /--local-proof/); assert.match(helper, /awk -F\/.*!found.*found=1/, "large archive inspection consumes tar output instead of causing SIGPIPE under pipefail"); assert.match(helper, /actions\\\.runner[\s\S]*systemd-run[\s\S]*\/usr\/local\/sbin\/1helm-candidate-install/); - assert.match(helper, /unlink "\$INBOX\/candidate\.json" "\$INBOX\/candidate\.tgz"/); + assert.match(helper, /^unlink "\$INBOX\/candidate\.json"$/m); + assert.match(helper, /^unlink "\$INBOX\/candidate\.tgz"$/m); + assert.doesNotMatch(helper, /unlink "\$INBOX\/candidate\.json" "\$INBOX\/candidate\.tgz"/); assert.match(read("ops/dress-rehearsal/runner.service.override.conf"), /ProtectSystem=strict[\s\S]*ReadWritePaths=.*candidate\/inbox/); + assert.match(read("ops/dress-rehearsal/runner.service.override.conf"), /ACTIONS_RUNNER_HOOK_JOB_STARTED=\/usr\/local\/lib\/1helm-candidate\/runner-job-started\.sh/); assert.match(hook, /GITHUB_EVENT_NAME.*workflow_run/); assert.match(hook, /run\.get\("event"\) == "push"/); assert.doesNotMatch(sudoersExample, /NOPASSWD:\s*ALL/);