Add first-run admin setup and independent password and API token rotation

This commit is contained in:
Mikei386
2026-09-28 15:02:41 +02:00
parent e86e540ae4
commit 9f3a13e45f
20 changed files with 651 additions and 76 deletions
+31 -3
View File
@@ -35,14 +35,15 @@ 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):
value=dict(host=host,path=path,method=method,cookie=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)
@@ -56,7 +57,8 @@ def main():
state=tempfile.TemporaryDirectory(prefix='athena-deck-test-')
password=secrets.token_urlsafe(32)
salt=secrets.token_bytes(16)
auth=dict(salt=salt.hex(),hash=hashlib.pbkdf2_hmac('sha256',password.encode(),salt,600000).hex())
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')
@@ -78,6 +80,22 @@ def main():
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')
@@ -132,6 +150,16 @@ cmd('ip','route','add','10.240.77.1/32','dev','peerwg0')
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)