Multi-Layer Sandboxing of AI Workloads in Kubernetes
Combining VM isolation with seccomp and Linux Security Modules for in-guest confinement
With the proliferation of AI agents, safeguarding the infrastructure hosting them has become more critical than ever. Take the case of AI agents deployed as containers in Kubernetes clusters.
A container does not provide a strict security boundary; it only serves as a namespace isolation boundary. The processes running inside a container share the worker node’s kernel. If there is a vulnerability in the kernel, it is often possible for them to escape the namespace and impact the worker node including all other workloads. This trade-off may be acceptable for trusted workloads, but it is not suitable for AI agents. AI agents may execute code generated from model outputs, process untrusted input, or operate with extensive file and network access, making security a significant concern.
Solution
The most straightforward way to run untrusted workloads is to run them in a virtual machine (VM), which provides strong isolation. Kata Containers follows this approach by launching each pod inside a dedicated micro-VM with its own kernel. This provides hardware-enforced isolation, ensuring that even a successful exploit of the guest kernel cannot directly compromise the worker node.
However, VM isolation alone does not constitute a complete sandbox. Inside the VM, the workload is still largely unrestricted — it can invoke arbitrary system calls, access any file within its filesystem, and establish outbound network connections. In other words, the VM protects the host from the workload, but not necessarily the workload from itself.
This is where in-guest confinement mechanisms such as seccomp and Linux Security Modules (LSMs) become essential. Seccomp (secure computing mode) is a kernel feature that filters system calls at the process boundary, allowing only explicitly defined syscalls and blocking all others. This significantly reduces the kernel attack surface exposed to the workload. In contrast, LSMs provide a framework for enforcing fine-grained security policies on resource access within the kernel. Landlock LSM, a modern unprivileged LSM, enables path-based restrictions on filesystem access, allowing a process to be confined to only the directories it explicitly needs.
Kubefence builds on this idea by making these layers composable and operationally simple in Kubernetes. It combines Kata’s hardware-backed isolation with in-guest enforcement using seccomp and Landlock, ensuring that every container process is confined from the moment it is created, not after it starts running.
Specifically, kubefence layers the following controls on top of Kata’s VM isolation:
- Landlock confinement via nono wrap — applies path-based filesystem restrictions before the workload executes using ‘nono wrap’ (nono.sh)
- Restricted seccomp profile — enforces a syscall allowlist from the first instruction of the process
- Kata agent policy restrictions — prevents out-of-band process execution (e.g., kubectl exec) via Kubernetes APIs
Together, these mechanisms transform Kata from an isolation boundary into a multi-layer sandbox, where both the worker node and the workload are protected through complementary controls.
How it works?
Kubefence is a Node Resource Interface (NRI) plugin. NRI is an extension point in containerd/cri-o that lets you intercept container lifecycle events and modify the OCI spec before the container starts.
The plugin runs as a DaemonSet. When a pod is created with an opted-in RuntimeClass, the plugin fires on the CreateContainer event and does three things:
- Wraps the container’s process args : prepends
/nono/nono wrap — — profile <profile> — —to whatever the container was going to exec. The nono binary applies Landlock rules, then execs into the original process. PID 1 is sandboxed before it runs a single instruction. - Bind-mounts the nono binary — No additions to the container image required
- Injects a seccomp policy: writes a syscall allowlist directly into the OCI spec via
SetLinuxSeccompPolicy. The kata-agent enforces it inside the VM.
Pods that don’t use the opted-in RuntimeClass are skipped. The plugin logs the decision and moves on.
Opt-in via RuntimeClass
A pod opts in by specifying a RuntimeClass:
apiVersion: v1
kind: Pod
spec:
runtimeClassName: kata-nono-sandbox
containers:
- name: agent
image: my-ai-agent:latestThat’s it. The plugin sees the kata-nono-sandbox RuntimeClass, injects nono, and applies the seccomp profile. A pod using the generic kata-qemu RuntimeClass gets Kata’s VM isolation but no nono injection. An unlabelled pod gets neither.
For per-workload tuning, an annotation overrides the default Landlock profile:
metadata:
annotations:
nono.sh/profile: "strict"Two RuntimeClasses, two trust levels
The cluster runs two Kata RuntimeClasses:
The NRI plugin only watches the kata-nono-sandbox RuntimeClass, allowing you to run the same cluster for both regular Kata workloads and sandboxed AI workloads without any issues.
What the seccomp filter actually blocks?
The restricted profile starts from Docker’s RuntimeDefault allowlist and removes syscalls that are particularly dangerous for AI generated workloads:
Here’s what a live test looks like: the same container running with generic kata-qemu vs. kata-nono-sandbox:
── kata-qemu (no seccomp) ─────────────────────────────
ALLOWED seccomp(SET_MODE_FILTER) handler ran → filter reachable
ALLOWED ptrace(PTRACE_PEEKDATA) handler ran → process inspection reachable
ALLOWED io_uring_setup(entries=1) io_uring instance created fd=3
── kata-nono-sandbox (restricted) ────────────────────────
BLOCKED seccomp(SET_MODE_FILTER) cannot weaken/replace the active filter
BLOCKED ptrace(PTRACE_PEEKDATA) cannot inspect init process memory
BLOCKED io_uring_setup(entries=1) CVE-2022-2639 / CVE-2023-2598 vector unreachable
BLOCKED process_vm_readv(pid=1) cannot read init process address space
BLOCKED bpf(BPF_PROG_LOAD) cannot load eBPF programs
BLOCKED userfaultfd kernel exploit temporal primitive unavailableThe seccomp filter is enforced by the kata-agent inside the VM.
The stack at runtime
Four layers before the workload touches anything interesting:
- QEMU process seccomp: restricts what QEMU itself can do on the worker node
- VM isolation: hardware boundary between guest and worker node’s kernel
- Guest seccomp: OCI policy enforced by kata-agent before PID 1 starts
- Landlock LSM: path-level filesystem access control applied by nono, inherited by all child processes
What it doesn’t do?
Kubefence doesn’t replace network policy, RBAC, or admission control. It doesn’t prevent a workload from making outbound network connections (nono’s default profile allows outbound). It doesn’t audit what files were accessed ; it prevents access based on policies.
The Landlock profile controls which paths the workload can reach. The default profile is permissive enough for most AI workloads. If you need tighter filesystem control, you can use custom profiles and select them per pod with the annotation.
What you need to run it?
- Kubernetes with containerd 1.7+ (NRI must be enabled in containerd config)
- Linux 5.13+ on the nodes (Landlock LSM requirement)
- Kata Containers 3.28+ with a kernel built with CONFIG_SECURITY_LANDLOCK=y
The default Kata kernel ships with Landlock disabled. kubefence publishes a pre-built kernel image (ghcr.io/kubefence/kata-kernel-landlock) so you don’t have to compile it yourself. A GitHub Actions workflow rebuilds it weekly against Kata releases.
Deployment is a single helm install:
helm install kubefence oci://ghcr.io/kubefence/charts/kubefence \
--namespace kube-system \
--set kata.enabled=true \
--set config.seccompProfile=restrictedConclusion
In practice, Kubefence builds on well-understood primitives to turn Kata Containers into a multi-layer sandbox for untrusted AI workloads. The Kata VM provides the isolation boundary; the kata-agent governs process-creation paths (including blocking out-of-band exec); and the combination of seccomp and the Landlock LSM constrains what a process can do — both in terms of syscalls and resource access — from its very first instruction.
None of these mechanisms is new, and it’s entirely possible to assemble them yourself. What Kubefence offers is an opinionated, composable model that wires these layers together at the right point in the lifecycle — at process birth, and via the runtime — without requiring changes to container images or developer workflows. The result is a setup that is not only robust in isolation but also practical to deploy and operate consistently across a cluster.
The project is available at https://kubefence.github.io/kubefence/. If this approach aligns with your threat model, please give it a try — and contributions or issue reports are very welcome.
