generated from coulomb/repo-seed
Refine OpenBao taint resolution
This commit is contained in:
@@ -1029,10 +1029,33 @@ def state_value(ok: bool, set_value: bool = False, err: bool = False) -> str:
|
||||
return "nil"
|
||||
|
||||
|
||||
def openbao_trial_taint(data: dict[str, Any], relation: str = "downstream") -> dict[str, Any]:
|
||||
def openbao_trial_taint(
|
||||
data: dict[str, Any],
|
||||
relation: str = "downstream",
|
||||
material: str = "general",
|
||||
) -> dict[str, Any]:
|
||||
if not yes(data, "openbao_trial_material_exposed") or yes(data, "openbao_compromise_response_complete"):
|
||||
return {}
|
||||
unseal_resolved = yes(data, "openbao_unseal_keys_rotated")
|
||||
root_resolved = data.get("root_token_disposition") == "revoked"
|
||||
if material == "unseal" and unseal_resolved:
|
||||
return {}
|
||||
if material == "root-token" and root_resolved:
|
||||
return {}
|
||||
|
||||
relation_text = "Directly marked" if relation == "direct" else "Downstream"
|
||||
unresolved = []
|
||||
if not unseal_resolved:
|
||||
unresolved.append("exposed unseal shares need rotation")
|
||||
if not root_resolved:
|
||||
unresolved.append("exposed initial root token needs revocation")
|
||||
if unresolved:
|
||||
resolution = " Remaining: " + "; ".join(unresolved) + "."
|
||||
else:
|
||||
resolution = (
|
||||
" Exposed unseal shares are rotated and the exposed initial root token is revoked; "
|
||||
"only residual downstream review remains before marking the compromise response complete."
|
||||
)
|
||||
return {
|
||||
"tainted": True,
|
||||
"taint_source": "Trial key material exposed",
|
||||
@@ -1040,7 +1063,8 @@ def openbao_trial_taint(data: dict[str, Any], relation: str = "downstream") -> d
|
||||
"taint_reason": (
|
||||
f"{relation_text} from recorded OpenBao trial key-material exposure. "
|
||||
"Operator may proceed, but resulting evidence and work should be treated as tainted "
|
||||
"until rotation, reset, or another compromise response is recorded."
|
||||
"until rotation, revocation, reset, or another compromise response is recorded."
|
||||
+ resolution
|
||||
),
|
||||
}
|
||||
|
||||
@@ -1232,7 +1256,8 @@ def artifact_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
|
||||
state = bootstrap_secret_state()
|
||||
root_disposition = str(data.get("root_token_disposition") or "")
|
||||
init_output = yes(data, "openbao_init_output_produced")
|
||||
openbao_direct_taint = openbao_trial_taint(data, "direct")
|
||||
openbao_unseal_taint = openbao_trial_taint(data, "direct", "unseal")
|
||||
openbao_root_taint = openbao_trial_taint(data, "direct", "root-token")
|
||||
return [
|
||||
{
|
||||
"name": "platform-root",
|
||||
@@ -1334,7 +1359,7 @@ def artifact_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
|
||||
"location": "created during attended init; not stored here",
|
||||
"state": state_value(yes(data, "openbao_unseal_keys_rotated"), init_output or yes(data, "openbao_initialized")),
|
||||
},
|
||||
openbao_direct_taint,
|
||||
openbao_unseal_taint,
|
||||
),
|
||||
add_taint(
|
||||
{
|
||||
@@ -1346,7 +1371,7 @@ def artifact_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
|
||||
"location": "created during attended init; never pasted here",
|
||||
"state": state_value(root_disposition in {"revoked", "offline-sealed"}, init_output or yes(data, "openbao_initialized")),
|
||||
},
|
||||
openbao_direct_taint,
|
||||
openbao_root_taint,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1449,8 +1474,10 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
|
||||
initial_config_applied = yes(data, "openbao_initial_config_applied")
|
||||
trial_exposed = yes(data, "openbao_trial_material_exposed")
|
||||
response_complete = yes(data, "openbao_compromise_response_complete")
|
||||
keys_rotated = yes(data, "openbao_unseal_keys_rotated")
|
||||
openbao_direct_taint = openbao_trial_taint(data, "direct")
|
||||
openbao_downstream_taint = openbao_trial_taint(data, "downstream")
|
||||
openbao_unseal_taint = openbao_trial_taint(data, "downstream", "unseal")
|
||||
|
||||
key_compromise_location = "Template: record the exposure, choose reset versus rotation, inspect affected paths, and record only the non-secret outcome."
|
||||
if trial_exposed and not response_complete:
|
||||
@@ -1493,7 +1520,7 @@ def runbook_payloads(data: dict[str, Any]) -> list[dict[str, str]]:
|
||||
"location": rotate_location,
|
||||
"state": "template",
|
||||
},
|
||||
openbao_downstream_taint if trial_exposed and not response_complete else {},
|
||||
openbao_unseal_taint if trial_exposed and not response_complete and not keys_rotated else {},
|
||||
),
|
||||
add_taint(
|
||||
{
|
||||
@@ -2753,7 +2780,7 @@ def ui_html() -> str:
|
||||
<div id="runbooks-records" class="record-list"></div>
|
||||
<div class="choice-list">
|
||||
<label class="choice"><input id="openbao_trial_material_exposed" type="checkbox"><span><strong>Trial key material exposed</strong><span>Init output, unseal shares, or root-token material escaped the custody boundary during a trial.</span></span></label>
|
||||
<label class="choice"><input id="openbao_compromise_response_complete" type="checkbox"><span><strong>Compromise response complete</strong><span>Exposed material was rotated or the trial environment was reset. No secret values are recorded here.</span></span></label>
|
||||
<label class="choice"><input id="openbao_compromise_response_complete" type="checkbox"><span><strong>Compromise response complete</strong><span>Unseal shares, root token, and derived access paths were rotated, revoked, reset, or accepted as residual risk. No secret values are recorded here.</span></span></label>
|
||||
<label class="choice"><input id="openbao_unseal_keys_rotated" type="checkbox"><span><strong>New unseal keys generated</strong><span>OpenBao generated replacement unseal shares under the current runbook.</span></span></label>
|
||||
<label class="choice"><input id="openbao_emergency_lockdown_drilled" type="checkbox"><span><strong>Emergency lock-down drill recorded</strong><span>Railiance OpenBao was sealed and status-confirmed during a drill or real lock-down. No token or share is recorded here.</span></span></label>
|
||||
</div>
|
||||
|
||||
@@ -309,6 +309,12 @@ Integration with gates for the dedicated KeyCape OpenBao client, OpenBao
|
||||
OIDC/JWT auth configuration, and MFA-backed OpenBao admin login verification;
|
||||
cleanup and reopening move to S5/S6.
|
||||
|
||||
**2026-05-26:** Refined the OpenBao trial-exposure taint model so direct
|
||||
unseal-share taint clears after confirmed unseal-key rotation, and direct
|
||||
initial-root-token taint clears after the exposed OpenBao root token is
|
||||
revoked. Downstream work remains visibly tainted until derived access paths
|
||||
are reviewed and the compromise response is explicitly recorded complete.
|
||||
|
||||
**2026-05-24:** Stepped back from ad hoc secret rollout and added the
|
||||
custodian age-key bootstrap model to the control surface. The UI now records
|
||||
the custodian public age recipient, a derived fingerprint, and a non-secret
|
||||
|
||||
Reference in New Issue
Block a user