the-custodian/docs/changes/2026-11-01-scanner-json-digest/attempt-r4-access/diagnose_access.py

79 lines
4.6 KiB
Python

"""Founder-run selected access diagnostics, never config/credentials/raw errors.
No mutation or receipt reset. Reads selected node and self-identity metadata with
the catalogued attended context. The rollout packet and failed receipt stay intact.
"""
from pathlib import Path
import importlib.util
import json
import os
import subprocess
HERE=Path(__file__).resolve().parent
R4=HERE.parent/'native-r4'
EXPECTED='64c73100f2347470fda1b8b3194dbf6a8009291d9d80223af463b3c5ecab52fd'
ADMIN=Path('/home/worsch/.kube/config-railiance01')
OUTPUT=Path('/tmp/forge-scanner-access-diagnostic-receipt.json')
def category(returncode,stderr):
if returncode==0:return 'passed'
lower=stderr.lower()
groups=[('authentication-required',[b'the server has asked for the client to provide credentials',b'must be logged in to the server',b'unauthorized',b'invalid bearer token']),
('authorization-denied',[b'forbidden']),
('certificate-validity-failure',[b'certificate has expired',b'certificate is not yet valid',b'not yet valid']),
('certificate-verification-failure',[b'x509:',b'failed to verify certificate',b'certificate signed by unknown authority']),
('client-certificate-required',[b'tls: certificate required',b'client certificate required']),
('connection-refused',[b'connection refused']),
('transport-timeout',[b'i/o timeout',b'context deadline exceeded',b'tls handshake timeout',b'timed out']),
('name-resolution',[b'no such host']),
('context-selection-failure',[b'current-context is not set',b'context was not found',b'no context exists',b'does not exist']),
('configuration-unavailable',[b'error loading config file',b'no configuration has been provided',b'no such file or directory']),
('configuration-parse-failure',[b'yaml:',b'invalid configuration']),
('credential-plugin-failure',[b'getting credentials: exec',b'exec plugin',b'no auth provider found']),
('api-unavailable',[b'serviceunavailable',b'bad gateway',b'currently unable to handle the request',b'connection reset by peer',b'eof']),
('protocol-mismatch',[b'http response to https client'])]
for name,needles in groups:
if any(n in lower for n in needles):return name
return 'unclassified-command-failure'
def selected_nodes(raw,cfg):
data=json.loads(raw);items=data.get('items',[])
matched=len(items)==1 and items[0].get('metadata',{}).get('uid')==cfg['node_uid'] and any(x.get('address')=='92.205.62.239' for x in items[0].get('status',{}).get('addresses',[]))
return {'node_count':len(items),'target_node_matches':matched}
def selected_identity(raw):
data=json.loads(raw);user=data.get('status',{}).get('userInfo',{})
return {'self_identity_read':True,'identity_is_builder':user.get('username')=='ops-mason-builder',
'identity_in_system_masters': 'system:masters' in user.get('groups',[])}
def main():
s=importlib.util.spec_from_file_location('reviewed_r4',R4/'rollout.py');m=importlib.util.module_from_spec(s);s.loader.exec_module(m)
if m.verify_packet()!=EXPECTED:raise RuntimeError('reviewed-packet-drift')
cfg=json.loads((R4.parent/'native-r2/inputs.json').read_bytes())
env=dict(os.environ,KUBECONFIG=str(ADMIN))
receipt={'read_only':True,'native_mutation':False,'rollout_packet_sha256':EXPECTED,
'catalog_route':'kubernetes-railiance01-admin','admin_file_present':ADMIN.is_file(),
'admin_file_readable':os.access(ADMIN,os.R_OK),'checks':[]}
for label,args,select in [('get-nodes',['get','nodes','-o','json'],lambda raw:selected_nodes(raw,cfg)),
('self-identity',['auth','whoami','-o','json'],selected_identity)]:
try:
q=subprocess.run(['kubectl','--request-timeout=8s',*args],capture_output=True,timeout=15,env=env)
item={'operation':label,'returncode':q.returncode,'category':category(q.returncode,q.stderr)}
if q.returncode==0:
try:item.update(select(q.stdout))
except (ValueError,TypeError,KeyError,AttributeError):item['category']='unexpected-response; raw withheld'
except subprocess.TimeoutExpired:item={'operation':label,'category':'client-command-timeout'}
receipt['checks'].append(item)
OUTPUT.write_text(json.dumps(receipt,indent=2)+'\n')
print(json.dumps(receipt,indent=2));print('receipt:',str(OUTPUT))
if __name__=='__main__':
try:main()
except Exception as error:
print(json.dumps({'result':'read-only-diagnostic-refused','reason':str(error) if isinstance(error,RuntimeError) else type(error).__name__}))
raise SystemExit(1)