railiance-platform/scripts/email_connect_smtp_foundation.py
codex e77f3989b4
Some checks failed
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
CCR intake gate / gate (push) Has been cancelled
Prepare supervised email-connect SMTP foundation and projection
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
2026-10-31 12:00:57 +01:00

314 lines
15 KiB
Python

#!/usr/bin/env python3
"""Silent attended CCR-2026-0056 foundation, admission and projection.
Default output is an offline proposal. Never changes the application sender.
An incomplete attempt requires inspection, not an automatic retry or rollback.
"""
from __future__ import annotations
import argparse
import hashlib
import http.client
import json
import os
from pathlib import Path
import socket
import subprocess
import sys
import tempfile
import time
from datetime import datetime, timezone
import email_connect_smtp_delivery as admission
from state_hub_preflight_lane import command, data, bao, capabilities
ROOT = admission.ROOT
BUNDLE = ROOT / 'openbao/email-connect-platform-smtp/foundation.json'
NS = 'email-connect'
SA = admission.LANE['service_account']
KUBECONFIG = '/home/worsch/.kube/config-railiance01'
require = admission.require
def digest(value):
return 'sha256:' + hashlib.sha256(json.dumps(value, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
def prepare():
ccr, owner, owner_digest = admission.prepare()
bundle = json.loads(BUNDLE.read_text())
proposal = dict(schema='platform.email-connect-smtp-foundation-proposal.v1',
foundation=bundle, admission_digest=owner_digest,
kubeconfig=KUBECONFIG,
controller_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest())
return ccr, proposal, digest(proposal)
class Native:
def __init__(self):
self.kube = ['kubectl', '--kubeconfig', KUBECONFIG]
def get(self, kind, name, namespace=NS):
args = self.kube + ['get', kind, name, '--ignore-not-found', '-o', 'json']
if namespace:
args += ['-n', namespace]
result = command(args)
return data(result) if result.stdout.strip() else None
def target_metadata(self, name):
# Kubernetes content negotiation excludes Secret values at the server.
# Use a private local socket, one fixed GET path and no full-object fallback.
require(name == 'email-connect-platform-smtp', 'target_secret_scope_drift')
path = '/api/v1/namespaces/email-connect/secrets/'+name
with tempfile.TemporaryDirectory(prefix='email-connect-metadata-') as directory:
sockpath = str(Path(directory)/'api.sock')
proc = subprocess.Popen(self.kube + ['proxy', '--unix-socket='+sockpath,
'--accept-paths=^'+path+'$', '--reject-methods=^(POST|PUT|PATCH|DELETE)$'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
conn = None
try:
for _ in range(50):
if Path(sockpath).exists():
break
require(proc.poll() is None, 'metadata_proxy_failed')
time.sleep(0.1)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(10)
try:
sock.connect(sockpath)
except BaseException:
sock.close()
raise
conn = http.client.HTTPConnection('localhost', timeout=10)
conn.sock = sock
conn.request('GET', path, headers={'Accept': 'application/json;as=PartialObjectMetadata;g=meta.k8s.io;v=v1'})
response = conn.getresponse()
if response.status == 404:
return None
require(response.status == 200, 'metadata_query_failed')
value = json.load(response)
require(value.get('kind') == 'PartialObjectMetadata' and
value.get('apiVersion') == 'meta.k8s.io/v1' and 'data' not in value,
'metadata_only_response_required')
return value['metadata']
finally:
if conn:
conn.close()
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill(); proc.wait(timeout=5)
def create(self, resource):
return data(command(self.kube + ['create', '-f', '-', '-o', 'json'], payload=resource))
def jwt(self, service_account=SA, audience='openbao'):
return command(self.kube + ['-n', NS, 'create', 'token', service_account,
'--audience='+audience, '--duration=10m']).stdout.decode().strip()
def token_review(self, jwt):
return data(command(self.kube + ['create', '-f', '-', '-o', 'json'], payload={
'apiVersion': 'authentication.k8s.io/v1', 'kind': 'TokenReview',
'spec': {'token': jwt, 'audiences': ['openbao']}}))['status']
def wait(self, resource):
command(self.kube + ['-n', NS, 'wait', '--for=condition=Ready',
resource['kind']+'/'+resource['metadata']['name'], '--timeout=45s'])
def exact_resource(actual, wanted):
require(actual is not None, 'resource_missing')
require(actual['apiVersion'] == wanted['apiVersion'] and actual['kind'] == wanted['kind'], 'resource_drift')
require(all(actual['metadata'].get(k) == v for k, v in wanted['metadata'].items()), 'resource_identity_drift')
if wanted['kind'] == 'ServiceAccount':
require(actual.get('automountServiceAccountToken') is False
and not actual.get('secrets') and not actual.get('imagePullSecrets'), 'service_account_drift')
else:
require(actual['spec'] == wanted['spec'], 'resource_spec_drift')
require(not actual['metadata'].get('deletionTimestamp'), 'resource_deleting')
def protected_application(native, bundle):
deployment = native.get('deployment', 'email-connect')
require(deployment['metadata']['uid'] == bundle['deployment_uid']
and deployment['metadata']['generation'] == bundle['deployment_generation'], 'application_changed')
values = []
for kind, name in [('deployment', 'email-connect'), ('configmap', 'email-connect-config'),
('externalsecret', 'email-connect-runtime')]:
obj = deployment if kind == 'deployment' else native.get(kind, name)
values.append({'uid': obj['metadata']['uid'], 'spec': obj.get('spec'), 'data': obj.get('data')})
return digest(values)
def verify_login(native):
def login(sa=SA, audience='openbao', negative=False):
result = bao(['write', '-format=json', 'auth/kubernetes/login', '-'],
payload={'role': SA, 'jwt': native.jwt(sa, audience)}, allow_failure=negative)
if negative:
if result.returncode == 0:
revoke(data(result)['auth']['client_token'])
raise admission.Refused('negative_login_succeeded')
require(b'400' in result.stderr or b'403' in result.stderr, 'negative_login_inconclusive')
return None
return data(result)['auth']
def revoke(token):
bao(['write', '-f', 'auth/token/revoke-self'], token=token)
check = bao(['token', 'lookup', '-format=json'], token=token, allow_failure=True)
require(check.returncode != 0 and b'403' in check.stderr, 'revocation_unconfirmed')
auth = login()
token = auth['client_token']
try:
require(auth['token_policies'] == [SA] and auth.get('policies', [SA]) == [SA]
and 0 < auth['lease_duration'] <= 900, 'effective_policy_or_ttl_drift')
path = admission.LANE['kv_path']
paths = [path, path.replace('/data/', '/metadata/', 1), path+'-unrelated',
'auth/token/create', 'auth/token/lookup-self', 'auth/token/revoke-self']
expected = [['read'], ['deny'], ['deny'], ['deny'], ['read'], ['update']]
require(capabilities(token, paths) == dict(zip(paths, expected)), 'capability_drift')
mailbox = data(bao(['read', '-format=json', path], token=token))['data']
admission.lane_module().validate_entry(mailbox)
finally:
revoke(token)
login('default', negative=True)
login(audience='wrong-audience', negative=True)
return dict(native_consumer_auth_verified=True, exact_read_verified=True,
mutation_sibling_metadata_denied=True, bounded_ttl=True,
other_service_account_denied=True, wrong_audience_denied=True,
workload_token_revocation_verified=True)
def run(proposal, receipt, checkpoint, native=None):
native = native or Native()
bundle = proposal['foundation']
status = data(bao(['status', '-format=json']))
require(status.get('cluster_id') == admission.OPENBAO_CLUSTER_ID and
status.get('initialized') is True and status.get('sealed') is False, 'openbao_instance_mismatch')
policies = data(bao(['token', 'lookup', '-format=json']))['data']['policies']
require('platform-admin' in policies and 'root' not in policies, 'attended_platform_admin_required')
for name, uid in [('kube-system', bundle['cluster_uid']), (NS, bundle['namespace_uid'])]:
obj = native.get('namespace', name, namespace=None)
require(obj and obj['metadata']['uid'] == uid and not obj['metadata'].get('deletionTimestamp'),
'cluster_or_namespace_identity_mismatch')
before = protected_application(native, bundle)
resources = bundle['resources']
require([x['kind'] for x in resources] == ['ServiceAccount', 'SecretStore', 'ExternalSecret'], 'foundation_contract_drift')
for x in resources:
require(native.get(x['kind'], x['metadata']['name']) is None, 'target_exists_requires_inspection')
target = resources[-1]['spec']['target']['name']
require(native.target_metadata(target) is None, 'target_secret_exists_requires_inspection')
receipt.update(phase='creating_service_account', mutation_outcome='unconfirmed')
checkpoint(receipt)
account = native.create(resources[0])
exact_resource(account, resources[0])
review = native.token_review(native.jwt())
require(review.get('authenticated') is True and review.get('audiences') == ['openbao']
and review['user']['username'] == 'system:serviceaccount:'+NS+':'+SA
and review['user']['uid'] == account['metadata']['uid'], 'native_binding_unproven')
receipt.update(phase='binding_verified', service_account_uid=account['metadata']['uid'],
native_binding_verified=True)
checkpoint(receipt)
validator = admission.module('email_connect_foundation_ccr', ROOT/'scripts/credential-change.py')
validator.confirm_binding(admission.CCR, reviewer='attended-platform-operator',
comment='CCR-2026-0056 foundation: pinned cluster and namespace, exact new service account and native audience TokenReview verified; receipt '+receipt['proposal_digest'])
ccr, _, owner_digest = admission.prepare()
admission.execution_guard(ccr, proposal['admission_digest'], owner_digest)
owner_receipt = {}
def owner_checkpoint(value):
receipt.update(phase='owner_admission', owner_admission=dict(value))
checkpoint(receipt)
admission.admit(owner_receipt, owner_checkpoint)
receipt.update(phase='native_login_verification')
checkpoint(receipt)
receipt.update(verify_login(native))
for wanted in resources[1:]:
receipt.update(phase='creating_'+wanted['kind'].lower())
checkpoint(receipt)
created = native.create(wanted)
exact_resource(created, wanted)
native.wait(wanted)
live = native.get(wanted['kind'], wanted['metadata']['name'])
exact_resource(live, wanted)
require(live['metadata']['uid'] == created['metadata']['uid'] and
any(c.get('type') == 'Ready' and c.get('status') == 'True'
for c in live.get('status', {}).get('conditions', [])), 'projection_not_ready')
if wanted['kind'] == 'ExternalSecret':
require(bool(live['status'].get('refreshTime')), 'projection_refresh_unproven')
metadata = native.target_metadata(target)
require(metadata and any(o.get('uid') == live['metadata']['uid']
and o.get('controller') is True for o in metadata.get('ownerReferences', [])),
'projection_ownership_unproven')
receipt['projection_refresh_time'] = live['status']['refreshTime']
require(protected_application(native, bundle) == before, 'application_changed')
receipt.update(status='foundation_admitted_projection_ready', phase='completed',
mutation_outcome='verified', projection_ready=True, application_sender_changed=False)
checkpoint(receipt)
def execute(ccr, proposal, actual, expected, path, runner=run):
require(actual == expected, 'reviewed_digest_required')
require(ccr['status'] == 'approved', 'approved_ccr_required')
info = path.parent.lstat()
require(path.is_absolute() and path.parent.is_dir() and not path.parent.is_symlink()
and info.st_uid == os.getuid() and not info.st_mode & 0o077, 'private_receipt_parent_required')
fd = os.open(path, os.O_WRONLY | os.O_APPEND | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
receipt = dict(schema='platform.email-connect-smtp-foundation.v1', status='started',
phase='preflight', proposal_digest=actual, started_at=datetime.now(timezone.utc).isoformat(),
mutation_outcome='not_started', email_sent=False, application_sender_changed=False)
def checkpoint(value):
raw = (json.dumps(value, sort_keys=True)+'\n').encode()
while raw:
n = os.write(fd, raw)
if n <= 0:
raise OSError('receipt_write_failed')
raw = raw[n:]
os.fsync(fd)
try:
checkpoint(receipt)
parent = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(parent)
finally:
os.close(parent)
runner(proposal, receipt, checkpoint)
require(receipt['status'] == 'foundation_admitted_projection_ready', 'success_receipt_required')
result = 0
except Exception:
receipt.update(status='failed_requires_inspection', reason='contained_foundation_failure')
result = 1
finally:
key = 'interrupted_at' if receipt['status'] == 'started' else 'completed_at'
receipt[key] = datetime.now(timezone.utc).isoformat()
try:
checkpoint(receipt)
finally:
os.close(fd)
return result
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--execute', action='store_true')
parser.add_argument('--expect-digest')
parser.add_argument('--receipt', type=Path)
args = parser.parse_args()
ccr, proposal, value = prepare()
if not args.execute:
print(json.dumps(dict(proposal_digest=value, proposal=proposal, ccr_status=ccr['status']), indent=2))
return 0
require(args.receipt is not None, 'private_receipt_required')
return execute(ccr, proposal, value, args.expect_digest, args.receipt)
if __name__ == '__main__':
if '--execute' in sys.argv:
with open(os.devnull, 'w') as sink:
os.dup2(sink.fileno(), 1); os.dup2(sink.fileno(), 2)
try:
code = main()
except Exception:
code = 1
sys.exit(code)
sys.exit(main())