"""Run explicitly on a Linux Docker host; creates two disposable containers. No published ports, no production endpoints, fresh throwaway keys kept in memory. """ import hashlib import json import os from pathlib import Path import secrets import subprocess import tempfile import time IMAGE='athena-deck-network-test:20260928' DECK='athena-deck-test-runtime' PEER='athena-deck-test-peer' def run(*args, data=None, check=True): result=subprocess.run(args,input=data,text=True,capture_output=True,timeout=45) if check and result.returncode: raise RuntimeError('Test command failed: '+args[0]+' (output redacted)') return result.stdout.strip() def execute(container, script, value=None): payload=json.dumps(value) if value is not None else None result=run('docker','exec','-i',container,'python3','-c',script,data=payload) return json.loads(result) if result else None def rpc(action, **values): result=execute(DECK,"import json,sys;from network.rpc import request;print(json.dumps(request(json.load(sys.stdin))))",dict(action=action,**values)) return result HTTP = '''import http.client,json,sys v=json.load(sys.stdin) c=http.client.HTTPConnection(v['host'],8110,timeout=5) h={'Content-Type':'application/json','X-Athena-Deck':'1'} if v.get('cookie'):h['Cookie']=v['cookie'] if v.get('token'):h['Authorization']='Bearer '+v['token'] if v.get('origin'):h['Origin']=v['origin'] c.request(v.get('method','GET'),v.get('path','/'),json.dumps(v['body']) if 'body' in v else None,h) r=c.getresponse();b=r.read().decode() print(json.dumps({'status':r.status,'body':b,'cookie':r.getheader('Set-Cookie')})) ''' def http(host,path='/',method='GET',body=None,cookie=None,token=None): value=dict(host=host,path=path,method=method,cookie=cookie,token=token) if body is not None:value['body']=body return execute(PEER,HTTP,value) def main(): for name in (DECK,PEER): if run('docker','ps','-aq','--filter','name=^/'+name+'$'): raise RuntimeError('Test container name already exists; refusing takeover') ids=run('docker','ps','-aq').splitlines() baseline=run('docker','inspect','--format','{{.Id}} {{.State.StartedAt}}',*ids) if ids else '' state=tempfile.TemporaryDirectory(prefix='athena-deck-test-') password=secrets.token_urlsafe(32) salt=secrets.token_bytes(16) api_token=secrets.token_urlsafe(32) auth=dict(version=1,password=dict(salt=salt.hex(),hash=hashlib.pbkdf2_hmac('sha256',password.encode(),salt,600000).hex()),revision=secrets.token_hex(16),api_token_hash=hashlib.sha256(api_token.encode()).hexdigest(),password_changed_at=time.time(),token_changed_at=time.time()) p=Path(state.name)/'auth.json';p.write_text(json.dumps(auth));p.chmod(0o600) try: run('docker','run','-d','--name',PEER,'--cap-drop','ALL','--cap-add','NET_ADMIN',IMAGE,'sleep','600') run('docker','run','-d','--name',DECK,'--read-only','--cap-drop','ALL','--cap-add','NET_ADMIN','--cap-add','NET_RAW','--cap-add','SETUID','--cap-add','SETGID','--cap-add','CHOWN','--security-opt','no-new-privileges:true','--tmpfs','/run:rw,nosuid,nodev,size=8m','--tmpfs','/tmp:rw,nosuid,nodev,size=8m','-v',state.name+':/data',IMAGE) for _ in range(20): try: assert rpc('status')['state']=='disabled' break except Exception:time.sleep(.5) else:raise RuntimeError('Helper did not become ready') print('PASS helper startup and unprivileged web child',flush=True) uid=execute(DECK,"import json;from pathlib import Path;print(json.dumps([p.read_text().split('Uid:')[1].splitlines()[0].split()[0] for p in Path('/proc').glob('[0-9]*/status') if 'Name:\\tpython' in p.read_text()]))") assert '65534' in uid deckip=run('docker','inspect','--format','{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}',DECK) peerip=run('docker','inspect','--format','{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}',PEER) assert http(deckip,'/api/v1/status')['status']==401 login=http(deckip,'/api/v1/login','POST',{'password':password}) assert login['status']==200 cookie=login['cookie'].split(';')[0] assert http(deckip,'/api/v1/status',cookie=cookie)['status']==200 print('PASS real LAN login and authenticated API',flush=True) assert http(deckip,'/api/v1/status',token=api_token)['status']==200 assert http(deckip,'/api/v1/network',token=api_token)['status']==401 next_token=secrets.token_urlsafe(32) assert http(deckip,'/api/v1/auth/token','POST',{'current_password':password,'new_token':next_token},cookie)['status']==200 assert http(deckip,'/api/v1/status',token=api_token)['status']==401 assert http(deckip,'/api/v1/status',token=next_token)['status']==200 assert http(deckip,'/api/v1/status',cookie=cookie)['status']==200 next_password=secrets.token_urlsafe(32) assert http(deckip,'/api/v1/auth/password','POST',{'current_password':password,'new_password':next_password},cookie)['status']==200 assert http(deckip,'/api/v1/status',cookie=cookie)['status']==401 assert http(deckip,'/api/v1/login','POST',{'password':password})['status']==401 password=next_password login=http(deckip,'/api/v1/login','POST',{'password':password}) assert login['status']==200 cookie=login['cookie'].split(';')[0] print('PASS proxy forwards API token; rotations persist through privileged helper; old credentials rejected',flush=True) # Keys stay in this Python process and stdin pipes; never printed. private_a=run('docker','exec',PEER,'wg','genkey') private_b=run('docker','exec',PEER,'wg','genkey') public_a=run('docker','exec','-i',PEER,'wg','pubkey',data=private_a+'\n') public_b=run('docker','exec','-i',PEER,'wg','pubkey',data=private_b+'\n') conf=f'[Interface]\nPrivateKey = {private_a}\nAddress = 10.240.77.1/32\nListenPort = 51822\n[Peer]\nPublicKey = {public_b}\nEndpoint = {peerip}:51823\nAllowedIPs = 10.240.77.2/32\nPersistentKeepalive = 1\n' status=rpc('import',config=conf) assert private_a not in json.dumps(status) and public_b not in json.dumps(status) peerconf=f'[Interface]\nPrivateKey = {private_b}\nListenPort = 51823\n[Peer]\nPublicKey = {public_a}\nEndpoint = {deckip}:51822\nAllowedIPs = 10.240.77.1/32\nPersistentKeepalive = 1\n' execute(PEER,'''import json,sys,subprocess,os v=json.load(sys.stdin) def cmd(*a):subprocess.run(a,check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) cmd('ip','link','add','peerwg0','type','wireguard') fd=os.open('/tmp/peer.conf',os.O_WRONLY|os.O_CREAT,0o600) with os.fdopen(fd,'w') as f:f.write(v) cmd('wg','setconf','peerwg0','/tmp/peer.conf');os.unlink('/tmp/peer.conf') cmd('ip','addr','add','10.240.77.2/32','dev','peerwg0') cmd('ip','link','set','peerwg0','up') cmd('ip','route','add','10.240.77.1/32','dev','peerwg0') ''',peerconf) rpc('connect') for _ in range(20): if rpc('status')['connected']:break time.sleep(.5) else:raise RuntimeError('No real WireGuard handshake') assert http('10.240.77.1','/api/v1/status',cookie=cookie)['status']==403 print('PASS actual WireGuard handshake; LAN-only blocks tunnel ingress',flush=True) status=rpc('mode',mode='both');trial=status['pending']['id'] assert http('10.240.77.1','/api/v1/status',cookie=cookie)['status']==200 assert http(deckip,'/api/v1/network/confirm','POST',{'trial_id':trial},cookie)['status']==400 assert http('10.240.77.1','/api/v1/network/confirm','POST',{'trial_id':trial},cookie)['status']==200 print('PASS both paths; only actual tunnel ingress can confirm',flush=True) status=rpc('mode',mode='tunnel');trial=status['pending']['id'] assert http(deckip,'/api/v1/status',cookie=cookie)['status']==403 assert http('10.240.77.1','/api/v1/network/confirm','POST',{'trial_id':trial},cookie)['status']==200 rpc('disconnect') assert http(deckip,'/api/v1/status',cookie=cookie)['status']==403 assert rpc('status')['mode']=='tunnel' print('PASS tunnel-only closes LAN and stays closed after disconnect',flush=True) status=rpc('mode',mode='lan');trial=status['pending']['id'] assert http(deckip,'/api/v1/network/confirm','POST',{'trial_id':trial},cookie)['status']==200 rpc('connect') for _ in range(20): if rpc('status')['connected']:break time.sleep(.5) rpc('mode',mode='tunnel') run('docker','restart',DECK) for _ in range(20): try: status=rpc('status') break except Exception:time.sleep(.5) assert status['mode']=='lan' and not status['pending'] print('PASS restart discards unconfirmed trial and restores confirmed mode',flush=True) for _ in range(20): try: restored=http(deckip,'/api/v1/login','POST',{'password':password}) if restored['status']==200:break except Exception:pass time.sleep(.5) else:raise RuntimeError('Changed password did not survive container restart') assert http(deckip,'/api/v1/status',token=next_token)['status']==200 assert http(deckip,'/api/v1/status',token=api_token)['status']==401 print('PASS changed password and token survive container restart',flush=True) after=run('docker','inspect','--format','{{.Id}} {{.State.StartedAt}}',*ids) if ids else '' assert baseline==after print('PASS production container start times unchanged',flush=True) finally: for name in (DECK,PEER):run('docker','rm','-f',name,check=False) state.cleanup() if __name__=='__main__':main()