RPF-WP-0064-T07: in-memory /openbao/audit for the restored file audit device; native reseal regression
All checks were successful
CCR intake gate / gate (push) Successful in 2m10s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4384@bnt-lap001
Assistant-Session: 80d1f3da-7a91-4519-9737-9ba128b4da64
This commit is contained in:
codex 2026-10-24 11:22:00 +02:00
parent 624843e122
commit 7084b57d57
4 changed files with 39 additions and 1 deletions

View file

@ -72,8 +72,13 @@ def make_source_root(base: Path, mutate: dict[str, bytes] | None = None) -> tupl
# node id differing from this voter still restored and elected a leader, so the
# node id alone did not explain T07 attempt -01; it stays aligned with production.
PRODUCTION_NODE_ID = "openbao-0"
# It also declares production's file audit device (rapp-openbao values), so the
# snapshot's audit table re-enables it on the target after unseal.
PRODUCTION_AUDIT_PATH = "/openbao/audit/openbao-audit.log"
SOURCE_CONFIG = json.dumps({**json.loads(adapter.SERVER_CONFIG),
"storage": {"raft": {"path": "/openbao/data", "node_id": PRODUCTION_NODE_ID}}},
"storage": {"raft": {"path": "/openbao/data", "node_id": PRODUCTION_NODE_ID}},
"audit": {"file": {"file": {"description": "rehearsal mirror of production",
"options": {"file_path": PRODUCTION_AUDIT_PATH}}}}},
separators=(",", ":"))
@ -115,6 +120,9 @@ class SyntheticBao:
if leader.returncode == 0 and json.loads(leader.stdout)["data"].get("is_self"):
break
time.sleep(0.5)
audits = json.loads(self.bao(["audit", "list", "-format=json"], auth=True, check=True).stdout)
assert any(a.get("type") == "file" and a.get("options", {}).get("file_path") == PRODUCTION_AUDIT_PATH
for a in audits.values()), "source must carry production's file audit device"
return self
def __exit__(self, *exc):

View file

@ -85,6 +85,19 @@ class NativeRehearsal(unittest.TestCase):
self.assertEqual(out["reason"], "custody_identity_refused")
self.assertEqual(out["network"]["docker_networks"], [])
def test_unwritable_audit_path_reseals_and_aborts(self):
# Regression for T07 attempts -01/-02: without /openbao/audit the restored
# file audit device fails after unseal and OpenBao seals itself again.
removed = R.adapter.TMPFS.pop("/openbao/audit")
try:
session, receipt = self.run_session()
out = self.finish(session, receipt)
finally:
R.adapter.TMPFS["/openbao/audit"] = removed
self.assertEqual(out["reason"], "leader_not_elected")
self.assertTrue(out["restored"]["unsealed_with_original_shares"])
self.assertIs(out["restored"]["leader_wait"]["last_seal"]["sealed"], True)
def test_wrong_ciphertext_hash_aborts(self):
session, receipt = self.run_session(adm_override={"ciphertext_sha256": R.adapter.sha256_bytes(b"other")})
self.assertEqual(self.finish(session, receipt)["reason"], "ciphertext_mismatch")