65 lines
2.7 KiB
Python
65 lines
2.7 KiB
Python
"""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)
|