Lite mode: seccomp user notification¶
Lite mode is unprivileged. A seccomp filter pauses the mediated syscalls and hands them to the hallpass supervisor through a notification file descriptor. The supervisor reads the syscall's arguments from the process's memory, resolves the path, applies the policy and, if needed, asks you. Allowed syscalls continue with SECCOMP_USER_NOTIF_FLAG_CONTINUE; denied ones fail with EACCES.
This is what lets hallpass ask instead of just failing the call. It is best effort: because the allowed syscall is re-executed by the kernel, a malicious multi-threaded process can change the path between the check and the kernel acting on it (a TOCTOU race). Strict mode closes that.
In practice lite mode is still useful, because that gap only matters against a process deliberately racing the check. Most escapes are accidents — a wrong path, an overeager script, a command run in the wrong directory — not adversarial races, and lite mode catches those reliably. Strict mode closes the TOCTOU race so filesystem enforcement also holds against a deliberate racer.
Neither mode contains a hostile agent today: programs are matched by path only, and connect is not mediated by default, so a sandboxed process can still ask a service outside the sandbox (systemd-run --user, a container daemon, the systemd or D-Bus socket) to write for it. See Limitations.
The filter¶
The seccomp filter only decides which syscalls reach the supervisor; the policy is in the supervisor. It sees the syscall number and its integer arguments, not the memory they point to:
- Syscalls are paused unconditionally (
unlinkat,execve,connect...), or when an argument says so:openatwith a write flag (O_WRONLY,O_RDWR,O_CREAT,O_TRUNC),sendtowith an address. - Syscalls that hide their flags or address in a struct (
openat2,sendmsg,sendmmsg) are always paused, and the supervisor continues those that need no check. - Some are refused without asking:
io_uring_setup(EPERM), which would perform I/O without the syscalls the filter sees, and new user and mount namespaces (unshare,clone,setns;clone3fails withENOSYS, so libc falls back toclone), because inside a private mount namespace a process could bind-mount a protected directory at a harmless-looking path. On a workstation,--allow-usernslets namespaces through; strict mode always does. - Only the categories selected with
--mediate(or a policy'smediate) are installed.execveis paused in every case.
The syscalls of each category are listed in Files, Network and Programs.
For each paused syscall, the supervisor reads the strings and structs it needs from /proc/<pid>/mem, resolves relative paths from /proc/<pid>/cwd or /proc/<pid>/fd/<n>, follows symlinks one component at a time, and checks with SECCOMP_IOCTL_NOTIF_ID_VALID that the syscall is still waiting before using what it read. While a prompt is pending, the thread only reacts to fatal signals (SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV, Linux 5.19 and later).
On a workstation¶
hallpass re-executes itself as a small child, which sets no_new_privs, installs the filter, sends the notification file descriptor back to the supervisor over a socketpair, waits for the supervisor to be ready, and runs the command. Everything the command starts inherits the filter.
The supervisor is the subreaper of the processes the command leaves behind. If it exits while they run, their mediated syscalls fail (fail closed). See Background processes.
In Kubernetes¶
hallpass-node, a DaemonSet, plays the supervisor for the containers of a node, with the OCI seccomp agent protocol:
- Its NRI plugin, on
CreateContainerof an opted-in pod, adds a seccomp profile generated from the same rule table as the CLI's, merged into the container's own profile (RuntimeDefault,LocalhostorUnconfined). The profile'slistenerPathis the agent's unix socket, and its listener metadata names the pod, container and policy. - runc installs the filter when it starts the container, connects to
listenerPath, and sends the notification file descriptor and the container's state (pid, annotations) withSCM_RIGHTS. - The agent mediates each container as a session. It runs in the host pid namespace and resolves paths through
/proc/<pid>/root, so rules and prompts use the container's paths (/workspace/x).
Copies of the notification file descriptors are kept by hallpass-fd-store, so the sessions survive an agent restart. When every copy is closed, the mediated syscalls fail with ENOSYS. See Restarts and upgrades and Fail open.