Usage¶
Opting pods in¶
Sandboxing is opt-in via Kubernetes RuntimeClass. Set runtimeClassName on
the pod spec to activate kubefence for that pod.
Kata Containers (recommended):
apiVersion: v1
kind: Pod
metadata:
name: my-agent
spec:
runtimeClassName: kata-nono-sandbox
containers:
- name: agent
image: myimage:latest
command: ["myapp", "--flag"]
runc:
apiVersion: v1
kind: Pod
metadata:
name: my-agent
spec:
runtimeClassName: nono-runc
containers:
- name: agent
image: myimage:latest
command: ["myapp", "--flag"]
Pods without runtimeClassName, or pods whose RuntimeClass handler does not
match the plugin's configured runtime_classes, are completely unaffected.
The plugin logs a "skip" decision for them and returns immediately with no
adjustment.
nono profiles¶
nono profiles define the Landlock filesystem policy applied to the container
process. Specify a profile via the nono.sh/profile annotation:
The annotation value is validated against the regex ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$.
Invalid values are silently ignored and the pod falls back to the default_profile
configured in the Helm values or TOML config.
Verified profiles¶
The following profiles are verified to work with kubefence's nono wrap
injection (tested against nono v0.23.0):
| Profile | Notes |
|---|---|
default |
Base system profile. Safe default for most workloads |
claude-code |
Claude Code agent profile |
codex |
OpenAI Codex agent profile |
opencode |
Open-source code agent profile |
swival |
Python/Node.js development agent profile |
Incompatible profiles¶
Some nono profiles enable proxy network mode and require nono run instead of
nono wrap. These cannot be used with kubefence:
| Profile | Failure reason | Workaround |
|---|---|---|
python-dev |
nono wrap does not support proxy mode |
Requires nono run |
node-dev |
Same | Requires nono run |
go-dev |
Same | Requires nono run |
rust-dev |
Same | Requires nono run |
Profile availability varies by nono version. Re-verify profiles after upgrading the nono binary.
What happens at runtime¶
From the operator's perspective, the injection is transparent. The pod starts normally and its original command runs as expected.
Internally:
- The NRI plugin intercepts the
CreateContainerevent before the container starts - It prepends
/nono/nono wrap --profile <profile> --to the container's args - It bind-mounts the nono binary directory from the host at
/nonoinside the container - It sets
NONO_PROFILE=<profile>and prepends/nonoto the container'sPATH - The container starts; nono is PID 1
- nono applies the Landlock filesystem policy, then
exec()s into the original command - The original command becomes PID 1; Landlock restrictions are inherited by all child processes
The exec() means nono does not remain in the process tree — /proc/1/cmdline
shows the original application command, not nono.
The /nono prefix on PATH enables nono wrapper scripts to intercept child
process execs (e.g. sh, bash, python3) that are spawned without a full
path. This covers kubectl exec sessions and subprocesses.
Verification¶
After deploying a sandboxed pod, verify injection is working:
# Apply a test pod
kubectl apply -f deploy/test-pod.yaml
kubectl wait --for=condition=ready pod/nono-test --timeout=60s
# nono exec()s into sleep — /proc/1/cmdline shows the original command
kubectl exec nono-test -- cat /proc/1/cmdline | tr '\0' ' '
# Expected: sleep infinity
# nono binary is bind-mounted into the container
kubectl exec nono-test -- ls -la /nono/nono
# Expected: -rwxr-xr-x 1 root root ... /nono/nono
# Check plugin decision logs for this pod
kubectl logs -n kube-system -l app.kubernetes.io/name=kubefence | grep nono-test
# Expected: {"msg":"injected","decision":"inject","pod":"nono-test","profile":"default",...}
# Cleanup
kubectl delete pod nono-test
Tip
For Kata pods, kubectl exec is blocked by the kata-agent OPA policy.
The verification commands above will fail with a permission error — this
is the expected, correct behavior for Kata-sandboxed pods.