Share registrar scenario deployment setup (#981) - #982
Conversation
The red-team and endurance scenarios each carried their own copy of the same isolated deployment: the run root and its audit tmpfs, the four allocated host ports, the fingerprinted provisioning config, the responder image build, `infra install`, `init` under sudo, the DNS aliases step-ca resolves its challenge through, the daemon configuration, and the supervisor that owns the inherited listener. Two copies of one deployment drift, and the drift is invisible: each arm still passes against whatever deployment it happens to be standing up. They share one implementation now, and keep their own assertions. The instance name derives through that shared helper too, against the budget the binary validates rather than a per-scenario literal. Both arms keep the name they had for every token that fits, which is every token either launcher passes; an over-long one now truncates to its discriminating tail instead of failing `infra install` minutes into a run. `scripts/validate-e2e-run-scope.sh` exercises the helper for both prefixes rather than grepping one script for the lines it used to hold. `append_configured_anchors` is documented where a later reader will find it: what gates it, why an unchanged root-anchor pin needs it across a leaf renewal, and that the registrar surface is its only consumer today while the behaviour sits in the shared publication arm any future one would inherit. That disclosure was owed when the change merged, and a pull request description is not where it survives. Closes #981
|
[Reviewer Round 1] Request changes — PR hygiene findings:
The code refactor itself looks sound: shared setup remains limited to common deployment/supervisor behavior, scenario assertions stay local, instance-name derivation preserves the existing launcher tokens, and the in-tree documentation meets the issue’s technical requirements. |
|
[Review Verdict Round 1: NOT_APPROVED] |
|
[Author Round 1] Both review items were PR-description hygiene; no code change was warranted. Dispositions1. Remove the Removed the section. The issue's point is that the disclosure must not be restated in a later, unrelated PR description, and that body section did restate all three substantive points (the What I kept: test-plan item 4. The issue's own test plan requires confirming "that it names the 2. Rename Valid: Push / CINothing to commit — the working tree is clean and the head commit is still |
|
[Reviewer Round 2] Approved — no outstanding findings. Both Round 1 PR-body issues are resolved. The shared setup is appropriately limited to common deployment/supervisor behavior, scenario-specific assertions remain local, and the in-tree Rustdoc/reference documentation covers the required anchor-publication behavior. PR linkage and test-plan hygiene are also correct. |
|
[Review Verdict Round 2: APPROVED] |
What this changes
The registrar red-team and endurance scenarios stood the same isolated deployment up from two private copies of the same code. Both now use one implementation in
scripts/impl/lib/registrar-docker.sh, and keep their own assertions and workloads.Shared: the run root and its audit tmpfs, the four allocated host ports, the fingerprinted provisioning config and the operator agent config
initis given, the responder image build, the third-party image pre-pull,infra install, the state predicate seed,initundersudoand its redacted log, the root-token header file, the OpenBao CA bundle, the explicit empty agent EAB, the KV mount and the two shared path constants, the registrar DNS aliases, the daemon configuration, and the supervisor that owns the root-owned inherited listener along with starting, awaiting, controlling and stopping it.Local to each scenario: every assertion, the leak bundle and its manifest, the audit-capacity and peer-credential fixtures, the red-team socket refusals, the 6-minute Step CA lifetime and 5s/4m/0s renewal cadence, the anchor pin, the AppRole control credentials and both
stracepasses, the post-expiry checks, and each scenario's own cleanup. The three parameters the shared setup takes are each scenario's slug, the component body of its provisioning config, and the extra[registrar]keys only the red-team arm needs.Two consequences worth naming:
REGISTRAR_DOCKER_MAX_INSTANCE_NAME_LEN, the budgetinfra installvalidates--instance-nameagainst, rather than a literal each. Every token either launcher actually passes fits, so both keep the name they had; an over-long token now truncates to its discriminating tail rather than failing the install minutes into a run.scripts/validate-e2e-run-scope.shexercises the helper for both prefixes and holds the constant to the value the binary derives, replacing the three greps it used to run againstrun-registrar-endurance.shfor lines that no longer exist.empty-eab-status.txt,empty-eab-headers.txtandempty-eab-response.jsonfor both arms; the red-team arm already wrote those, and the endurance arm gains them. Nothing else about either artifact directory changes.The pull request also carries a documentation-only change:
append_configured_anchors(src/acme/flow.rs) and §4.4 ofdocs/reference/registrar-client-identity.mdnow record the already-mergedLeafWithChainanchor publication in the tree, which is where the issue requires it to live. The disclosure itself stays there rather than being restated here. No production behaviour changed.Closes #981
Part of #783
Part of #784
Test plan
scripts/validate-e2e-run-scope.sh,scripts/validate-e2e-leftover-check.sh,scripts/validate-e2e-openssl-compat.sh,scripts/validate-compose-instance-names.shandscripts/validate-deploy-compose.shall pass, andshellcheck -xraises no new category on the three changed shell files. It is not a CI gate here and is not silent on these scripts: it reports SC2015, SC2024, SC2119 and SC2126, all info- or warning-level and every one of them already raised by the pre-refactor scripts. Sharing the setup removes more of them than it adds — the single addition is an SC2119 suggestion on the new no-argumentregistrar_docker_start_supervisorcall site. The run-scope validator now covers both registrar scenarios' instance-name derivation and holdsREGISTRAR_DOCKER_MAX_INSTANCE_NAME_LENto the value the binary derives.registrar-redteamarm's artifact carries all fifteenPASSlines acrossvalidate,deployment,containment,functionality,socket,peer-credentials,capacityanddone, and a 348-secondwall-clock.json.E2E Extendedrun 33570504691, dispatched against this branch.extended-summary.jsonrecords"overall_status": "pass"with{"case":"registrar-endurance","status":"pass"}. The case artifact holds the renewal evidence:original-leaves.jsonfor both leaves, apost-expiry-endpoint.jsonwhose live endpoint digest differs from the original whilepin_content_sha256andpinned_anchor_sha256are unchanged, the 6-minuteca.json, bothstracepasses with an emptydaemon-trace-matches.log, and every phase fromvalidatethroughcleanupinside the 20-minute deadline.LeafWithChainanchor-publication documentation is in the tree, not only in this description — the rustdoc onappend_configured_anchorsinsrc/acme/flow.rsand §4.4 ofdocs/reference/registrar-client-identity.md, each naming the[trust].ca_bundle_pathgate, the unchanged-root-anchor renewal rationale, andSURFACE_LEAF_PUBLICATIONinsrc/registrar_certs.rsas the sole consumer today.provisioning.toml,operator-agent.toml, both scenarios'endpoint.tomlandsupervisor.pycome out byte-identical to the inline versions at the merge-base, and both scenarios were driven through the whole deployment phase against stubbeddocker/bootroot/curlunderset -euo pipefail: image tags, instance names,infra installports,initflags and per-scenario secrets, the alias override and both hostname probes all match the pre-refactor contract.LISTEN_FDS=1and its config path, the launch prefix's environment reaches it,restartrespawns on the same inode,stopterminates the daemon, andregistrar_docker_stop_supervisorreaps both and is a no-op the second time.scripts/preflight/ci/check.shgreen across all nine steps (cargo audit's two allowed warnings are the standing baseline), andcargo test --no-fail-fastpasses 2723 tests over 42 targets, includingregistrar_redteam_assertions, which sources the changed library.Both Docker scenarios need passwordless
sudofor the root-owned registrar socket, and the endurance arm also needsstraceandmountpoint, so neither runs on the macOS host this was written on; both were run on CI against this branch instead, as linked above.Carried across unchanged
Not a shortfall against the issue — it is unchanged from
main, and calling it out here rather than quietly carrying it across the refactor.init.login the uploaded scenario artifact carries live init secrets, and the redaction that was meant to prevent it has never matched. The scenarios redact withsed 's/^\(root token: \).*/\1<redacted>/', butinitprints the line as- root token: …, so the^anchor never fires. This run's own artifact (ci-registrar-redteam-33570500941) holds the root token in clear, along with three unseal keys, the step-ca password, the responder HMAC and six AppRolerole_id/secret_idpairs — none of which the anchoredsedever attempted. Everything in it belongs to the run-scoped OpenBao deployment the same job tears down, so none of it opens anything that still exists. The repository is public, though, and an artifact on a public repository is downloadable by any authenticated GitHub user for as long as it is retained.Both scenarios carried that
sedverbatim, and this PR moves it intoregistrar_docker_build_and_initializeas it was. It is left as it was on purpose. The fix is not the missing anchor — it is deciding what the artifact should hold, given that--enable show-secretsis what puts all of the above in the output: redact every secret the summary prints, stop asking forshow-secrets, or stop keepinginit.logat all. That is a decision worth making deliberately rather than inside a refactor, and one place carries it now instead of two.