diff --git a/.github/workflows/qualify-secure-runtime-release.yml b/.github/workflows/qualify-secure-runtime-release.yml index 348c337f31..659aa475e1 100644 --- a/.github/workflows/qualify-secure-runtime-release.yml +++ b/.github/workflows/qualify-secure-runtime-release.yml @@ -331,6 +331,11 @@ jobs: --env "PULSE_SECURE_RUNTIME_TRANSCRIPT_RECORD_PATH=docs/release-control/v6/internal/records/secure-agent-runtime-systemd-transcript-v7-${TAG}.jsonl" \ "${container}" \ sh -c 'cd /workspace/scripts/installtests && exec /harness/installtests-linux-amd64.test -test.run "^TestSecureRuntimeSystemdDockerV7Lab$" -test.count=1 -test.v' + # The lab writes its receipt and transcript as root inside the + # privileged host. Hand the evidence directory to the runner user so + # the attester can read it and write the attestation beside it. The + # evidence content itself is not touched. + docker exec "${container}" chown -R "$(id -u):$(id -g)" /evidence elapsed="$(( $(date +%s) - start_epoch ))" if [[ "${elapsed}" -lt 1 ]]; then elapsed=1; fi echo "elapsed_seconds=${elapsed}" >> "$GITHUB_OUTPUT" diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 2520ea971c..96cff70a01 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -1304,6 +1304,10 @@ artifact-selection behaviour. helper's private network isolation. Canary setup and the no-default-route check must fail qualification before that lab if the boundary is absent; they must not skip the lab or change the release packet under test. + The lab writes its receipt and transcript as root inside that container. + After a passing lab and before attestation, the workflow must hand the + evidence directory to the runner user without altering its content, so the + attester can read the receipt and write the attestation beside it. PVE jobs must consume their runner users' persistent local Go and npm caches directly; disposable-runner Actions cache restore/save phases must remain disabled because archiving those same caches adds network work after the diff --git a/scripts/installtests/build_release_assets_test.go b/scripts/installtests/build_release_assets_test.go index 96e889fd40..1f8fca5a6a 100644 --- a/scripts/installtests/build_release_assets_test.go +++ b/scripts/installtests/build_release_assets_test.go @@ -2856,6 +2856,7 @@ func TestSecureRuntimeQualificationPacketIsHostedAndReleaseBound(t *testing.T) { `ip link add pulse-can0 type veth peer name pulse-can1`, `ip address add 192.0.2.1/32 dev pulse-can0`, `test -z "$(ip -4 route show default)"`, + `docker exec "${container}" chown -R "$(id -u):$(id -g)" /evidence`, `pulse-secure-runtime-fixture:v7`, "--verify-release-packet-only", "--verified-packet-dir", @@ -2878,6 +2879,11 @@ func TestSecureRuntimeQualificationPacketIsHostedAndReleaseBound(t *testing.T) { if canaryIndex <= privilegedExecutionIndex || labIndex <= canaryIndex { t.Fatal("the isolated host-interface canary must be configured before running the immutable RC lab") } + ownershipIndex := strings.Index(qualificationWorkflow, `docker exec "${container}" chown -R "$(id -u):$(id -g)" /evidence`) + attestIndex := strings.Index(qualificationWorkflow, `--output "${evidence_dir}/attestation.json"`) + if ownershipIndex <= labIndex || attestIndex <= ownershipIndex { + t.Fatal("the lab evidence must be handed to the runner user after the lab and before attestation") + } if !strings.Contains(qualificationWorkflow, `--privileged \ --network none \ --cgroupns=host`) {