Repository navigation
feat(supervisor): NFLOG-based bypass detection to drop the CAP_SYSLOG requirement #2382
Description
Problem Statement
Sandbox pods/containers currently request CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_SYS_PTRACE, and CAP_SYSLOG. With Kubernetes user namespaces (GA in 1.36, already supported via enable_user_namespaces) the first three become namespaced and lose their host-level power — but CAP_SYSLOG does not. The kernel checks syslog(2)//dev/kmsg access against the initial user namespace (capable(), not ns_capable()), so in a hostUsers: false pod the granted CAP_SYSLOG is a no-op and the bypass detection monitor silently degrades.
The only consumer of CAP_SYSLOG is the bypass detection monitor in openshell-supervisor-process, which tails dmesg --follow for nftables log entries (prefix openshell:bypass:<ns>:) emitted by the per-workload netns REJECT rules. Reading the host-global kernel ring buffer from inside a sandbox is also a mild information leak, and requires the nf_log_all_netns sysctl on the node.
Proposed Design
Switch bypass log delivery from kernel-printk to nfnetlink_log (NFLOG):
- During netns setup (privileged startup, before supervisor seccomp hardening), bind an
AF_NETLINK/NETLINK_NETFILTERsocket to an NFLOG group inside the workload netns. Netns affinity is fixed atsocket()time; binding needs onlyCAP_NET_ADMINover the netns owner, which works under user namespaces. - Generate the nftables log rules as
log prefix <p> group <N>when the socket bind succeeds; keep the existinglog ... flags skuidprintk rules as an automatic fallback when it fails (e.g. kernel withoutnfnetlink_log). - The bypass monitor consumes NFLOG packet messages (prefix attribute for filtering,
NFULA_UIDfor workload UID, raw IP payload for dst/ports/proto) and reports through the existing OCSF dual-emit path. Thedmesgpath remains as fallback (still useful in the VM driver, which runs with full capabilities). - Drop
CAP_SYSLOGfrom the Kubernetes, Docker, and Podman driver capability sets.
Enforcement is unaffected: the nftables REJECT rules do not depend on the log delivery mechanism. NFLOG delivery is netns-scoped, so the monitor no longer reads host-global kernel logs, and nf_log_all_netns is no longer needed on the NFLOG path.
Alternatives Considered
- Keep dmesg, relax
kernel.dmesg_restricton nodes — exposes host kernel logs to every pod on the node; strictly worse. - eBPF-based detection —
bpf()is also initial-userns-gated (CAP_BPF/CAP_SYS_ADMINin init ns), so it has the same problem asCAP_SYSLOG, plus a new dependency. - Netlink crate dependency (
netlink-packet-netfilter/libnetfilter_logbindings) — the required protocol subset (group bind, copy mode, packet attr parsing) is ~stable kernel ABI and small; a hand-rolledlibcimplementation avoids new dependencies in a security-sensitive crate and matches its existing raw-FFI style.
Agent Investigation
CAP_SYSLOG's only consumer isbypass_monitor::spawn(crates/openshell-supervisor-process/src/bypass_monitor/mod.rs); drivers request it inopenshell-driver-kubernetes/src/driver.rs,openshell-driver-docker/src/lib.rs,openshell-driver-podman/src/container.rs.- No eBPF usage anywhere in the workspace; netns/nftables management shells out to
iproute2/nft, so NFLOG covers the whole detection path. - The supervisor seccomp prelude does not restrict
socket(); the workload runtime filter blocks non-NETLINK_ROUTEnetlink sockets, so the sandboxed workload cannot bind the NFLOG group itself. - The sidecar topology has no bypass log listener today (no
CAP_SYSLOGthere either); it keeps printk rules unchanged. - Implementation is complete on a branch with unit tests (netlink message layout, datagram parsing, IPv4/IPv6 packet parsing, ruleset generation); Linux
cargo check/clippy(pedantic+nursery)/tests pass. PR to follow.
Activity
drew commentedon Jul 22, 2026
gaveezy commentedon Jul 22, 2026
@drew thanks for the triage, I will submit the PR for the implementation soon
drew commentedon Jul 22, 2026
Before implementing, can you talk a little more about how this would get configured and when we would use it? If understand this replaces an existing implementation and is used for all drivers.
gaveezy commentedon Jul 23, 2026
@drew for the VM and Docker backend, no configuration changes, for Kubernetes if we want to opt-in user namespace then need to set
pod.spec.hostUsersfield tofalsegithub-actions commentedon Aug 7, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.
github-actions commentedon Oct 13, 2026
Closing this issue after 14 days with no activity since it was marked stale. Reopen it if the work is still relevant.