#!/usr/bin/env python3 """Project-scoped, read-only traffic admission; attestation never calls a model.""" import argparse import hashlib import json import os from pathlib import Path import re import stat import subprocess import sys import tempfile import time import urllib.request BASE = Path('/mnt/PoolOne/apps/youyidian-3d') POLICY = BASE / 'release-policy/desktop-admission' RECEIPTS = POLICY / 'receipts' ENV_FILE = BASE / '.env' PRIVATE = BASE / 'detail-generator-private' PROXY = BASE / 'local-proxy/youyidian-3d-material/nginx.conf' NETWORK = 'youyidian-3d_app-network' NAMES = {'youyidian-3d-material-blue', 'youyidian-3d-material-green'} VERSION = 1 class Rejected(Exception): """Only fixed, non-secret reason codes may leave this process.""" def require(condition, code): if not condition: raise Rejected(code) def command(args): try: result = subprocess.run(args, capture_output=True, text=True, timeout=12) require(result.returncode == 0, 'runtime_inspection_failed') return result.stdout except (OSError, subprocess.TimeoutExpired): raise Rejected('runtime_inspection_failed') from None def physical(path, directory=False, owner=None, mode=None): """Never follow a substituted private store, policy file or ancestor.""" path = Path(path) for part in (path, *path.parents): require(not part.is_symlink(), 'symlink_rejected') try: value = path.stat() except OSError: raise Rejected('required_path_missing') from None require(stat.S_ISDIR(value.st_mode) if directory else stat.S_ISREG(value.st_mode), 'path_type_mismatch') if owner is not None: require((value.st_uid, value.st_gid) == owner, 'path_owner_mismatch') if mode is not None: require(stat.S_IMODE(value.st_mode) == mode, 'path_mode_mismatch') return value def enabled_from_env(text): # Accept the unambiguous subset of Docker --env-file syntax. In particular, # quotes/comments in a value are literal, not dotenv syntax. Ambiguity stops # the release instead of silently turning authorization off. values = [] for line in text.removeprefix('\ufeff').splitlines(): line = line.lstrip() if not line or line.startswith('#'): continue key, sep, value = line.partition('=') if key == 'DETAIL_GENERATOR_ENABLED': require(sep and value in ('true', 'false'), 'approved_flag_invalid') values.append(value) elif key.strip() == 'DETAIL_GENERATOR_ENABLED': raise Rejected('approved_flag_invalid') require(len(values) == 1, 'approved_flag_ambiguous') return values[0] == 'true' def environment_values(lines, key): return [line.partition('=')[2] for line in lines if line.partition('=')[0] == key] def config_target(text): names = set(re.findall(r'^\s*add_header X-Material-Proxy-Target ([^;\s]+) always;\s*$', text, re.M)) ips = set(re.findall(r'^\s*server (\d+\.\d+\.\d+\.\d+):3000\s+[^;]*;\s*$', text, re.M)) require(len(names) == 1 and names <= NAMES and len(ips) == 1, 'proxy_target_ambiguous') return next(iter(names)), next(iter(ips)) def inspect_target(name, expected_enabled, private_identity): require(name in NAMES, 'container_scope_rejected') try: data = json.loads(command(['docker', 'inspect', name])) except (ValueError, TypeError): raise Rejected('runtime_inspection_invalid') from None require(isinstance(data, list) and len(data) == 1, 'runtime_inspection_invalid') d = data[0] require(d.get('Name') == '/' + name and d.get('State', {}).get('Running') is True, 'container_not_running') container_id, image_id = d.get('Id', ''), d.get('Image', '') require(re.fullmatch(r'[a-f0-9]{64}', container_id), 'container_identity_invalid') require(re.fullmatch(r'sha256:[a-f0-9]{64}', image_id), 'image_identity_invalid') cfg = d.get('Config') or {} env = cfg.get('Env') or [] expected = 'true' if expected_enabled else 'false' require(environment_values(env, 'DETAIL_GENERATOR_ENABLED') == [expected], 'runtime_flag_mismatch') if expected_enabled: require(environment_values(env, 'DETAIL_GENERATOR_STORAGE_DIR') == ['/app/detail-generator-private'], 'runtime_storage_config_mismatch') mounts = [m for m in d.get('Mounts', []) if m.get('Destination') == '/app/detail-generator-private'] require(len(mounts) == 1, 'private_mount_missing_or_ambiguous') mount = mounts[0] require(mount.get('Source') == str(PRIVATE) and mount.get('Type') == 'bind' and mount.get('RW') is True, 'private_mount_mismatch') # Access check only. Never create/delete material files in a gate. access = "require('fs').accessSync('/app/detail-generator-private',require('fs').constants.R_OK|require('fs').constants.W_OK)" command(['docker', 'exec', '--user', '1001:1001', name, 'node', '-e', access]) revision = (cfg.get('Labels') or {}).get('org.opencontainers.image.revision', '') require(re.fullmatch(r'[a-f0-9]{40}', revision), 'revision_invalid') require(command(['docker', 'exec', name, 'cat', '/app/DEPLOYMENT_COMMIT']).strip() == revision, 'baked_revision_mismatch') network = (d.get('NetworkSettings', {}).get('Networks') or {}).get(NETWORK) or {} ip = network.get('IPAddress', '') require(re.fullmatch(r'\d+\.\d+\.\d+\.\d+', ip), 'network_identity_invalid') return {'container': name, 'containerId': container_id, 'imageId': image_id, 'revision': revision, 'enabled': expected_enabled, 'privateIdentity': private_identity, 'ip': ip} def http_json(url): try: opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) request = urllib.request.Request(url, headers={'User-Agent': 'gongxiangp2-desktop-admission/1', 'Connection': 'close'}) with opener.open(request, timeout=5) as response: require(response.status == 200, 'candidate_config_unavailable') return json.loads(response.read(16385)) except Rejected: raise except Exception: raise Rejected('candidate_config_unavailable') from None def current_snapshot(): physical(ENV_FILE) enabled = enabled_from_env(ENV_FILE.read_text(encoding='utf-8-sig')) identity = None if enabled: value = physical(PRIVATE, directory=True, owner=(1001, 1001), mode=0o700) identity = [value.st_dev, value.st_ino, value.st_uid, value.st_gid] physical(PROXY) name, ip = config_target(PROXY.read_text(encoding='utf-8')) value = inspect_target(name, enabled, identity) require(value['ip'] == ip, 'proxy_network_mismatch') config = http_json('http://' + ip + ':3000/api/desktop/config') require(isinstance(config, dict) and config.get('enabled') is enabled and config.get('clientId') == 'jiansun-detail-desktop' and config.get('pkce') == 'S256', 'candidate_config_mismatch') value.pop('ip') return value def policy_digest(): return hashlib.sha256(Path(__file__).read_bytes()).hexdigest() def receipt_path(snapshot): return RECEIPTS / (snapshot['containerId'] + '.json') def attest(snapshot, entry_sha): require(re.fullmatch(r'[a-f0-9]{64}', entry_sha), 'entry_digest_invalid') physical(POLICY, directory=True, owner=(0, 0), mode=0o700) physical(RECEIPTS, directory=True, owner=(0, 0), mode=0o700) receipt = {'schema': VERSION, 'policySha256': policy_digest(), 'entrySha256': entry_sha, 'verifiedAt': int(time.time()), 'snapshot': snapshot} target = receipt_path(snapshot) if target.exists(): physical(target, owner=(0, 0), mode=0o600) descriptor, temp = tempfile.mkstemp(prefix='.attest-', dir=RECEIPTS) try: with os.fdopen(descriptor, 'w', encoding='utf-8') as stream: os.fchown(stream.fileno(), 0, 0) os.fchmod(stream.fileno(), 0o600) json.dump(receipt, stream, sort_keys=True) stream.flush() os.fsync(stream.fileno()) # Recheck identity after HTTP/filesystem observation, before publishing. require(current_snapshot() == snapshot, 'candidate_changed_during_attestation') os.replace(temp, target) finally: if os.path.exists(temp): os.unlink(temp) def validate_receipt(snapshot): physical(POLICY, directory=True, owner=(0, 0), mode=0o700) physical(RECEIPTS, directory=True, owner=(0, 0), mode=0o700) target = receipt_path(snapshot) physical(target, owner=(0, 0), mode=0o600) try: receipt = json.loads(target.read_text(encoding='utf-8')) except (OSError, ValueError): raise Rejected('receipt_invalid') from None require(receipt.get('schema') == VERSION and receipt.get('policySha256') == policy_digest() and re.fullmatch(r'[a-f0-9]{64}', receipt.get('entrySha256', '')) and receipt.get('snapshot') == snapshot, 'candidate_not_attested') def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--attest-config', action='store_true') parser.add_argument('--entry-sha256', default='') args = parser.parse_args() try: require(os.geteuid() == 0, 'root_required') snapshot = current_snapshot() if args.attest_config: attest(snapshot, args.entry_sha256) else: require(not args.entry_sha256, 'unexpected_entry_digest') validate_receipt(snapshot) print(json.dumps({'desktopAdmission': 'accepted', 'mode': 'attest' if args.attest_config else 'check', 'containerId': snapshot['containerId'], 'revision': snapshot['revision'], 'enabled': snapshot['enabled'], 'privateMountVerified': snapshot['enabled']})) return 0 except Rejected as error: print(json.dumps({'desktopAdmission': 'rejected', 'reason': str(error)}), file=sys.stderr) return 1 except Exception: # Docker output, env values, HTTP bodies and exception details can carry # credentials. Never forward them to deployment logs or journald. print(json.dumps({'desktopAdmission': 'rejected', 'reason': 'inspection_failed'}), file=sys.stderr) return 1 if __name__ == '__main__': sys.exit(main())