"""Synthetic, network-none checks against the actual pinned Core and nginx.""" import hashlib from pathlib import Path import stat import urllib.error import urllib.request KEY = "11" * 32 def request(port, path, expected, method="GET", key=None): headers = {} if key is None else {"X-API-KEY": key} req = urllib.request.Request(f"http://127.0.0.1:{port}{path}", method=method, headers=headers) try: with urllib.request.urlopen(req, timeout=8) as response: code, body = response.status, response.read() except urllib.error.HTTPError as error: code, body = error.code, error.read() assert code == expected, (method, path, code, expected, body[:200]) assert KEY.encode() not in body, "managed key leaked in HTTP response" print(f"PASS {method} {port}{path}: {code}") return body children = set() for proc in Path('/proc').iterdir(): if not proc.name.isdigit(): continue try: command = (proc / 'cmdline').read_bytes().split(b'\x00')[0] if command.endswith(b'java') or command.startswith(b'nginx:'): assert b'QORTAL_API_KEY=' not in (proc / 'environ').read_bytes() children.add('java' if command.endswith(b'java') else 'nginx') except FileNotFoundError: pass # An unrelated short-lived fixture process exited. assert children == {'java', 'nginx'} print('PASS managed environment secret absent from actual Core/nginx children') key_path = Path("/qortal/apikey.txt") assert key_path.read_text() == KEY info = key_path.stat() assert stat.S_ISREG(info.st_mode) and stat.S_IMODE(info.st_mode) == 0o600 assert (info.st_uid, info.st_gid) == (1000, 100) assert Path("/qortal/operator-sentinel").read_bytes() == b"preserve operator data\n" settings_digest = hashlib.sha256(Path("/qortal/settings.json").read_bytes()).hexdigest() settings_receipt = Path("/qortal/fixture-settings-sha256") if settings_receipt.exists(): assert settings_receipt.read_text() == settings_digest, "operator settings changed" else: settings_receipt.write_text(settings_digest) receipt = Path("/qortal/fixture-key-inode") if receipt.exists(): assert int(receipt.read_text()) == info.st_ino, "key unexpectedly replaced" else: receipt.write_text(str(info.st_ino)) request(12391, "/admin/info", 200) request(12390, "/", 200) request(12390, "/core/admin/info", 200) request(12390, "/core/admin/stop", 404, "GET", KEY) request(12390, "/core/admin/info", 403, "POST", KEY) request(12391, "/admin/stop", 403, "GET") request(12391, "/admin/stop", 403, "GET", "22" * 32) request(12391, "/admin/info", 200) print("PASS managed key/private mode/inode and operator bytes preserved") # Explicit stop of this disposable node only, after unauthorized calls failed. request(12391, "/admin/stop", 200, "GET", KEY)