NK-WP-0056-T10: attended OpenBao setup and proof for the executor lanes
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

tools/service-binds/executor-lanes.py (bind-lane.py shape): under both
sha256-bound, approved, binding-confirmed CCRs (0058 write, 0059 journey) and
an attended platform-admin, apply both policies, set cas_required on the
svc-authelia key, create the standing journey-read role, refuse if the
window-scoped write role exists, prove the agent fence; verify proves the
journey token reads only its path (<=15m) and denies wrong SA, wrong audience
and write-role login. Value-free receipts. 12 tests on a fake OpenBao (real
helpers), in make bounded-executor-test. Founder runbook:
sso-mfa/k8s/bounded-executor/T10-LANES-RUNBOOK.md.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
This commit is contained in:
tegwick 2026-10-31 19:15:46 +01:00
parent 37f4d4f306
commit 634b3a41df
4 changed files with 479 additions and 1 deletions

View file

@ -0,0 +1,163 @@
"""executor-lanes.py against an in-memory OpenBao and Kubernetes (NK-WP-0056-T10); no values leave the fakes."""
import hashlib
import importlib.util
import json
import shutil
import subprocess
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
ROOT = Path(__file__).resolve().parents[1]
PLATFORM = ROOT.parent / 'railiance-platform'
CCRS = {'write': 'CCR-2026-0058-net-kingdom-bind-executor-svc-authelia-write.yaml',
'journey': 'CCR-2026-0059-net-kingdom-bind-executor-syn-journey-read.yaml'}
JOURNEY = 'platform/data/workloads/net-kingdom/synthetic-journey/syn-journey'
BIND = 'platform/data/workloads/net-kingdom/directory-binds/svc-authelia'
FENCE = ''.join(f'path "{p}" {{ capabilities = ["deny"] }}\n' for p in (
JOURNEY, JOURNEY.replace('/data/', '/metadata/'), BIND, BIND.replace('/data/', '/metadata/')))
pytestmark = pytest.mark.skipif(not all((PLATFORM / 'credential-change-requests' / c).exists() for c in CCRS.values()),
reason='railiance-platform checkout with CCR-2026-0058/0059 required')
def platform_copy(tmp_path, confirm=True):
root = tmp_path / 'railiance-platform'
shutil.copytree(PLATFORM / 'scripts', root / 'scripts', ignore=shutil.ignore_patterns('__pycache__'))
(root / 'openbao').mkdir(); shutil.copytree(PLATFORM / 'openbao' / 'policies', root / 'openbao' / 'policies')
(root / 'credential-change-requests').mkdir()
for name in CCRS.values():
shutil.copy(PLATFORM / 'credential-change-requests' / name, root / 'credential-change-requests' / name)
if confirm:
subprocess.run([sys.executable, str(root / 'scripts/credential-change.py'), 'confirm-binding',
'--reviewer', 'platform-owner', '--comment', 'synthetic test',
str(root / 'credential-change-requests' / name)], check=True, capture_output=True)
return root
def digests(root):
return {k: hashlib.sha256((root / 'credential-change-requests' / v).read_bytes()).hexdigest() for k, v in CCRS.items()}
class FakeBao:
def __init__(self, m):
self.m = m
self.store = {m.BOUNDARY: {'policy': 'path "x" { capabilities = ["deny"] }\n' + FENCE}}
self.cas = False
self.identity = {'policies': ['platform-admin'], 'path': 'auth/netkingdom/oidc/callback'}
self.login_ok = {('nk-bind-executor', 'openbao', m.LANES['journey']['role'])}
self.wide = False
self.revoked = []
def __call__(self, args, *, payload=None, token=None, allow_failure=False):
out = lambda d, code=0, err=b'': SimpleNamespace(returncode=code, stdout=json.dumps(d).encode(), stderr=err)
missing = out({}, 2, b'No value found')
if args[:2] == ['token', 'lookup']: return out({'data': self.identity})
if args[:2] == ['token', 'create']: return out({'auth': {'client_token': 'child'}})
if args[:3] == ['kv', 'metadata', 'get']: return out({'data': {'cas_required': self.cas}})
if args[:3] == ['kv', 'metadata', 'patch']:
assert '-cas-required=true' in args; self.cas = True; return out({})
if args[0] == 'read':
path = args[-1]
if path == JOURNEY and token == 'reader':
return out({'data': {'data': {'PASSWORD': 'SYNTHETIC-PW-VALUE', 'TOTP_SEED': 'SYNTHETIC-SEED-VALUE'}, 'metadata': {'version': 1}}})
return out({'data': self.store[path]}) if path in self.store else missing
if args[0] == 'write':
path = args[-2] if args[-1] == '-' else args[-1]
if path == 'auth/token/revoke': self.revoked.append(payload['token']); return out({})
if path == 'sys/capabilities':
def cap(p):
if payload['token'] == 'reader' and (p == JOURNEY or self.wide): return ['read']
return ['deny']
return out({'data': {p: cap(p) for p in payload['paths']}})
if path == 'auth/kubernetes/login':
sa, aud = payload['jwt'].split('|')
if (sa, aud, payload['role']) in self.login_ok:
return out({'auth': {'client_token': 'reader', 'lease_duration': 900,
'token_policies': [self.m.LANES['journey']['policy']]}})
return out({}, 2, b'Code: 403')
if path.startswith('auth/kubernetes/role/'):
self.store[path] = {k: (900 if v == '15m' else v) for k, v in payload.items()}; return out({})
self.store[path] = payload; return out({})
raise AssertionError(args)
def build(tmp_path, monkeypatch, confirm=True):
root = platform_copy(tmp_path, confirm)
monkeypatch.setenv('NK_PLATFORM_ROOT', str(root))
spec = importlib.util.spec_from_file_location('executor_lanes', ROOT / 'tools/service-binds/executor-lanes.py')
m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m)
fake = FakeBao(m)
for module in (m, sys.modules['state_hub_preflight_lane'], sys.modules['telemetry_smtp_delivery']):
if hasattr(module, 'bao'):
monkeypatch.setattr(module, 'bao', fake) # the real helpers (ensure, capabilities, revoke) run on the fake
monkeypatch.setattr(m, 'kube', lambda kc, args: f'{args[4]}|{args[5].split("=", 1)[1]}'.encode())
return m, fake, digests(root)
def run(m, action, d):
r = {}
try:
(m.setup(d, r) if action == 'setup' else m.verify(d, 'kc', r))
except m.Refused as error:
r.update(status='failed', reason=str(error))
return r
def test_setup_applies_policies_cas_and_journey_role_but_never_the_write_role(tmp_path, monkeypatch):
m, fake, d = build(tmp_path, monkeypatch)
r = run(m, 'setup', d)
assert r['status'] == 'lanes_prepared', r
for lane in m.LANES.values():
assert fake.store['sys/policies/acl/' + lane['policy']]['policy'] == lane['policy_text']
assert fake.cas is True
assert 'auth/kubernetes/role/' + m.LANES['journey']['role'] in fake.store
assert 'auth/kubernetes/role/' + m.LANES['write']['role'] not in fake.store
assert run(m, 'setup', d)['status'] == 'lanes_prepared' # idempotent
def test_verify_after_setup(tmp_path, monkeypatch):
m, fake, d = build(tmp_path, monkeypatch)
run(m, 'setup', d)
r = run(m, 'verify', d)
assert r['status'] == 'lanes_verified', r
assert r['write_login_denied'] and r['wrong_audience_denied'] and 'reader' in fake.revoked
assert 'SYNTHETIC-' not in json.dumps(r) # no values in the receipt
@pytest.mark.parametrize('mutate, reason', [
(lambda m, f, d: d.update(write='0' * 64), 'write:ccr_digest'),
(lambda m, f, d: f.store.update({m.BOUNDARY: {'policy': 'path "x" { capabilities = ["deny"] }\n'}}),
'agent_fence_missing'),
(lambda m, f, d: f.identity.update(policies=['root']), 'attended_platform_admin_required'),
(lambda m, f, d: f.store.update({'auth/kubernetes/role/' + m.LANES['write']['role']: {}}),
'write_role_exists_outside_a_window'),
(lambda m, f, d: f.store.update({'sys/policies/acl/' + m.LANES['journey']['policy']: {'policy': 'path "*" {}'}}),
'net-kingdom-bind-executor-syn-journey-read:policy_drift'),
])
def test_setup_refusals(tmp_path, monkeypatch, mutate, reason):
m, fake, d = build(tmp_path, monkeypatch)
mutate(m, fake, d)
r = run(m, 'setup', d)
assert (r['status'], r['reason']) == ('failed', reason)
def test_unconfirmed_binding_is_refused(tmp_path, monkeypatch):
m, fake, d = build(tmp_path, monkeypatch, confirm=False)
assert run(m, 'setup', d)['reason'] == 'write:binding_unconfirmed'
@pytest.mark.parametrize('mutate, reason', [
(lambda m, f: f.login_ok.add(('nk-bind-executor', 'openbao', m.LANES['write']['role'])), 'negative_login_accepted'),
(lambda m, f: f.login_ok.add(('default', 'openbao', m.LANES['journey']['role'])), 'negative_login_accepted'),
(lambda m, f: setattr(f, 'wide', True), 'journey_capabilities'),
(lambda m, f: setattr(f, 'cas', False), 'cas_required_not_set'),
])
def test_verify_refusals(tmp_path, monkeypatch, mutate, reason):
m, fake, d = build(tmp_path, monkeypatch)
run(m, 'setup', d)
mutate(m, fake)
r = run(m, 'verify', d)
assert (r['status'], r['reason']) == ('failed', reason)