Skip to content

Add registrar redteam scenario (#782) - #969

Merged
sehkone merged 42 commits into
mainfrom
sehkone/issue-782
Aug 31, 2026
Merged

Add registrar redteam scenario (#782)#969
sehkone merged 42 commits into
mainfrom
sehkone/issue-782

Conversation

@sehkone

@sehkone sehkone commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds the registrar-redteam Docker scenario with a bounded read-only registrar-leak bundle, live OpenBao escalation and access checks, audit-capacity coverage, and root-owned inherited-socket tests.
  • Gates every pull request exactly once: code-changing pull requests run the dedicated matrix entry, while documentation-only pull requests run the focused companion job. Both publish the registrar-redteam artifact.
  • Keeps the policy fixture synchronized with the production policy constants and records scenario wall-clock data in the artifacts and CI logs.

Closes #782

Test plan

  • Run cargo test --bin bootroot, including the non-Docker privileged-policy fixture guard.
  • Run shell syntax validation, cargo fmt -- --check --config group_imports=StdExternalCrate, and cargo clippy --all-targets -- -D warnings.
  • Validate the negative startup fixture matcher by removing the required [registrar_endpoint] server_key_path setting.
  • Run the full registrar-redteam scenario locally. The macOS host lacks Linux setpriv, required by the peer-credential fixture, so Linux CI is the final gate.
  • Verify the code-changing matrix dispatch runs only the dedicated registrar-redteam entry, invokes the explicit launcher contract, uploads ci-registrar-redteam, and prints its wall-clock data.
  • Verify the documentation-only companion runs the equivalent focused scenario and artifact path, leaving unrelated Docker scenarios skipped.

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
Comment thread tests/e2e/registrar/redteam_client.py Fixed
sehkone added 26 commits August 31, 2026 07:13
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
@sehkone sehkone changed the title Add registrar redteam scenario Add registrar redteam scenario (#782) Aug 31, 2026
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
@sehkone

sehkone commented Aug 31, 2026

Copy link
Copy Markdown
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.

@sehkone

sehkone commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

[Review Verdict Round 1: APPROVED]

@sehkone

sehkone commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Suggested squash commit

Title

Add registrar redteam scenario

Body

Establish a live OpenBao credential-boundary regression gate for the
registrar, including root-owned socket and audit-capacity cases that
cargo-only tests cannot cover.

Closes #782

@sehkone
sehkone merged commit cd2b64a into main Aug 31, 2026
23 checks passed
@sehkone
sehkone deleted the sehkone/issue-782 branch August 31, 2026 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the registrar-redteam Docker scenario and gate every pull request on it

2 participants