Allow bounded ESO token self-validation and cleanup
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
codex 2026-09-05 16:47:26 +02:00
parent 238d261536
commit 9c86983f1a
5 changed files with 29 additions and 2 deletions

View file

@ -238,6 +238,8 @@ def validate_workload_kv_read(ccr: dict[str, Any], errors: list[str], warnings:
)
if "metadata_read" in openbao and not isinstance(openbao["metadata_read"], bool):
errors.append("openbao.metadata_read must be boolean")
if "token_self_lifecycle" in openbao and not isinstance(openbao["token_self_lifecycle"], bool):
errors.append("openbao.token_self_lifecycle must be boolean")
fields = [str(field) for field in require_list(openbao.get("fields"), "openbao.fields", errors)]
if not fields:
errors.append("openbao.fields must contain at least one field")
@ -544,6 +546,9 @@ def generated_policy_hcl(ccr: dict[str, Any]) -> str:
if openbao.get("metadata_read", True):
body += (f'\npath "{mount}/metadata/{suffix}" {{\n'
' capabilities = ["read"]\n' "}\n")
if openbao.get("token_self_lifecycle", False):
body += ('\npath "auth/token/lookup-self" {\n capabilities = ["read"]\n}\n'
'\npath "auth/token/revoke-self" {\n capabilities = ["update"]\n}\n')
return body

View file

@ -61,7 +61,8 @@ def approved_contract():
if (not auth['bound_claims_confirmed'] or ccr['openbao']['kv_path'] != KV
or ccr['openbao']['fields'] != [FIELD] or auth['role'] != ROLE
or auth['mount'] != 'kubernetes' or module.auth_payload(ccr) != expected
or ccr['openbao'].get('metadata_read') is not False):
or ccr['openbao'].get('metadata_read') is not False
or ccr['openbao'].get('token_self_lifecycle') is not True):
raise LaneError('exact_contract_required')
policy = module.generated_policy_hcl(ccr)
if (ROOT / ccr['openbao']['policy_file']).read_text() != policy:
@ -159,6 +160,15 @@ def run(args, receipt):
identity = data(bao(['token', 'lookup', '-format=json']))['data']
if 'platform-admin' not in identity['policies'] or 'root' in identity['policies']:
raise LaneError('attended_platform_admin_required')
if args.action == 'repair-policy':
current = data(bao(['read', '-format=json', 'sys/policies/acl/' + POLICY]))['data']['policy']
original = policy.split('\npath "auth/token/lookup-self"')[0]
if current not in {original, policy}:
raise LaneError('read_policy_drift')
bao(['write', 'sys/policies/acl/' + POLICY, '-'], payload={'policy': policy})
verify_access(kube, receipt)
receipt['status'] = 'custody_verified_pending_eso_and_api_acceptance'
return
if args.action == 'verify':
verify_access(kube, receipt)
receipt['status'] = 'custody_verified_pending_eso_and_api_acceptance'
@ -198,7 +208,7 @@ def run(args, receipt):
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('action', choices=['provision', 'rotate', 'verify'])
parser.add_argument('action', choices=['provision', 'rotate', 'verify', 'repair-policy'])
parser.add_argument('--expected-version', required=True, type=int)
parser.add_argument('--kubeconfig', required=True)
parser.add_argument('--receipt', required=True)