Files
truf-server/tests/test_edge_deployment.py
T
2026-09-30 20:30:56 +03:00

651 lines
29 KiB
Python

import configparser
import importlib.util
import json
from pathlib import Path
import re
import stat
import pytest
import yaml
ROOT = Path(__file__).resolve().parents[1]
UPDATER_PATH = ROOT / "deploy" / "fail2ban" / "truf_caddy_admin_denylist.py"
SPEC = importlib.util.spec_from_file_location("truf_caddy_admin_denylist", UPDATER_PATH)
updater_module = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(updater_module)
def updater(tmp_path, runner=None, now=1_700_000_000):
state_dir = tmp_path / "state"
snippet_dir = tmp_path / "snippet"
state_dir.mkdir(mode=0o700)
snippet_dir.mkdir(mode=0o750)
snippet = snippet_dir / "admin-denylist.caddy"
snippet.write_text(updater_module.EMPTY_SNIPPET, encoding="ascii")
instance = updater_module.DenylistUpdater(
state_dir / "admin-denylist.json",
snippet,
project_directory=tmp_path / "project",
env_file=tmp_path / "edge.env",
runner=runner or (lambda command: True),
clock=lambda: now,
)
return instance, state_dir / "admin-denylist.json", snippet
def test_updater_persists_canonical_state_and_expires_automatically(tmp_path):
commands = []
instance, state_path, snippet_path = updater(
tmp_path, runner=lambda command: commands.append(command) or True,
)
result = instance.update("ban", "2001:db8:0:0::2")
assert result["bans"] == {"2001:db8::2": 1_700_086_400}
state = json.loads(state_path.read_text(encoding="ascii"))
assert state["bans"] == result["bans"]
assert re.fullmatch(r"[0-9a-f]{64}", state["applied_sha256"])
assert "2001:db8::2" in snippet_path.read_text(encoding="ascii")
assert any("validate" in command for command in commands)
assert any("reload" in command and "unix//run/caddy-admin.sock" in command for command in commands)
commands.clear()
persisted = updater_module.DenylistUpdater(
state_path, snippet_path, runner=lambda command: commands.append(command) or True,
clock=lambda: 1_700_086_401,
)
status = persisted.update("status")
assert status["expired"] == 1
assert status["bans"] == {}
assert snippet_path.read_text(encoding="ascii") == updater_module.EMPTY_SNIPPET
assert json.loads(state_path.read_text(encoding="ascii"))["bans"] == {}
assert len(commands) == 2
def test_updater_reuses_persisted_applied_digest_without_reloading(tmp_path):
instance, state_path, snippet_path = updater(tmp_path)
instance.update("ban", "192.0.2.4")
commands = []
persisted = updater_module.DenylistUpdater(
state_path, snippet_path, runner=lambda command: commands.append(command) or True,
clock=lambda: 1_700_000_001,
)
assert persisted.update("status")["bans"] == {"192.0.2.4": 1_700_086_400}
assert commands == []
def test_updater_rejects_corrupt_state_before_commands_or_snippet_changes(tmp_path):
commands = []
instance, state_path, snippet_path = updater(
tmp_path, runner=lambda command: commands.append(command) or True,
)
state_path.write_text('{"version":1,"bans":[]}', encoding="ascii")
before = snippet_path.read_bytes()
with pytest.raises(updater_module.UpdateError):
instance.update("status")
assert commands == []
assert snippet_path.read_bytes() == before
def test_updater_rejects_symlinked_snippet_when_supported(tmp_path):
commands = []
instance, state_path, snippet_path = updater(
tmp_path, runner=lambda command: commands.append(command) or True,
)
target = snippet_path.with_name("target.caddy")
target.write_text(updater_module.EMPTY_SNIPPET, encoding="ascii")
snippet_path.unlink()
try:
snippet_path.symlink_to(target)
except OSError:
pytest.skip("symlink creation is unavailable")
with pytest.raises(updater_module.UpdateError):
instance.update("ban", "192.0.2.5")
assert commands == []
assert not state_path.exists()
@pytest.mark.parametrize("value", ["", "1.2.3.4/32", "1.2.3.4;touch /tmp/x", "::%eth0", "0.0.0.0", "ff02::1"])
def test_updater_rejects_non_ip_or_unsafe_addresses_without_commands(tmp_path, value):
commands = []
instance, state_path, snippet_path = updater(
tmp_path, runner=lambda command: commands.append(command) or True,
)
before = snippet_path.read_bytes()
with pytest.raises(updater_module.UpdateError):
instance.update("ban", value)
assert commands == []
assert not state_path.exists()
assert snippet_path.read_bytes() == before
def test_updater_renders_deterministically_and_bounds_matcher_lines():
bans = {"2001:db8::2": 4, "192.0.2.10": 3, "2001:db8::1": 2, "192.0.2.2": 1}
rendered = updater_module.render_snippet(bans).decode("ascii")
matcher = next(line for line in rendered.splitlines() if line.startswith("@truf_admin_denied_"))
assert matcher.endswith("192.0.2.2 192.0.2.10 2001:db8::1 2001:db8::2")
assert max(map(len, rendered.splitlines())) < 4096
assert "global" not in rendered and "iptables" not in rendered
def test_updater_rolls_back_state_and_snippet_when_reload_fails(tmp_path):
instance, state_path, snippet_path = updater(tmp_path)
instance.update("status")
old_state = state_path.read_bytes()
old_snippet = snippet_path.read_bytes()
reloads = 0
def fail_first_reload(command):
nonlocal reloads
if "reload" in command:
reloads += 1
return reloads > 1
return True
instance.runner = fail_first_reload
with pytest.raises(updater_module.CommandFailure):
instance.update("ban", "198.51.100.9")
assert reloads == 2
assert state_path.read_bytes() == old_state
assert snippet_path.read_bytes() == old_snippet
def test_updater_rolls_back_without_reload_when_validation_fails(tmp_path):
instance, state_path, snippet_path = updater(tmp_path)
instance.update("status")
old_state = state_path.read_bytes()
old_snippet = snippet_path.read_bytes()
commands = []
def fail_validation(command):
commands.append(command)
return "validate" not in command
instance.runner = fail_validation
with pytest.raises(updater_module.CommandFailure):
instance.update("ban", "198.51.100.11")
assert len(commands) == 1
assert state_path.read_bytes() == old_state
assert snippet_path.read_bytes() == old_snippet
def test_fail2ban_filter_counts_only_redacted_supplied_bad_credentials():
parser = configparser.ConfigParser(interpolation=None)
parser.read(ROOT / "deploy" / "fail2ban" / "filter.d-truf-admin-auth.conf", encoding="ascii")
failregex = parser["Definition"]["failregex"]
host = r"(?P<host>[0-9A-Fa-f:.]+)"
pattern = re.compile(failregex.replace("<HOST>", host))
bad = '{"level":"info","ts":1700000000.1,"logger":"http.log.access.admin_auth_failures","msg":"handled request","status":401,"event":"admin_auth_failure","remote_ip":"203.0.113.8"}'
assert pattern.match(bad).group("host") == "203.0.113.8"
assert not pattern.match(bad.replace('"status":401', '"status":200'))
assert not pattern.match(bad.replace('"event":"admin_auth_failure",', ""))
assert not pattern.match(bad.replace('"remote_ip":"203.0.113.8"', '"remote_ip":"203.0.113.8","uri":"/secret"'))
challenge = '{"level":"info","ts":1700000000.1,"status":401,"remote_ip":"203.0.113.8"}'
assert not pattern.match(challenge)
def test_fail2ban_jail_is_persistent_bounded_and_admin_only():
jail = (ROOT / "deploy" / "fail2ban" / "jail.d-truf-admin-auth.local").read_text(encoding="ascii")
persistence = (ROOT / "deploy" / "fail2ban" / "fail2ban.d-truf-persistence.local").read_text(encoding="ascii")
action = (ROOT / "deploy" / "fail2ban" / "action.d-truf-caddy-admin-denylist.conf").read_text(encoding="ascii")
assert "maxretry = 2" in jail
assert "findtime = 10m" in jail
assert "bantime = 24h" in jail
assert "dbfile = /var/lib/fail2ban/fail2ban.sqlite3" in persistence
assert "actionban = /usr/local/sbin/truf-caddy-admin-denylist ban '<ip>'" in action
forbidden = ("iptables", "nft", "firewall-cmd", "ufw")
assert not any(name in (jail + action).lower() for name in forbidden)
def test_caddy_routes_and_failure_log_are_closed_and_redacted():
caddy = (ROOT / "deploy" / "edge" / "Caddyfile").read_text(encoding="ascii")
assert "admin unix//run/caddy-admin.sock" in caddy
assert "skip_install_trust" in caddy
assert "trusted_proxies" not in caddy
assert "import {$TRUF_EDGE_TLS_INCLUDE:/etc/caddy/tls/automatic.caddy}" in caddy
site = caddy.split("{$TRUF_EDGE_HOST} {", 1)[1]
assert site.index("import admin_security") < site.index("@worker path")
assert "@worker path /api/v1/worker/*" in caddy
assert "@admin_root path /{$TRUF_ADMIN_PREFIX}" in caddy
assert "@admin path /{$TRUF_ADMIN_PREFIX}/*" in caddy
assert "redir * /{$TRUF_ADMIN_PREFIX}/ 308" in caddy
assert "header_down -Strict-Transport-Security" in caddy
assert "uri strip_prefix /{$TRUF_ADMIN_PREFIX}" in caddy
assert "request_header -X-Truf-Admin-Edge" in caddy
assert "request_header -X-Truf-Admin-Operator" in caddy
assert "header_up X-Truf-Admin-Edge {$TRUF_ADMIN_EDGE_MARKER}" in caddy
assert "header_up X-Truf-Admin-Operator {http.auth.user.id}" in caddy
assert caddy.count("header_up -X-Truf-Admin-Operator") == 1
assert "uri path_regexp ^ /admin-internal" in caddy
assert "reverse_proxy 127.0.0.1:8766" in caddy
assert "127.0.0.1:8767" not in caddy
assert "import {$TRUF_ADMIN_DENYLIST_FILE:/etc/caddy/denylist/admin-denylist.caddy}" in caddy
assert "basic_auth bcrypt" in caddy
assert 'respond "" 404' in caddy
assert "request delete" in caddy and "resp_headers delete" in caddy
assert "log routine_access" in caddy and "output discard" in caddy
assert "log_name admin_auth_failures" in caddy
assert "header Authorization *" in caddy
assert "@admin_unauthorized" in caddy
assert caddy.count('WWW-Authenticate "Basic realm=\\"truf-admin\\""') == 2
for header in ("no-store", "same-origin", "Content-Security-Policy", "nosniff", "Strict-Transport-Security"):
assert header in caddy
assert "unsafe-inline" not in caddy
assert not re.search(r"\bpath\s+/dashboard(?:\s|$)", caddy)
assert not re.search(r"\bpath\s+/admin(?:\s|$)", caddy)
def test_edge_compose_only_opts_runtime_namespace_into_https_publication():
base = yaml.safe_load((ROOT / "compose.yaml").read_text(encoding="ascii"))
edge = yaml.safe_load((ROOT / "compose.edge.yaml").read_text(encoding="ascii"))
assert "ports" not in base["services"]["runtime"]
assert base["services"]["runtime"]["read_only"] is True
assert edge["services"]["runtime"]["ports"] == [{
"target": 443, "published": "443", "protocol": "tcp", "mode": "host",
}]
service = edge["services"]["edge"]
assert "TRUF_CADDY_ADMIN" not in service["environment"]
assert service["network_mode"] == "service:runtime"
assert "ports" not in service
assert service["read_only"] is True
assert service["cap_drop"] == ["ALL"]
assert service["cap_add"] == ["NET_BIND_SERVICE"]
assert service["build"]["target"] == "edge"
config = yaml.safe_load((ROOT / "app" / "config.linux.yaml").read_text(encoding="utf-8"))
assert config["supervisor"]["worker_api"]["enabled"] is False
assert config["supervisor"]["worker_api"]["admin"]["enabled"] is False
assert config["supervisor"]["worker_api"]["address"] == "127.0.0.1"
assert config["supervisor"]["dashboard"]["address"] == "127.0.0.1"
def test_edge_image_and_startup_require_pinned_caddy_hash_and_random_prefix():
dockerfile = (ROOT / "deploy" / "edge" / "Dockerfile").read_text(encoding="ascii")
entrypoint = (ROOT / "deploy" / "edge" / "entrypoint.sh").read_text(encoding="ascii")
assert re.search(r"^FROM caddy:2\.10\.2-alpine@sha256:[0-9a-f]{64} AS edge$", dockerfile, re.MULTILINE)
assert "${#prefix}" in entrypoint and "^[0-9a-f]{64}$" in entrypoint
assert "supported bcrypt hash" in entrypoint
assert "TRUF_ADMIN_EDGE_MARKER must encode 256 random bits" in entrypoint
assert "deploy/edge/automatic-tls.caddy /etc/caddy/tls/automatic.caddy" in dockerfile
assert "localhost requires an explicit static TLS include" in entrypoint
assert "TRUF_EDGE_TLS_INCLUDE must be an absolute Caddy TLS include" in entrypoint
assert 'exec caddy run --config "$caddyfile"' in entrypoint
assert "FROM edge AS edge-e2e" in dockerfile
assert "admin 127.0.0.1:2019" in dockerfile
def test_shared_host_edge_is_loopback_only_marker_gated_and_route_confined():
compose = yaml.safe_load((ROOT / "compose.shared-host.yaml").read_text(encoding="ascii"))
provision = compose["services"]["provision"]
runtime = compose["services"]["runtime"]
edge = compose["services"]["edge"]
assert provision == {"cpus": 0.9, "mem_limit": "720m"}
assert runtime == {
"network_mode": "host", "cpus": 0.9, "mem_limit": "720m",
}
assert edge["network_mode"] == "service:runtime"
assert "ports" not in edge and "cap_add" not in edge
assert edge["cap_drop"] == ["ALL"]
assert edge["environment"]["TRUF_EDGE_MODE"] == "shared-host-edge-v1"
assert "TRUF_SHARED_INGRESS_MARKER" in edge["environment"]
assert compose["volumes"]["data"] == {
"external": True, "name": "truf-remote-server-data",
}
caddy = (ROOT / "deploy" / "edge" / "Caddyfile.shared-host").read_text(encoding="ascii")
assert "http://:18766" in caddy
assert "\tbind 127.0.0.1" in caddy
assert "http://127.0.0.1:18766" not in caddy
assert "admin unix//run/caddy-admin.sock" in caddy
assert "trusted_proxies static 127.0.0.1/32 ::1/128" in caddy
assert "X-Truf-Shared-Ingress {$TRUF_SHARED_INGRESS_MARKER}" in caddy
assert "request_header -X-Truf-Shared-Ingress" in caddy
assert "log_append remote_ip {http.request.client_ip}" in caddy
assert "tls " not in caddy.lower()
snippet = (ROOT / "deploy" / "edge" / "host-caddy-shared.caddy").read_text(encoding="ascii")
assert snippet.count("reverse_proxy 127.0.0.1:18766") == 2
assert "@truf_worker path /api/v1/worker/*" in snippet
assert "@truf_admin path /{$TRUF_ADMIN_PREFIX}" in snippet
assert snippet.count(
"header_up X-Truf-Shared-Ingress {$TRUF_SHARED_INGRESS_MARKER}"
) == 2
assert "header_up -X-Truf-Shared-Ingress" not in snippet
assert "tls" not in snippet.lower()
assert "handle {" not in snippet
def test_shared_host_denylist_uses_forwarded_client_identity_and_private_reload(tmp_path):
instance, _, snippet = updater(tmp_path)
instance = updater_module.DenylistUpdater(
instance.state_path,
snippet,
project_directory=tmp_path / "project",
env_file=tmp_path / "edge.env",
profile=updater_module.SHARED_HOST_PROFILE,
runner=lambda command: True,
clock=lambda: 1_700_000_000,
)
instance.update("ban", "198.51.100.7")
assert "client_ip 198.51.100.7" in snippet.read_text(encoding="ascii")
assert any(str(value).endswith("compose.shared-host.yaml") for value in instance.validate_command)
assert "/etc/caddy/Caddyfile.shared-host" in instance.validate_command
assert "unix//run/caddy-admin.sock" in instance.reload_command
def test_edge_e2e_image_inputs_are_exact_and_host_orchestrator_stays_host_only():
dockerignore = (ROOT / ".dockerignore").read_text(encoding="ascii").splitlines()
allowed = {line[1:] for line in dockerignore if line.startswith("!")}
edge_e2e_inputs = {name for name in allowed if "edge_e2e" in name}
assert edge_e2e_inputs == {
"deploy/fail2ban/edge_e2e_docker_shim.py",
"tests/edge_e2e_backend.py",
"tests/edge_e2e_client.py",
}
assert "docker/verify_edge_e2e.py" not in allowed
dockerfile = (ROOT / "Dockerfile").read_text(encoding="ascii")
test_stage = dockerfile.split("FROM runtime-base AS test", 1)[1].split(
"FROM runtime-base AS runtime", 1,
)[0]
assert "COPY --chown=10001:10001 tests/ /opt/truf/tests/" in test_stage
assert "COPY --chown=10001:10001 docker/ /opt/truf/docker/" in test_stage
assert "edge_e2e != {'edge_e2e_backend.py', 'edge_e2e_client.py'}" in test_stage
assert "COPY app/" not in test_stage
def test_production_server_omits_trufflehog_but_worker_and_test_retain_it():
dockerfile = (ROOT / "Dockerfile").read_text(encoding="ascii")
server_native = dockerfile.split("FROM python-base AS native-dependencies", 1)[1].split(
"FROM native-dependencies AS dependencies", 1,
)[0]
test_stage = dockerfile.split("FROM runtime-base AS test", 1)[1].split(
"FROM runtime-base AS runtime", 1,
)[0]
worker_native = dockerfile.split("FROM python-base AS worker-native-dependencies", 1)[1].split(
"FROM worker-native-dependencies AS worker", 1,
)[0]
assert "trufflehog" not in server_native.lower()
assert "/usr/local/bin/trufflehog" in worker_native
assert "/usr/local/bin/trufflehog" in test_stage
def test_edge_e2e_orchestrator_is_private_labelled_and_uses_production_updater():
orchestrator = (ROOT / "docker" / "verify_edge_e2e.py").read_text(encoding="ascii")
backend = (ROOT / "tests" / "edge_e2e_backend.py").read_text(encoding="ascii")
assert "TEST_IMAGE = 'truf-edge-e2e-runtime:test'" in orchestrator
assert "EDGE_IMAGE = 'truf-edge-e2e:test'" in orchestrator
assert "FAIL2BAN_IMAGE = 'truf-fail2ban-edge-e2e:test'" in orchestrator
assert "truf-local:edge" not in orchestrator
assert "'network', 'create', '--driver', 'bridge', '--internal'" in orchestrator
assert "'--network', 'container:' + self.names['backend']" in orchestrator
assert "--publish" not in orchestrator and "--privileged" not in orchestrator
assert "docker compose" not in orchestrator.lower()
assert "DenylistUpdater(" not in orchestrator
assert "update_denylist(" not in orchestrator
assert "'/usr/bin/fail2ban-client'" in orchestrator
assert "'--pid', 'container:' + self.names['edge']" in orchestrator
assert "TRUF_CADDY_ADMIN" not in orchestrator
assert "'--cap-add', 'KILL'" not in orchestrator
assert "'--user', '10001:10001', '--read-only', '--cap-drop', 'ALL'" in orchestrator
assert "/var/run/docker.sock" not in orchestrator
assert "expected={direct_ip}" in orchestrator
assert "['unbanip', direct_ip]" in orchestrator
assert "['container', 'kill', '--signal', 'SIGKILL', fail2ban['id']]" in orchestrator
assert "['gitlab', 'dockerhub', 'huggingface']" in backend
assert "db.enqueue_targets('gitlab', 'gitlab'" in backend
assert "baseline['operation_id']" in orchestrator
def test_production_runbook_requires_fixed_host_agent_and_runtime_paths():
readme = (ROOT / "deploy" / "edge" / "README.md").read_text(encoding="ascii")
for required in (
"truf_host_agent_install.py install",
"truf_host_agent_install.py validate",
"/run/truf/host-agent.sock",
"/etc/truf/runtime/config.yaml",
"/etc/truf/runtime",
"/etc/truf/worker-packages",
"/data/worker-packages",
"root:root mode 0644",
"/data/managed-files",
"-f compose.yaml -f compose.edge.yaml",
):
assert required in readme
assert readme.index("Create `/etc/truf-edge/edge.env`") < readme.index(
"truf_host_agent_install.py install"
)
assert "build runtime edge" in readme
def test_real_caddy_e2e_crawls_all_admin_routes_and_detail():
client = (ROOT / "tests" / "edge_e2e_client.py").read_text(encoding="ascii")
for route in (
"'overview'", "'search'", "'supervisor'", "'logs'", "'config'",
"'secrets'", "'files'", "'operations'", "'audit'",
):
assert route in client
assert "f'operations/{same_site_operation_id}'" in client
def test_fail2ban_edge_e2e_image_is_pinned_minimal_and_uses_production_gate_files():
dockerfile = (ROOT / "deploy" / "fail2ban" / "Dockerfile.edge-e2e").read_text(
encoding="ascii",
)
assert re.search(
r"^FROM caddy:2\.10\.2-alpine@sha256:[0-9a-f]{64} AS caddy-edge-e2e$",
dockerfile,
re.MULTILINE,
)
assert re.search(
r"^FROM debian:bookworm-slim@sha256:[0-9a-f]{64} AS fail2ban-edge-e2e$",
dockerfile,
re.MULTILINE,
)
assert "fail2ban=1.0.2-2" in dockerfile
for path in (
"filter.d-truf-admin-auth.conf",
"jail.d-truf-admin-auth.local",
"action.d-truf-caddy-admin-denylist.conf",
"fail2ban.d-truf-persistence.local",
"truf_caddy_admin_denylist.py",
):
assert path in dockerfile
assert "apt-get install -y --no-install-recommends" in dockerfile
assert "docker-ce" not in dockerfile and "docker.io" not in dockerfile
assert "admin 127.0.0.1:2019" in dockerfile
runtime = (ROOT / "docker" / "Dockerfile.edge-e2e").read_text(encoding="ascii")
assert runtime.startswith("FROM truf-worker-test:test AS edge-e2e-runtime\n")
assert "tests/edge_e2e_backend.py tests/edge_e2e_client.py" in runtime
def test_fail2ban_edge_e2e_reload_shim_accepts_only_production_updater_commands(tmp_path):
path = ROOT / "deploy" / "fail2ban" / "edge_e2e_docker_shim.py"
spec = importlib.util.spec_from_file_location("truf_edge_e2e_docker_shim", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
assert module.classify_command(module.VALIDATE_COMMAND) == "validate"
assert module.classify_command(module.RELOAD_COMMAND) == "reload"
for command in (
module.VALIDATE_COMMAND[:-1],
module.RELOAD_COMMAND + ("other",),
tuple("/var/run/docker.sock" if value == "edge" else value for value in module.RELOAD_COMMAND),
):
with pytest.raises(ValueError):
module.classify_command(command)
env_file = tmp_path / "edge.env"
env_file.write_text(
"TRUF_EDGE_HOST=localhost\n"
"TRUF_EDGE_TLS_INCLUDE=/etc/caddy/tls/static-tls.caddy\n"
f"TRUF_ADMIN_PREFIX={'a' * 64}\n"
"TRUF_ADMIN_USER=edge-e2e-admin\n"
f"TRUF_ADMIN_PASSWORD_HASH=$2b$12${'A' * 53}\n"
f"TRUF_ADMIN_EDGE_MARKER={'b' * 64}\n",
encoding="ascii",
)
values = module.load_environment(env_file)
assert set(module.REQUIRED_ENV).issubset(values)
assert values["PATH"] == "/usr/bin:/bin"
module.VALIDATION_ERROR_PATH = tmp_path / "validation.error"
secret = values["TRUF_ADMIN_PASSWORD_HASH"]
module.record_validation_error(
(secret + " " + "c" * 64 + "\n" + "x" * 8192).encode("ascii"), values,
)
evidence = module.VALIDATION_ERROR_PATH.read_bytes()
assert len(evidence) <= 4096
assert secret.encode("ascii") not in evidence
assert ("c" * 64).encode("ascii") not in evidence
def test_docker_e2e_verifiers_snapshot_foreign_state_and_revalidate_cleanup():
paths = (
ROOT / "docker" / "verify_packaged_workers.py",
ROOT / "docker" / "verify_edge_e2e.py",
)
for path in paths:
source = path.read_text(encoding="ascii")
assert "MAX_DOCKER_RESOURCES" in source
assert "CONTAINER_METADATA_FORMAT" in source
assert "VOLUME_METADATA_FORMAT" in source
assert "snapshot_foreign()" in source
assert "metadata_snapshot(exclude_owned=True)" in source
assert "foreign_docker_state_changed" in source
assert "def guarded_stop(self):" in source
assert "inspect_owned('containers', name, expected)" in source
assert "inspect_owned('volumes', name, expected)" in source
assert "inspect_owned('networks', name, expected)" in source
assert "def isolated_test_subnet(" in source
assert "'--subnet', isolated_test_subnet(" in source
assert "return f'198." in source
assert "['container', 'rm', '--force'" not in source
assert "system prune" not in source.lower()
def test_e2e_failure_evidence_is_sanitized_metadata_only():
packaged = (ROOT / "docker" / "verify_packaged_workers.py").read_text(encoding="ascii")
edge = (ROOT / "docker" / "verify_edge_e2e.py").read_text(encoding="ascii")
for source in (packaged, edge):
assert "'stage': label, 'class': failure_class, 'exit_code': exit_code" in source
assert "'artifacts': os.fspath(verifier.run_root) if verifier is not None else None" not in source
assert "'owned': verifier" not in source
assert "clear_run_artifacts()" in source
assert "secret_present_in_evidence" in source
assert "'stderr': stderr.decode" not in edge
assert "'stdout': stdout.decode" not in edge
assert "client-" + "failure.json" not in edge
assert "write_bytes(phase_root / 'client.log'" not in packaged
assert "write_bytes(phase_root / 'server.log'" not in packaged
assert "write_json(phase_root / 'completed.json'" not in packaged
assert "retain_safe_evidence('summary.json', summary)" in packaged
def test_edge_failure_stop_revalidates_then_targets_only_captured_id():
path = ROOT / "docker" / "verify_edge_e2e.py"
spec = importlib.util.spec_from_file_location("truf_verify_edge_e2e_pure", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
run_id = "a" * 16
name = f"truf-edge-e2e-{run_id}-backend"
identifier = "b" * 64
class Runner:
def __init__(self):
self.calls = []
def docker(self, label, arguments, **kwargs):
self.calls.append((label, arguments))
if label == "inspect_owned_container":
return 0, json.dumps({
"id": identifier, "name": "/" + name, "run": run_id,
"kind": "backend", "running": True, "paused": False,
"restarting": False,
}).encode("ascii"), b""
assert label == "guarded_stop_owned_container"
return 0, b"", b""
runner = Runner()
resources = module.Resources(runner, run_id)
resources.created["containers"][name] = {"id": identifier, "kind": "backend"}
assert resources.guarded_stop() == 1
assert [label for label, _ in runner.calls] == [
"inspect_owned_container", "inspect_owned_container",
"guarded_stop_owned_container",
]
assert runner.calls[-1][1] == ["container", "stop", "--time", "30", identifier]
@pytest.mark.parametrize(
("filename", "method"),
[
("verify_packaged_workers.py", "retain_safe_evidence"),
("verify_edge_e2e.py", "retain_failure_evidence"),
],
)
def test_failure_evidence_removes_raw_artifacts_before_safe_write(tmp_path, filename, method):
path = ROOT / "docker" / filename
spec = importlib.util.spec_from_file_location("truf_" + path.stem + "_pure", path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
verifier = object.__new__(module.Verifier)
verifier.root = tmp_path
build = tmp_path / "build"
build.mkdir()
prefix = "packaged-worker-e2e-" if "packaged" in filename else "edge-e2e-"
verifier.run_root = build / (prefix + "a" * 16)
verifier.run_root.mkdir()
raw = verifier.run_root / "fixture"
raw.mkdir()
client_log = raw / "client.log"
client_log.write_text("SECRET-SENTINEL", encoding="ascii")
client_log.chmod(stat.S_IRUSR)
raw.chmod(stat.S_IRUSR | stat.S_IXUSR)
if "packaged" in filename:
verifier.tokens = {"target": "SECRET-SENTINEL"}
verifier.device_tokens = {}
getattr(verifier, method)("failure.json", {
"stage": "test_failure", "class": "Failure", "exit_code": 7,
})
else:
verifier.admin_password = "SECRET-SENTINEL"
verifier.edge_marker = "edge-marker"
verifier.prefix_secret = "prefix-secret"
verifier.tokens = {}
getattr(verifier, method)({
"stage": "test_failure", "class": "Failure", "exit_code": 7,
})
assert [item.name for item in verifier.run_root.iterdir()] == ["failure.json"]
failure = json.loads((verifier.run_root / "failure.json").read_text(encoding="ascii"))
assert failure == {"stage": "test_failure", "class": "Failure", "exit_code": 7}
def test_packaged_restart_proof_uses_stable_durable_state_not_log_timing():
source = (ROOT / "docker" / "verify_packaged_workers.py").read_text(encoding="ascii")
assert "def assert_restart_stable(" in source
assert "_restart_rescanned_or_rewrote_bundle" in source
assert "checks >= 2" in source
assert "time.monotonic() - started >= 2.5" in source
assert "wait_windows_restart_attempts" not in source
assert "wait_linux_restart_attempts" not in source
def test_worker_packaging_boundary_allows_shared_db_free_modules_only():
readme = (ROOT / "docker" / "build-dependencies" / "README.md").read_text(
encoding="ascii",
)
assert "required shared DB-free modules" in readme
assert "server runtime/control authority" in readme
assert "provider and detailed" in readme and "keycheck authority" in readme
assert "server credentials, and database credentials are absent" in readme
assert "grants no provider or detailed keycheck authority" in readme
assert "PostgreSQL tools, server/test code" not in readme
def test_production_updater_cli_defaults_remain_unchanged():
args = updater_module.parse_args(["status"])
assert args.state_path == "/var/lib/truf-edge/admin-denylist.json"
assert args.snippet_path == "/etc/truf-edge/denylist/admin-denylist.caddy"
assert args.project_directory == "/opt/truf"
assert args.env_file == "/etc/truf-edge/edge.env"