net-kingdom/tools/bootstrap-recovery/retrieve-identity-offline.py
tegwick 1764f40b43 Prepare independent outage recovery and Storage Share documentation
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
2026-10-16 17:59:18 +02:00

138 lines
6.2 KiB
Python

#!/usr/bin/env python3
"""Download one reviewed ciphertext using privately entered provider access.
No OpenBao, SSO, Kubernetes, credential discovery, provider writes or age key.
Run by the operator in their own terminal, never through a recorded agent PTY.
"""
import argparse
import base64
from datetime import datetime, timezone
import getpass
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import re
import shutil
import signal
import sys
import tempfile
import urllib.request
HERE = Path(__file__).resolve().parent
spec = importlib.util.spec_from_file_location('offsite', HERE / 'identity-offsite.py')
offsite = importlib.util.module_from_spec(spec)
spec.loader.exec_module(offsite)
ACCOUNT_BASE = offsite.HOST + '/remote.php/dav/files/Backup/railiance-backups/net-kingdom-identity/'
def validate(record):
if (record.get('schema') != 'netkingdom.identity-offsite.v1'
or record.get('status') != 'readback_verified'
or record.get('provider') != 'Storage Share'
or record.get('folder') != 'Backup/railiance-backups/net-kingdom-identity'
or not offsite.NAME.fullmatch(record.get('artifact', ''))
or not re.fullmatch('[a-f0-9]{64}', record.get('ciphertext_sha256', ''))
or type(record.get('ciphertext_bytes')) is not int
or not 0 < record['ciphertext_bytes'] <= offsite.LIMIT
or not re.fullmatch(r'"[^"\x00-\x20]{1,180}"', record.get('etag', ''))):
raise offsite.Refused()
def retrieve(record, secret, mode, client=None):
validate(record)
if mode not in {'account', 'folder-share'} or not secret or any(c in secret for c in '\r\n\x00'):
raise offsite.Refused()
auth = offsite.headers(secret) if mode == 'folder-share' else {
'Authorization': 'Basic ' + base64.b64encode(('Backup:' + secret).encode()).decode(),
'Accept-Encoding': 'identity'}
url = (offsite.BASE if mode == 'folder-share' else ACCOUNT_BASE) + record['artifact']
client = client or offsite.opener()
directory = None
complete = False
try:
with client.open(urllib.request.Request(url, method='HEAD', headers=auth), timeout=30) as response:
if offsite.etag(response, url, record['ciphertext_bytes']) != record['etag']:
raise offsite.Refused()
directory = Path(tempfile.mkdtemp(prefix='nk49-identity-fetched-', dir='/tmp'))
target = directory / record['artifact']
auth['If-Match'] = record['etag']
count = 0
digest = hashlib.sha256()
with client.open(urllib.request.Request(url, headers=auth), timeout=180) as response:
if offsite.etag(response, url, record['ciphertext_bytes']) != record['etag']:
raise offsite.Refused()
fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, 'wb') as output:
while block := response.read(1024 * 1024):
count += len(block)
if count > record['ciphertext_bytes']:
raise offsite.Refused()
digest.update(block)
output.write(block)
output.flush()
os.fsync(output.fileno())
if count != record['ciphertext_bytes'] or digest.hexdigest() != record['ciphertext_sha256']:
raise offsite.Refused()
with client.open(urllib.request.Request(url, method='HEAD', headers=auth), timeout=30) as response:
if offsite.etag(response, url, record['ciphertext_bytes']) != record['etag']:
raise offsite.Refused()
complete = True
# Closed allowlist: never copy arbitrary source-receipt fields to output.
result = {k: record[k] for k in ('schema', 'provider', 'folder', 'artifact',
'ciphertext_bytes', 'ciphertext_sha256', 'etag')}
result.update(status='readback_verified', fetched_path=str(target),
independent_owner_retrieval_verified=True,
credential_source='independently_held_provider_access',
provider_auth_mode=mode, openbao_contacted=False, sso_contacted=False,
provider_writes_performed=False, age_private_key_used=False,
secret_values_exported=False, object_unchanged_during_transfer=True,
downloaded_at=datetime.now(timezone.utc).isoformat())
return result
finally:
if directory is not None and not complete:
shutil.rmtree(directory)
def interrupted(signum, frame):
raise InterruptedError()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--source-receipt', required=True)
parser.add_argument('--receipt', required=True)
parser.add_argument('--auth', choices=('account', 'folder-share'), default='account')
args = parser.parse_args()
os.umask(0o077)
fd = os.open(args.receipt, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
result = {'status': 'failed', 'error': 'offline_retrieval_failed_private_diagnostics_suppressed'}
try:
record = json.loads(Path(args.source_receipt).read_text())
validate(record)
# getpass must never fall back to echoed stdin (e.g. a captured agent tool).
with open('/dev/tty', 'r+') as terminal:
if not terminal.isatty():
raise offsite.Refused()
secret = getpass.getpass('Storage Share Backup app password or exact folder-share token: ', stream=terminal)
try:
result = retrieve(record, secret, args.auth)
finally:
secret = None
except (Exception, KeyboardInterrupt):
pass
finally:
with os.fdopen(fd, 'w') as output:
json.dump(result, output, indent=2)
output.write('\n')
ok = result['status'] == 'readback_verified'
print('OK: ciphertext retrieved without OpenBao or SSO; ETag, size and SHA256 verified.' if ok
else 'ERROR: retrieval refused; private diagnostics suppressed. Use a new receipt path to retry.')
return 0 if ok else 1
if __name__ == '__main__':
for sig in (signal.SIGINT, signal.SIGTERM, signal.SIGHUP):
signal.signal(sig, interrupted)
sys.exit(main())