diff --git a/api/tests/test_invariants.py b/api/tests/test_invariants.py index 8aeb5cf..1877cd4 100644 --- a/api/tests/test_invariants.py +++ b/api/tests/test_invariants.py @@ -409,6 +409,16 @@ def test_i05_runner_never_mounts_the_host_docker_socket(): assert "/var/run/docker.sock" not in ln, "I-5: never mount the host docker socket" +def test_i05_jobs_get_a_network_per_job_not_a_shared_bridge(): + """A shared flat bridge lets concurrent jobs see each other, and breaks + service-container DNS (service aliases only exist on a per-job network). + Per-job is both stricter and correct.""" + cfg = (ROOT / "deploy" / "runner" / "config.yaml").read_text() + active = [ln for ln in cfg.splitlines() if ln.strip() and not ln.strip().startswith("#")] + net = [ln for ln in active if ln.strip().startswith("network:")] + assert net and net[0].strip() == 'network: ""', "jobs must get a per-job network" + + def test_i05_jobs_cannot_bind_mount_from_the_daemon_host(): cfg = (ROOT / "deploy" / "runner" / "config.yaml").read_text() assert "valid_volumes: []" in cfg diff --git a/deploy/runner/config.yaml b/deploy/runner/config.yaml index 78888d8..07e0230 100644 --- a/deploy/runner/config.yaml +++ b/deploy/runner/config.yaml @@ -26,10 +26,18 @@ cache: dir: /data/cache container: - # Job containers join the dind daemon's own bridge. NOT the forge network: - # untrusted code must never be able to reach the forge's Postgres or its - # environment (I-5). - network: bridge + # Empty = act creates a NETWORK PER JOB and removes it afterwards. + # + # This started as `bridge` for isolation, which was a mistake in both + # directions. It broke service containers — Postgres came up healthy but the + # job could not resolve the name `postgres`, because service DNS aliases only + # exist on a per-job network — and it was *weaker* isolation, since every + # concurrent job shared one flat bridge and could see its neighbours. + # + # A per-job network is both correct and stricter. Still no route to the forge: + # these networks live inside the dind daemon, which has no forge attachment + # at all. + network: "" privileged: false options: workdir_parent: /workspace