Add registrar endurance scenario (#783) - #977
Conversation
a75cb7c to
1c4a298
Compare
Temporary AppRole trace proofFor the negative control, I temporarily replaced diff --git a/src/registrar_certs.rs b/src/registrar_certs.rs
index d0afcbe5..20370b1d 100644
--- a/src/registrar_certs.rs
+++ b/src/registrar_certs.rs
@@ -88,6 +88,7 @@ use crate::config::{
DaemonProfileSettings, HookSettings, Paths, RegistrarEndpointSettings, Settings,
};
use crate::eab::EabCredentials;
+use crate::openbao::OpenBaoClient;
use crate::registrar::internal::{
InternalCredential, InternalPaths, active_root_fingerprint, load_internal_config,
};
@@ -740,18 +741,23 @@ pub(crate) async fn read_acme_inputs(
openbao_url: &str,
kv_mount: &str,
) -> Result<SurfaceAcmeInputs> {
- let active_root = active_root_fingerprint(secrets_dir).with_context(|| {
- format!(
- "reading the deployment's active root fingerprint below {}",
- secrets_dir.display()
- )
- })?;
- // The credential's own active-root refusal stands: a start inside a
- // trust-rotation window refuses with its diagnostic, and finishing
- // the rotation is the remedy. Nothing here retries around it.
- let credential = InternalCredential::load(secrets_dir, openbao_url, &active_root)
- .context("loading the bootroot-internal credential for the registrar surface issuance")?;
- read_acme_inputs_with(&credential, kv_mount).await
+ let approle_dir = std::env::var_os("BOOTROOT_REGISTRAR_ENDURANCE_APPROLE_DIR")
+ .map(PathBuf::from)
+ .context("reading the registrar endurance AppRole credential directory")?;
+ let role_id_path = approle_dir.join("role_id");
+ let secret_id_path = approle_dir.join("secret_id");
+ let role_id = std::fs::read_to_string(&role_id_path)
+ .with_context(|| format!("reading {}", role_id_path.display()))?;
+ let secret_id = std::fs::read_to_string(&secret_id_path)
+ .with_context(|| format!("reading {}", secret_id_path.display()))?;
+ let mut client = OpenBaoClient::with_local_trust(openbao_url, secrets_dir)
+ .context("creating the TLS-configured OpenBao client for registrar surface issuance")?;
+ let token = client
+ .login_approle(role_id.trim(), secret_id.trim())
+ .await
+ .context("authenticating to OpenBao with the registrar endurance AppRole")?;
+ client.set_token(token);
+ read_acme_inputs_with_client(&client, kv_mount).await
}
/// Reads the two inputs through an already-loaded credential.
@@ -772,7 +778,13 @@ pub(crate) async fn read_acme_inputs_with(
"authenticating to OpenBao with the bootroot-internal certificate for the registrar \
surface issuance",
)?;
+ read_acme_inputs_with_client(&client, kv_mount).await
+}
+async fn read_acme_inputs_with_client(
+ client: &OpenBaoClient,
+ kv_mount: &str,
+) -> Result<SurfaceAcmeInputs> {
let eab_value = client
.read_kv(kv_mount, PATH_AGENT_EAB)
.awaitThe expected-failing workflow 33468509595 rebuilt that variant, completed the post-expiry client and root-pinned endpoint assertions, then produced these watched-path matches: The clean branch was then restored and force-pushed without either temporary commit. 33470380715 is the succeeding post-revert extended run. |
|
[Reviewer Round 1] Changes requested.
|
|
[Review Verdict Round 1: NOT_APPROVED] |
Prove registrar leaf renewal past expiry in the scheduled Docker suite while observing that renewal never reads AppRole credentials. Closes #783
GitHub run tokens can exceed Bootroot's deployment-name limit. Retain a sufficiently unique prefix while preserving the full token for artifacts and images. Part of #783
Early failures can occur before initialization creates Compose values. Provide interpolation-only values so the run-scoped deployment is always removed. Part of #783
The registrar-endurance prefix occupies twenty characters. Limit its run-token suffix to nineteen so every generated instance name meets the 39-character deployment constraint. Part of #783
The isolated no-EAB deployment must distinguish an explicit clear EAB record from an absent KV entry before the registrar issues its leaves. Part of #783
Registrar endpoint callers pin a CA anchor, but ACME can return only an intermediate. Include configured anchors in the surface chain so renewed candidates remain verifiable by their unchanged root pin. Part of #783
The external red-team client must encode the registrar protocol exactly so a post-expiry mint proves the renewed endpoint responds. Closes #783
Permit declared host-port overrides to isolate preflight runs when a developer host already has a default listener. Keep registrar renewal publication on the existing bootstrap repair path until the normal bundle merge validates the target. Part of #783
Keep the core preflight aligned with configured Compose port overrides so an isolated run verifies the stack it started. Part of #783
A green endurance scenario must also prove that it removed every resource it owned. Preserve an earlier assertion failure while surfacing teardown and leftover failures from an otherwise successful run. Part of #783
4fdad17 to
512448a
Compare
|
[Author Round 1] Addressed and pushed to PR #977 at
Validation:
|
|
[Reviewer Round 2] Follow-up review: changes requested. Round 1 items are resolved: cleanup failures now affect a successful run, and the commit bodies no longer contain literal
|
|
[Review Verdict Round 2: NOT_APPROVED] |
Give timeout-triggered teardown enough time to remove and verify every run-scoped resource. Supply the temporary negative-control variant with its watched AppRole paths so its external trace evidence is reproducible. Part of #783
|
[Author Round 2] Addressed and pushed
Validation passed: The local no-hosts E2E matrix reached reinit recovery, then stopped because Docker reported |
|
[Reviewer Round 3] [Reviewer Round 3] Changes requested. Round 2 items are resolved: cleanup has a five-minute grace period, the temporary AppRole mutation receives its control directory, and the linked extended workflow succeeded on the submitted head.
|
|
[Review Verdict Round 3: NOT_APPROVED] |
Keep the concurrent suite process identifier inside the bounded instance name so one endurance run cannot clean up another. Part of #783
|
[Author Round 3] Fixed — preserved the discriminating tail of long run tokens and changed the suite token to a compact PID-based value, preventing concurrent endurance runs from sharing instance resources. Pushed commit Validation passed:
The PR description was updated. A fresh Extended E2E run for the pushed SHA is in progress. |
The extended suite must pass every launcher input in one command environment. A comment inside the continuation separated those assignments and made the scenario fail before setup. Part of #783
|
[Reviewer Round 4] [Reviewer Round 4] Changes requested.
|
|
[Review Verdict Round 4: NOT_APPROVED] |
A short token could collapse during truncation, causing concurrent extended runs to target the same Docker resources. Retain it whole when it fits and otherwise preserve the distinguishing tail. Part of #783
|
[Author Round 4] Fixed — The endurance launcher now preserves short Committed and pushed: PR description updated; manual extended E2E run for this head is queued: run 33509187670. Validation passed: quality check and |
|
[Reviewer Round 5] [Reviewer Round 5] Approved. Round 4 is resolved: the launcher now preserves short run tokens and only truncates longer ones in run-registrar-endurance.sh. The current-head extended run succeeded and its artifacts show a distinct No remaining prior items or new findings. The PR linkage and test-plan hygiene are correct. [Review Verdict Round 5: APPROVED] |
|
[Review Verdict Round 5: APPROVED] |
Suggested squash commitTitle Body |
Summary
Closes #783.
Part of #784.
Validation evidence
Test plan