Add registrar redteam scenario (#782) - #969
Merged
Merged
Conversation
Gate the registrar credential boundary on every pull request and keep its policy attack input synchronized with the installer constants.\n\nCloses #782
Exercise the credential boundary against a real isolated deployment so per-PR gating catches authorization, audit, socket, and endpoint-trust regressions that unit tests cannot observe. Part of #782
An exhausted audit reserve must stop before any registrar verb can alter OpenBao. The live red-team scenario now proves that fail-closed boundary against a scenario-local tmpfs. Part of #782
Keep the external red-team client within the repository Python quality gate so the Docker scenario can start after the common checks pass. Part of #782
The deploy compose install intentionally uses pull-never, so the scenario must acquire its third-party images through the scoped compose deployment before it starts the isolated stack. Part of #782
Run every bootroot lifecycle command from the copied deployment tree so state and generated files stay inside the scenario boundary. Part of #782
Fresh infra installs have no state inventory, so the scenario supplies the minimal valid registrar predicate that init preserves into its final state. Part of #782
The surface issuance input distinguishes a clear EAB record from an absent one. Record the clear value after init, and bound failed-run teardown so CI can report the real scenario outcome. Part of #782
Persist the non-secret OpenBao response when the scenario cannot create its explicit clear EAB record, so CI exposes the exact setup failure. Part of #782
OpenBao presents a leaf under the deployment intermediary, so the scenario must use the same root-plus-intermediate trust bundle for its post-init authenticated setup request. Part of #782
The red-team fixture sent an escaped field name, so its root token was\nnever presented to OpenBao. Write a literal header file and retain the\nHTTP status, headers, and body when the setup write fails.\n\nPart of #782
Init already starts the rendered responder and its agents. Replaying infra\nup changes that fixture state before the registrar uses its pre-issued\ncredential, so leave the initialized deployment in place. Bound supervisor\nshutdown so failure cleanup cannot overrun the scenario timeout.\n\nPart of #782
The registrar endpoint's leaf has its own ACME hostname. Attach that\nname to the rendered responder before starting the daemon so step-ca can\nvalidate the live HTTP-01 challenge without replacing responder state.\n\nPart of #782
The registrar mTLS client leaf is issued through the same HTTP-01\nresponder as the endpoint leaf. Expose both run-scoped ACME names to\nstep-ca before the daemon starts.\n\nPart of #782
A certificate entry trusted the deployment root but did not require the\nbootroot-internal leaf's common name. Require that exact name alongside\nits DNS SAN so a registrar client certificate cannot obtain authority.\n\nPart of #782
The live OpenBao test fixture must carry the same common-name and DNS identity that production issuance creates, so it can verify the cert-auth containment constraint.\n\nPart of #782
The red-team login must exercise the daemon's named privileged role, which is the role the registrar material is attempting to access.\n\nPart of #782
Record the deployed cert-auth role before exercising the registrar leaf so a containment failure identifies whether the role or login behavior drifted.\n\nPart of #782
The red-team root helper writes a literal HTTP header, so curl must consume it with --header rather than as a configuration file.\n\nPart of #782
The harness must hand the activation descriptor directly to the daemon so LISTEN_PID identifies the process that consumes it. Use the deployment CA bundle for later OpenBao requests, which the intermediate-signed listener requires. Part of #782
The unauthenticated escalation requests must verify the scenario TLS listener so their status reflects authorization rather than a failed certificate chain. Part of #782
The external scenario must send the owner-defined rendered strings, not obsolete typed JSON values. Retain failed mint diagnostics in the uploaded artifact. Part of #782
Registrar endpoint pins are SHA-256 digests of certificate DER, not PEM text. Generate the staged client pin in the production format. Part of #782
The registrar socket is intentionally root-only, so the red-team\ncredential model must invoke its allowed caller with the same privilege.\n\nPart of #782
The external registrar probe must reject legacy TLS versions while\nvalidating the endpoint certificate.\n\nPart of #782
Registrar minting derives OpenBao role names from the registration ID,\nso containment checks must inspect the derived resource.\n\nPart of #782
The endpoint correctly refuses its reserved client name; wire error\nclassification is not part of the credential-boundary contract.\n\nPart of #782
The scenario owns a root-only audit store, so its audit evidence\nchecks must run with the required inspection privilege.\n\nPart of #782
The scenario asserts the endpoint protocol values rather than internal\naudit outcome names.\n\nPart of #782
The exhausted-state filler only needs six MiB after the low-water filler. Keeping the total below the fixture tmpfs capacity lets the scenario reach the registrar capacity assertion. Part of #782
The capacity test deliberately exhausts its audit store. Running it last prevents that controlled refusal from masking independent socket and peer-credential assertions. Part of #782
The scenario process cannot write directly to the root-owned supervisor FIFO. Sending control messages through sudo preserves that boundary while allowing the restart assertions to run. Part of #782
The post-bind peer fixture must start as its configured unprivileged identity. Numeric sudo identity arguments avoid account-name resolution at that process boundary. Part of #782
The peer assertion needs only the listener process's numeric UID. Avoiding an explicit numeric group keeps the fixture portable across sudo configurations while preserving the SO_PEERCRED check.\n\nPart of #782
The CI sudo policy did not reliably start the unprivileged listener with a RunAs identity. Setpriv applies the intended numeric UID and GID before the socket bind.\n\nPart of #782
The root-only CI arm must leave evidence for an unprivileged listener that fails before it binds, so its next failure can identify the launch boundary.\n\nPart of #782
The intentionally owner-only fixture directory hides its bound socket from the runner user. Check the post-bind metadata and socket presence through root instead.\n\nPart of #782
Tighten the live scenario assertions so its evidence follows the production contracts and records the scenario wall clock for review. Part of #782
The socket boundary test must corrupt material loaded by the endpoint, not a registrar client key already issued before restart. Part of #782
The prior negative fixture was repaired by registrar surface certificate issuance before TLS activation. Use a missing trust bundle instead, which TLS must load but issuance cannot restore. Part of #782
The fixture must alter the generated trust setting so the daemon reaches the intended failed-start path. Its escaped raw regex matched neither tabs nor newlines correctly, preventing that alteration. Part of #782
The fixture changed the first ca_bundle_path, which was unrelated to endpoint startup and could keep the daemon alive for certificate repair. Remove a required endpoint material setting instead so validation fails before repair or activation. Closes #782
Contributor
Author
|
[Reviewer Round 1] Approved — no blocking findings. The scenario, launcher contract, CI split for docs-only PRs, bounded leak bundle, OpenBao checks, socket/peer fixtures, and policy-fixture guard align with issue #782. PR linkage and test-plan hygiene are also correct. |
Contributor
Author
|
[Review Verdict Round 1: APPROVED] |
Contributor
Author
Suggested squash commitTitle Body |
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #782
Test plan
cargo test --bin bootroot, including the non-Docker privileged-policy fixture guard.cargo fmt -- --check --config group_imports=StdExternalCrate, andcargo clippy --all-targets -- -D warnings.[registrar_endpoint] server_key_pathsetting.setpriv, required by the peer-credential fixture, so Linux CI is the final gate.registrar-redteamentry, invokes the explicit launcher contract, uploadsci-registrar-redteam, and prints its wall-clock data.