# IHVRK Linux runtime

Two separate mechanisms with different strengths. Neither is the authorization boundary; `ExecutionGate` is.

## 1. `talos_gatekeeper.sh`: cgroup v2 freeze (containment)

**Terms.**
* A *task* is a kernel thread of execution (a process or a thread).
* A *cgroup* is a resource-control group of tasks, a directory under `/sys/fs/cgroup`. `cgroup.freeze` stops
  every task in that cgroup and its descendant cgroups.
* A *PID namespace* is a separate pid numbering. Freezing a cgroup does not freeze a namespace; the two are
  independent, and a namespace's tasks may span cgroups.
* A *container* is a deployment convention that usually combines namespaces and one cgroup.

**What it does.** It validates that the configured cgroup lies under `$IHVRK_CGROUP_ROOT`, is on cgroup2fs and is
writable, and that the target pid is a member. It records the pid's start time, writes `1` to `cgroup.freeze`, and
waits for `frozen 1` in `cgroup.events`. Then it polls a decision command with a timeout. It thaws only on
`AUTHORIZE`, and only if the same process (same start time, not a zombie, still in the cgroup) is still there. Every
other outcome, including its own death by SIGKILL, leaves the cgroup frozen. `--on-deny kill` uses `cgroup.kill`.
Every transition goes to a JSON-lines log.

**Tested (on this host, in disposable `systemd-run --scope` units; `tests/runtime/`, 22 tests):** pending freezes
and authorize thaws (F1); DENY/EXPIRED/REVOKED/garbage stay frozen (F2); timeout (F3); descendants frozen (F4); kill
on deny (F5); enforcer killed → still frozen (F6); pid outside the cgroup refused (F7); frozen process died → no thaw
(F8, found defect F-003); path traversal and shell metacharacters refused (F9); bad root or relative command
refused (F10); log complete (F11); helper outside the cgroup NOT contained (F12, asserted on purpose).

**Residual escape paths.**

| Path | Contained? |
|---|---|
| tasks inside the cgroup, including forked children | yes |
| helper process started outside the cgroup (another service, cron, a remote agent) | **no** (F12) |
| sockets already open | no new syscalls run, so nothing is sent from user space; data already queued in the kernel is still transmitted |
| writes already in the page cache | flushed by the kernel regardless |
| the agent moving itself to another cgroup | needs write access to the target `cgroup.procs`; give the agent no delegation of its cgroup (systemd `Delegate=no`) |
| shared resources (a database the agent already sent a query to) | outside the host's control |
| root inside the agent | can thaw itself: an unprivileged agent is a hard requirement |

Privileges: root, or a user that owns the delegated subtree. Example unit: `systemd/ihvrk-gatekeeper@.service`.

## 2. eBPF

| Program | Hooks | Semantics | Status on this host |
|---|---|---|---|
| `ebpf/ihvrk_lsm.bpf.c` | `lsm/socket_connect`, `lsm/bprm_check_security`, `lsm/file_permission` (write/append only) | **prevention**: returns `-EPERM` before the operation for tasks whose cgroup id is in `ihvrk_blocked`; keeps an earlier LSM's denial | compiles (clang 21), all 3 programs pass the kernel verifier (`evidence/ebpf_verifier_load.txt`); **cannot attach**: active LSMs are `lockdown,capability,landlock,yama,apparmor,ima,evm` (no `bpf`) |
| `ebpf/ihvrk_telemetry.bpf.c` | tracepoints `sys_enter_connect/execve/write` | **telemetry only**: a tracepoint's return value is ignored, so it cannot block anything | compiles, passes the verifier, not attached |

Design points for the LSM program:
* Identity is `bpf_get_current_cgroup_id()`, so moving between cgroups changes it (see the escape table).
* Map lifecycle: the daemon inserts `cgroup_id → epoch` when a request becomes pending and deletes it after
  the permit is consumed. Maps are bounded (4096 entries). A full map makes the insert fail, and the daemon must
  then freeze or refuse the request.
* Events go to a 64 KiB ring buffer. When it is full the denial still happens and `ihvrk_dropped` is incremented,
  so event loss is counted and never silent.
* Daemon crash: pinned maps persist, so blocked cgroups stay blocked (fail-closed). Removal requires an
  explicit map update.
* The agent cannot change the maps without `CAP_BPF`/`CAP_SYS_ADMIN`; an unprivileged agent is required.
* `file_permission` covers `write(2)` on already-open files too, but not `mmap`ed writes or I/O through another
  process.
* Kernel requirement: `CONFIG_BPF_LSM=y` (present) **and** `bpf` in the `lsm=` boot parameter (absent). Enabling it
  means a reboot with changed kernel parameters, which is an owner decision.
* **Not written:** the user-space daemon that attaches the programs and manages the maps. Without it, and without
  attachment, no kernel-level denial has been demonstrated.

Rebuild and verifier-load check (needs root, clang, bpftool; loads into the kernel verifier WITHOUT attaching, then
unpins):

```bash
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
clang -O2 -g -target bpf -D__TARGET_ARCH_x86 -I. -c ihvrk_lsm.bpf.c -o ihvrk_lsm.bpf.o
bpftool prog loadall ihvrk_lsm.bpf.o /sys/fs/bpf/ihvrk_verify && rm -rf /sys/fs/bpf/ihvrk_verify
```

No hardware or non-maskable interrupt is used or claimed (L-NMI).
