# Evolution 7 — Worker isolation review

Code: `cain/talos/talos_worker.py` (gateway side), `cain/talos/talos_sandbox_launcher.py` (sandbox side),
`cain/landlock_sandbox.py` (Landlock binding, unchanged). Tests: `test_e7_worker_isolation.py`,
`test_e7_enforcement.py` (E7-E-034..039, E7-E-044).

## Boundary

    gateway --Popen--> L0 launcher: applies and checks every control, then waits
                        `-- L1 supervisor: pid 1 of a new PID namespace, trusted, enforces the wall clock
                             `-- P workload: reads the request, runs it

The request is written to the sandbox only after L0 reports READY with every required control measured and
the gateway has itself confirmed in `/proc` that L1 is pid 1 of a nested PID namespace and a child of the
launcher it spawned. If anything is missing the request is never read by any process and the kernel returns
`ENFORCEMENT_UNAVAILABLE`. There is no degraded mode.

## Controls applied, in order, and how each is verified

| # | Control | Verified by | Denies (observed) |
|---|---|---|---|
| 1 | `PR_SET_PDEATHSIG(SIGKILL)` on launcher, supervisor, workload | return value, parent pid | survival after the gateway dies (E7-W-047) |
| 2 | new PID, IPC, UTS namespaces | supervisor reports pid 1; gateway checks `NSpid`/`PPid` | signalling or seeing host processes: `ESRCH` (E7-W-021) |
| 3 | new network namespace when the scope has no network | interface list is exactly `lo` | TCP, UDP, host loopback, abstract sockets (E7-W-010, 012) |
| 4 | rlimits: AS, CPU, NOFILE, FSIZE, CORE=0, NPROC | read back | memory (E7-W-040), descriptors and file size (E7-W-044), core dumps (E7-W-032) |
| 5 | optional worker uid/gid, supplementary groups cleared | read back | makes NPROC effective (E7-W-045) |
| 6 | all capabilities dropped: bounding emptied, ambient cleared, eff/perm/inh zero | `/proc/self/status` | setgroups, chown, mknod, sethostname: `EPERM` (E7-W-020) |
| 7 | `no_new_privs` | `/proc/self/status` | privilege gain through setuid or file capabilities |
| 8 | Landlock ruleset: filesystem; TCP connect/bind ports | a path outside every rule is opened and must fail `EACCES` | reads, writes, creates, renames, links, symlink and `..` escapes outside the scope (E7-W-001..006) |
| 9 | seccomp-bpf: `socket()` families; denylist of ptrace, bpf, mount, namespace, module, io_uring, keyring, clock syscalls; `clone3` -> ENOSYS | `PR_GET_SECCOMP`; `socket(AF_UNIX)` must fail | Unix-socket connect, netlink, raw and packet sockets, user namespaces, mounts (E7-W-010, 011, 020) |
| 10 | empty launcher environment; fixed base environment plus signed variables | — | inherited secrets (E7-W-030) |
| 11 | only stdout and stderr open in the workload | — | inherited descriptors (E7-W-031) |
| 12 | supervisor kills the namespace at the wall-clock limit; gateway backstop 3 s later; gateway kills on output cap | — | timeout bypass (E7-W-041), process-tree survival (E7-W-042), output flooding (E7-W-043) |

Why 3 and 9 exist: on this host a Landlock-only worker connected to a Unix socket outside its scope and sent
UDP (measured before the fix). Landlock's network rules cover TCP bind and connect only.

A control that silently does nothing is caught: seven launcher variants with one control turned into a no-op
each abort with the matching reason and the canary is not created (E7-E-039).

## Controls NOT applied

* **No mount namespace or pivot_root.** The host filesystem tree is visible to path lookups; Landlock denies access.
* **No cgroup.** No cap on total memory across processes, no `pids.max`. For a uid-0 worker Linux does not
  enforce `RLIMIT_NPROC`; a fork bomb is bounded only by the wall-clock kill of the namespace.
* **No syscall allowlist.** The seccomp filter is a denylist plus the socket rule.
* **Network allowed = TCP port rules only.** With `allow_network`, the worker shares the host network
  namespace: UDP (including DNS to any server), ICMP via datagram sockets and abstract Unix sockets are not
  restricted, and destination addresses are not restricted, only ports.
* **No user namespace.** The launcher needs root (CAP_SYS_ADMIN) to unshare; an unprivileged gateway gets
  `ENFORCEMENT_UNAVAILABLE`.
* **No cancellation.** Revocation after start does not stop a running worker.
* **x86_64 only.** Other architectures abort (`SECCOMP_UNSUPPORTED_ARCHITECTURE`).

## Beyond this boundary

Kernel exploits reachable through allowed syscalls, hostile native code targeting the kernel, CPU and cache
side channels, and a same-host root attacker all defeat this sandbox. For hostile multi-tenant code the
appropriate boundary is a microVM (Firecracker, Cloud Hypervisor) or gVisor, with cgroup limits, and hardware
attestation where the claim requires it. None of that is implemented or claimed here.

## Host requirements

Linux with Landlock ABI >= 4, seccomp, PID/NET/IPC/UTS namespaces, `pidfd_open`; x86_64; the gateway able to
`unshare` (root). Observed only on: Linux 7.0.0-38, Landlock ABI 8, Python 3.14.4, euid 0. Launch overhead
about 70 ms per request on this host (not a benchmark).

## Safety of these tests

No real fork bomb or memory bomb was run: this host carries live services. Process-count and memory tests use
small fixed ceilings (8 processes, 128 MiB). No real network peer was contacted; sockets and "secrets" are
fixtures created by the tests.
