enroll(rustdesk-server): test suite + bridge poll (status-key overlay, ws + punch-hole-amplification tests)
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user