From 153223983feeb09e6050f4d4db436f49f0d5816a Mon Sep 17 00:00:00 2001 From: codex Date: Sat, 5 Sep 2026 18:40:46 +0200 Subject: [PATCH] Verify fresh ESO reconciliation and safely retire unused delivery tokens Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883 --- scripts/repair_eso_kubernetes_auth.py | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/scripts/repair_eso_kubernetes_auth.py b/scripts/repair_eso_kubernetes_auth.py index 1d30888..98fae5c 100644 --- a/scripts/repair_eso_kubernetes_auth.py +++ b/scripts/repair_eso_kubernetes_auth.py @@ -121,18 +121,51 @@ def run(args, receipt): # JSON Patch tests the observed spec atomically; replace only authentication. patch = [{'op':'test','path':'/spec','value':live['spec']}, {'op':'replace','path':'/spec/provider/vault/auth','value':b['auth']}] command(kube + ['patch', 'clustersecretstore', lane['store'], '--type=json', '-p', json.dumps(patch)]) + before = data(command(kube + ['-n', lane['namespace'], 'get', 'externalsecret', lane['name'], '-o', 'json'])).get('status', {}).get('refreshTime') command(kube + ['annotate', 'clustersecretstore', lane['store'], 'force-sync=' + str(time.time_ns()), '--overwrite']) command(kube + ['-n', lane['namespace'], 'annotate', 'externalsecret', lane['name'], 'force-sync=' + str(time.time_ns()), '--overwrite']) command(kube + ['wait', '--for=condition=Ready', 'clustersecretstore/' + lane['store'], '--timeout=45s']) command(kube + ['-n', lane['namespace'], 'wait', '--for=condition=Ready', 'externalsecret/' + lane['name'], '--timeout=45s']) + for attempt in range(20): + status = data(command(kube + ['-n', lane['namespace'], 'get', 'externalsecret', lane['name'], '-o', 'json'])).get('status', {}) + if status.get('refreshTime') != before and any(c['type'] == 'Ready' and c['status'] == 'True' for c in status.get('conditions', [])): + row['refresh_time'] = status['refreshTime'] + break + time.sleep(2) + else: + raise LaneError('fresh_reconcile_not_observed') compare_custody(kube, lane) row.update(status='passed', unchanged_workload_values=True, store_ready=True, external_secret_ready=True) + if args.action == 'cleanup': + # Retired delivery tokens only; never delete workload Secrets or KV data. + stores = data(command(kube + ['get', 'clustersecretstores,secretstores', '-A', '-o', 'json']))['items'] + for lane, row in zip(lanes, receipt['lanes']): + name = lane['store'] + '-eso-token' + for store in stores: + ref = store['spec'].get('provider', {}).get('vault', {}).get('auth', {}).get('tokenSecretRef', {}) + require(ref.get('name') != name, 'retired_token_still_referenced') + secret = data(command(kube + ['-n', 'external-secrets', 'get', 'secret', name, '-o', 'json'])) + old_token = base64.b64decode(secret['data']['token']).decode() + lookup = bao(['write', '-format=json', 'auth/token/lookup', '-'], payload={'token': old_token}, allow_failure=True) + if lookup.returncode == 0: + policies = set(data(lookup)['data']['policies']) + require(policies <= {lane['policy'].removesuffix('-eso'), 'default'}, 'retired_token_scope_requires_review') + revoke(old_token) + row['retired_token_revoked'] = True + else: + require(b'400' in lookup.stderr and (b'bad token' in lookup.stderr.lower() or b'invalid token' in lookup.stderr.lower()), 'retired_token_invalidity_unproven') + row['retired_token_already_invalid'] = True + check = bao(['token', 'lookup', '-format=json'], token=old_token, allow_failure=True) + require(check.returncode != 0 and b'403' in check.stderr, 'retired_token_denial_unproven') + options = {'apiVersion':'v1','kind':'DeleteOptions','preconditions':{'uid':secret['metadata']['uid'],'resourceVersion':secret['metadata']['resourceVersion']}} + command(kube + ['delete', '--raw', '/api/v1/namespaces/external-secrets/secrets/' + name, '-f', '-'], payload=options) + row['retired_token_secret_removed'] = True receipt['status'] = 'passed' def main(): p = argparse.ArgumentParser(description=__doc__) - p.add_argument('action', choices=['apply', 'verify']) + p.add_argument('action', choices=['apply', 'verify', 'cleanup']) p.add_argument('--kubeconfig', required=True) p.add_argument('--receipt', required=True) p.add_argument('--confirm', required=True)