Files
cc-ci/tests/rustdesk-server/custom/test_identity_key.py
T

96 lines
3.4 KiB
Python

"""rustdesk-server — identity-key round-trip across three independent surfaces (§4.3-style).
hbbs mints an ed25519 identity keypair on first boot into the shared rustdesk_data volume;
the PUBLIC key is what clients configure to pin the server. This is the recipe's defining
generated state, and we read it back through three genuinely different paths:
1. HTTPS through Traefik + the cc-ci status sidecar: GET /id_ed25519.pub (the serving path
a remote client could observe),
2. hbbs's OWN startup telemetry: the `Key: <b64>` log line (docker service logs),
3. the volume file itself, read inside the status sidecar (cat /pub/id_ed25519.pub).
All three must AGREE and be a valid base64-encoded 32-byte ed25519 public key. A wedged or
crash-looping hbbs fails (1) with 404 forever (no key file); a key mismatch across surfaces
would mean we are reading different deployments' state — either way this test names the
broken layer. Non-vacuous: every surface carries app-generated state, nothing is echoed from
config.
"""
from __future__ import annotations
import base64
import os
import re
import subprocess
import sys
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
from harness import lifecycle # noqa: E402
_KEY_RE = re.compile(rb"Key: ([A-Za-z0-9+/]{43}=)")
def _stack(domain: str) -> str:
return domain.replace(".", "_")
def _https_key(domain: str) -> tuple[int, str]:
return lifecycle.http_fetch(domain, "/id_ed25519.pub")
def _log_key(domain: str) -> bytes | None:
out = subprocess.run(
["docker", "service", "logs", "--tail", "200", f"{_stack(domain)}_app"],
capture_output=True,
text=True,
timeout=60,
).stdout
m = _KEY_RE.search(out.encode())
return m.group(1) if m else None
def _volume_key(domain: str) -> str:
return lifecycle.exec_in_app(domain, ["cat", "/pub/id_ed25519.pub"], service="status").strip()
def test_identity_key_agrees_across_https_log_and_volume(live_app):
https_status, https_body = _https_key(live_app)
assert https_status == 200, (
f"status route /id_ed25519.pub not serving (HTTP {https_status}) — hbbs never minted "
"its key, or the status sidecar/routing is broken"
)
https_key = https_body.strip().encode()
log_key = _log_key(live_app)
assert log_key is not None, "hbbs startup log carries no 'Key: <b64>' line (telemetry)"
vol_key = _volume_key(live_app).encode()
assert https_key == log_key == vol_key, (
"identity key disagrees across surfaces — "
f"https={https_key[:20]!r} log={log_key[:20]!r} volume={vol_key[:20]!r} "
"(reading different deployments' state?)"
)
raw = base64.b64decode(https_key, validate=True)
assert len(raw) == 32, f"not a 32-byte ed25519 public key ({len(raw)} bytes)"
@pytest.mark.parametrize(
"path",
[
"/",
"/id_ed25519", # the PRIVATE key lives in the same volume — must never be served
"/db_v2.sqlite3",
"/db_v2.sqlite3-wal",
],
)
def test_status_route_serves_only_the_public_key(live_app, path):
"""Anything but the exact .pub location 404s (default surface = deny). The private key and
the sqlite peer DB share the status sidecar's volume — a 200 on any of these paths would
be a real exposure."""
status, _ = lifecycle.http_fetch(live_app, path)
assert status == 404, f"status route served {path} (HTTP {status}) — exposure, not health"