R4 (e2e half): 3-hop nested-SSH circuit delivery + per-hop pcaps (gate item 1)
run_circuit_fixture stands up an isolated docker N-hop nested-SSH chain, pipes a seed-deterministic self-generated payload through it, verifies end-to-end delivery, captures + checksums a per-hop pcap, emits R3 events, and always tears the circuit down. Containment is load-bearing: assert_isolated refuses local/unknown engines up front, every hop is built through the ForwarderPlan guard, only our own fixture bytes move between our own containers, and the runner is wired for docker only (multipass e2e refused, not faked). Adds the sor-hop fixture image (alpine + sshd + tcpdump, lab-relay only) and a docker-gated acceptance test that skips where no daemon/image is present. Instrument-validation gate item 1 GREEN: live 3-hop delivery verified, 3 distinct hops, pcaps checksum. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
"""R4 (e2e half) — 3-hop nested-SSH circuit delivery acceptance check.
|
||||
|
||||
Acceptance predicate (instrument-validation gate item 1, the traffic-moving half
|
||||
of roadmap R4): a >=3-hop self-traffic circuit of *isolated* containers delivers
|
||||
a known, seed-deterministic payload end-to-end, and a per-hop pcap exists and
|
||||
checksums. This suite drives the real ``run_circuit_fixture`` against a live
|
||||
docker engine and asserts exactly that conjunct.
|
||||
|
||||
Containment is still law here: the runner refuses ``local``/unknown engines up
|
||||
front (re-checked offline below), only self-generated fixture bytes move, they
|
||||
move only between our own containers on our own engine, and the circuit is always
|
||||
torn down. The e2e cell needs a live docker daemon + the ``sor-hop`` image; it is
|
||||
skipped (not failed) where either is absent, so the offline suite stays green on
|
||||
any host.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
from cmd_chat.sor.config import Domain, SorRng
|
||||
from cmd_chat.sor.forwarder import (
|
||||
HOP_IMAGE,
|
||||
CircuitError,
|
||||
_seed_payload,
|
||||
run_circuit_fixture,
|
||||
)
|
||||
|
||||
|
||||
def _docker_ready() -> bool:
|
||||
if shutil.which("docker") is None:
|
||||
return False
|
||||
try:
|
||||
subprocess.run(["docker", "info"], capture_output=True, timeout=15, check=True)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _image_present() -> bool:
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["docker", "image", "inspect", HOP_IMAGE],
|
||||
capture_output=True, timeout=15, check=False,
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
return out.returncode == 0
|
||||
|
||||
|
||||
live = pytest.mark.skipif(
|
||||
not (_docker_ready() and _image_present()),
|
||||
reason="requires a live docker daemon and the sor-hop:latest image",
|
||||
)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Offline: containment refusals hold even for the e2e entry point.
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_e2e_refuses_local_engine():
|
||||
with pytest.raises(Exception):
|
||||
run_circuit_fixture(1, engine="local")
|
||||
|
||||
|
||||
def test_e2e_refuses_non_docker_isolated_engine():
|
||||
# multipass is isolated but the e2e runner is only wired for docker.
|
||||
with pytest.raises(CircuitError):
|
||||
run_circuit_fixture(1, engine="multipass")
|
||||
|
||||
|
||||
def test_e2e_refuses_fewer_than_three_hops():
|
||||
with pytest.raises(CircuitError):
|
||||
run_circuit_fixture(1, engine="docker", hops=2)
|
||||
|
||||
|
||||
def test_seed_payload_is_deterministic_self_traffic():
|
||||
# Same seed -> identical fixture bytes (no real data); different seed differs.
|
||||
assert _seed_payload(42) == _seed_payload(42)
|
||||
assert _seed_payload(42) != _seed_payload(43)
|
||||
# It is exactly the R1 PADDING stream (self-generated, reproducible).
|
||||
stream = SorRng(42).stream(Domain.PADDING)
|
||||
assert _seed_payload(42, 16) == bytes(stream.next_below(256) for _ in range(16))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Gate item 1 — the live conjunct.
|
||||
# --------------------------------------------------------------------------- #
|
||||
@live
|
||||
def test_three_hop_circuit_delivers_and_checksums(tmp_path):
|
||||
seed = 0xC0FFEE
|
||||
result = run_circuit_fixture(seed, engine="docker", hops=3, out_root=tmp_path)
|
||||
|
||||
# (a) end-to-end delivery of the known payload.
|
||||
assert result.delivered is True
|
||||
assert result.received_sha256 == result.payload_sha256
|
||||
|
||||
# (b) a per-hop pcap exists and checksums (3 distinct hops).
|
||||
assert set(result.pcaps.keys()) == {0, 1, 2}
|
||||
for i in (0, 1, 2):
|
||||
pcap = result.pcaps[i]
|
||||
assert pcap.exists() and pcap.stat().st_size > 0
|
||||
assert len(result.pcap_sha256[i]) == 64
|
||||
|
||||
# (c) provenance: the R3 event log was sealed.
|
||||
assert result.events_sha256 and len(result.events_sha256) == 64
|
||||
|
||||
# (d) containment: three distinct isolated containers, engine never local.
|
||||
assert result.engine == "docker"
|
||||
assert len(set(result.hop_containers)) == 3
|
||||
Reference in New Issue
Block a user