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"
|
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"):
|
if not yes(data, "openbao_trial_material_exposed") or yes(data, "openbao_compromise_response_complete"):
|
||||||
return {}
|
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"
|
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 {
|
return {
|
||||||
"tainted": True,
|
"tainted": True,
|
||||||
"taint_source": "Trial key material exposed",
|
"taint_source": "Trial key material exposed",
|
||||||
@@ -1040,7 +1063,8 @@ def openbao_trial_taint(data: dict[str, Any], relation: str = "downstream") -> d
|
|||||||
"taint_reason": (
|
"taint_reason": (
|
||||||
f"{relation_text} from recorded OpenBao trial key-material exposure. "
|
f"{relation_text} from recorded OpenBao trial key-material exposure. "
|
||||||
"Operator may proceed, but resulting evidence and work should be treated as tainted "
|
"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()
|
state = bootstrap_secret_state()
|
||||||
root_disposition = str(data.get("root_token_disposition") or "")
|
root_disposition = str(data.get("root_token_disposition") or "")
|
||||||
init_output = yes(data, "openbao_init_output_produced")
|
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 [
|
return [
|
||||||
{
|
{
|
||||||
"name": "platform-root",
|
"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",
|
"location": "created during attended init; not stored here",
|
||||||
"state": state_value(yes(data, "openbao_unseal_keys_rotated"), init_output or yes(data, "openbao_initialized")),
|
"state": state_value(yes(data, "openbao_unseal_keys_rotated"), init_output or yes(data, "openbao_initialized")),
|
||||||
},
|
},
|
||||||
openbao_direct_taint,
|
openbao_unseal_taint,
|
||||||
),
|
),
|
||||||
add_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",
|
"location": "created during attended init; never pasted here",
|
||||||
"state": state_value(root_disposition in {"revoked", "offline-sealed"}, init_output or yes(data, "openbao_initialized")),
|
"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")
|
initial_config_applied = yes(data, "openbao_initial_config_applied")
|
||||||
trial_exposed = yes(data, "openbao_trial_material_exposed")
|
trial_exposed = yes(data, "openbao_trial_material_exposed")
|
||||||
response_complete = yes(data, "openbao_compromise_response_complete")
|
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_direct_taint = openbao_trial_taint(data, "direct")
|
||||||
openbao_downstream_taint = openbao_trial_taint(data, "downstream")
|
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."
|
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:
|
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,
|
"location": rotate_location,
|
||||||
"state": "template",
|
"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(
|
add_taint(
|
||||||
{
|
{
|
||||||
@@ -2753,7 +2780,7 @@ def ui_html() -> str:
|
|||||||
<div id="runbooks-records" class="record-list"></div>
|
<div id="runbooks-records" class="record-list"></div>
|
||||||
<div class="choice-list">
|
<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_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_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>
|
<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>
|
</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;
|
OIDC/JWT auth configuration, and MFA-backed OpenBao admin login verification;
|
||||||
cleanup and reopening move to S5/S6.
|
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
|
**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
|
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
|
the custodian public age recipient, a derived fingerprint, and a non-secret
|
||||||
|
|||||||
Reference in New Issue
Block a user