write-role-audit: report write-policy request times and orphan first_seen; check period is required
The first live read (NK-WP-0058-T11) counted the attended 2026-11-04 evening logins because the incident default started at the window start. Times make the judgment checkable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 943911@bnt-lap001 Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
This commit is contained in:
parent
c6199b8a6e
commit
7031c97442
2 changed files with 14 additions and 6 deletions
|
|
@ -55,6 +55,7 @@ def pilot_log(overnight_login=False):
|
|||
|
||||
def run(lines, tmp_path, *extra):
|
||||
receipt = tmp_path / 'r.json'
|
||||
extra = extra or ('--check-from', '2026-11-04T22:39:00Z', '--check-to', '2026-11-05T08:25:00Z')
|
||||
done = subprocess.run([sys.executable, str(TOOL), '--receipt', str(receipt), *extra],
|
||||
input='\n'.join(lines) + '\n', capture_output=True, text=True)
|
||||
return done, json.loads(receipt.read_text())
|
||||
|
|
@ -100,7 +101,8 @@ def test_receipt_is_value_free_and_refuses_overwrite(tmp_path):
|
|||
done, r = run(pilot_log(overnight_login=True), tmp_path)
|
||||
text = (tmp_path / 'r.json').read_text() + done.stdout
|
||||
assert 'hmac' not in text and 'client_token' not in text and 'accessor' not in text
|
||||
again = subprocess.run([sys.executable, str(TOOL), '--receipt', str(tmp_path / 'r.json')], input='',
|
||||
again = subprocess.run([sys.executable, str(TOOL), '--receipt', str(tmp_path / 'r.json'),
|
||||
'--check-from', '2026-11-05T00:00:00Z', '--check-to', '2026-11-05T01:00:00Z'], input='',
|
||||
capture_output=True, text=True)
|
||||
assert again.returncode != 0 and 'FileExistsError' in again.stderr
|
||||
|
||||
|
|
@ -118,3 +120,9 @@ def test_pilot_window_just_after_the_check_does_not_count(tmp_path):
|
|||
assert r['check']['intervals_overlapping'] == 2 # the pilot window opened before --check-to
|
||||
assert r['check']['logins_in_check'] == 0 and r['check']['policy_requests_in_check'] == 0
|
||||
assert r['check']['no_login_in_check'] is True
|
||||
|
||||
|
||||
def test_policy_request_times_are_reported(tmp_path):
|
||||
_, r = run(pilot_log(), tmp_path)
|
||||
assert r['intervals'][1]['policy_request_times'] == ['2026-11-05T08:30:20.000000Z']
|
||||
assert r['intervals'][0]['first_seen'] is None
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
Reads the audit log as JSON lines on stdin. The founder streams it from the pod; nothing is stored on the way:
|
||||
|
||||
kubectl -n openbao exec openbao-0 -- cat /openbao/audit/openbao-audit.log \
|
||||
| python3 tools/service-binds/write-role-audit.py --receipt EVIDENCE/NN-write-role-audit.json
|
||||
| python3 tools/service-binds/write-role-audit.py --check-from T1 --check-to T2 --receipt EVIDENCE/NN-....json
|
||||
|
||||
It reports every interval in which the write role existed: from a successful create/update of
|
||||
auth/kubernetes/role/<role> to its successful delete. For each interval it reports:
|
||||
|
|
@ -116,6 +116,8 @@ def report(stats, intervals, check_from=None, check_to=None):
|
|||
'opened': iso(item['opened']), 'closed': iso(item['closed']), 'orphan': item.get('orphan', False),
|
||||
'logins': len(item['logins']), 'login_times': [iso(t) for t in item['logins'][:20]],
|
||||
'login_service_accounts': sorted(item['service_accounts']), 'policy_requests': len(item['policy_requests']),
|
||||
'policy_request_times': [iso(t) for t in item['policy_requests'][:20]],
|
||||
'first_seen': iso(item.get('first_seen')),
|
||||
'failed_k8s_logins': item['failed_k8s_logins']})
|
||||
out['role_present_at_log_end'] = bool(intervals and intervals[-1]['closed'] is None
|
||||
and not intervals[-1].get('orphan'))
|
||||
|
|
@ -135,10 +137,8 @@ def report(stats, intervals, check_from=None, check_to=None):
|
|||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
p.add_argument('--role', default=ROLE)
|
||||
p.add_argument('--check-from', type=when, default=when('2026-11-04T22:39:00Z'),
|
||||
help='start of the period to judge (default: BEA-2026-0002 window start)')
|
||||
p.add_argument('--check-to', type=when, default=when('2026-11-05T08:25:00Z'),
|
||||
help='end of the period to judge (default: just after the late close)')
|
||||
p.add_argument('--check-from', type=when, required=True, help='start of the period to judge (UTC, ...Z)')
|
||||
p.add_argument('--check-to', type=when, required=True, help='end of the period to judge (UTC, ...Z)')
|
||||
p.add_argument('--receipt', required=True)
|
||||
a = p.parse_args()
|
||||
fd = os.open(a.receipt, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue