How to sandbox AI agents on Kubernetes

To sandbox AI agents on Kubernetes, run each agent in its own Pod with a hardened security context, deny its egress with a NetworkPolicy, and give it a RuntimeClass that adds a stronger isolation boundary than a standard container. Agent Sandbox provides a Sandbox resource that manages that Pod’s lifecycle.
Checked against the Agent Sandbox README and threat model, and the Kubernetes docs for Pod security, Pod Security admission, NetworkPolicy and RuntimeClass, as fetched on 2026-11-05. Pin the Agent Sandbox release in the install step below to the one you have tested.
What a sandbox has to stop
An agent sandbox runs code nobody has reviewed, often code the model wrote a moment earlier. The Agent Sandbox threat model names the risks that matter for Sandbox Pods: a container escape to the node, a compromised Pod reaching other tenants over the network, a service account token used against the Kubernetes API, and a Pod that uses up the node’s CPU, memory or storage. Each risk needs a control at a different layer. This guide is part of our AI agent governance work.
| Risk | Control | Where you set it |
|---|---|---|
| Container escape to the node | A secure runtime such as gVisor or Kata Containers | runtimeClassName on the Pod, from a RuntimeClass |
| Privilege escalation inside the container | Non-root user, no privilege escalation, all capabilities dropped | securityContext |
| Cross-tenant network attack | Default-deny egress | NetworkPolicy |
| Kubernetes API abuse | No service account token mounted | automountServiceAccountToken: false |
| Resource exhaustion | CPU and memory requests and limits | resources on each container |
| Unauthorized sandbox creation | RBAC on the Sandbox resources | Role and RoleBinding |
For a per-request policy layer on top of Agent Sandbox, read our OpenShell on Kubernetes guide.
Create the sandbox with Agent Sandbox
Agent Sandbox is a SIG Apps project. It adds a Sandbox resource for a single stateful Pod with a stable identity and persistent storage. It does not provide the isolation itself, so the rest of this guide sets that up.
- Check the cluster version. Agent Sandbox needs Kubernetes 1.25 or later. The README recommends 1.29 or later, because CRD CEL validation is stable there.
- Install a pinned release. The README pins
v1.0.2in its production example, and recommends pinning for GitOps. - Confirm the CRD and the controller are present.
- Create a namespace for agents, so the policies below apply only to them.
export VERSION="v1.0.2"
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/sandbox-with-extensions.yaml
kubectl get crd sandboxes.agents.x-k8s.io
kubectl get deploy agent-sandbox-controller -n agent-sandbox-system
kubectl create namespace agents
Harden the Pod security context
A Sandbox takes a full podTemplate, so the Pod settings go under spec.podTemplate.spec. Set the fields the Pod Security Standards restricted profile asks for. Restricted requires runAsNonRoot, allowPrivilegeEscalation set to false, a RuntimeDefault seccomp profile, and capabilities that drop ALL, with NET_BIND_SERVICE as the only permitted add-back.
Kubernetes defaults allowPrivilegeEscalation to true, so set it yourself. Setting it to false cannot be combined with a privileged container or with CAP_SYS_ADMIN, so those settings will conflict with this one.
apiVersion: agents.x-k8s.io/v1beta1
kind: Sandbox
metadata:
name: agent-hardened
namespace: agents
spec:
podTemplate:
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: agent
image: registry.k8s.io/e2e-test-images/agnhost:2.45
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
Replace the test image with your agent image. The resource values are examples to size for your workload. Apply the namespace label and the NetworkPolicy from the next two sections before you create this Sandbox, so the first Pod starts under both controls. The namespace field above puts the Pod in agents even when your kubectl context points elsewhere.
Pod Security admission works as a backstop. Its labels apply at the namespace level when Pods are created, so the restricted level covers the Pods the controller makes, even when a manifest leaves a field out.
apiVersion: v1
kind: Namespace
metadata:
name: agents
labels:
pod-security.kubernetes.io/enforce: restricted
Deny egress by default
A Pod is non-isolated for egress until a NetworkPolicy selects it and lists Egress in policyTypes. Policies are additive, so once a Pod is isolated, only the connections that some policy allows get through. The docs say new Pods are isolated before they start, but a Pod created before the network plugin has handled a new policy may start unprotected. Create the policy before the first sandbox.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: agents
spec:
podSelector: {}
policyTypes:
- Egress
This policy also blocks DNS. Add a second NetworkPolicy that allows egress to your cluster’s DNS service, then add one allow rule for each destination the agent needs. The rule types the docs list are podSelector, namespaceSelector and ipBlock, so write each destination as a Pod, a namespace or a CIDR range. The policy also works only on a cluster whose networking plugin enforces NetworkPolicy. For managed sandboxes, the threat model describes a NetworkPolicy mode in SandboxTemplate that limits egress to the public internet.
Choose a RuntimeClass for untrusted code
A RuntimeClass names a runtime configuration that the CRI on each node provides. The handler must match a configuration in containerd or CRI-O on those nodes. Create one RuntimeClass per handler, then reference it from the Pod.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: agent-isolated
handler: myconfiguration
Then set the runtime on the Sandbox’s Pod spec:
spec:
podTemplate:
spec:
runtimeClassName: agent-isolated
If the RuntimeClass does not exist, or the CRI cannot run its handler, the Pod enters the Failed phase. Scheduling fields keep the Pod on nodes that support the runtime: the RuntimeClass nodeSelector and tolerations are merged with the Pod’s own. The threat model says administrators can require it by setting runtimeClassName in a SandboxTemplate.
| Option | Isolation added | Trade-off | Use it for |
|---|---|---|---|
| Hardened container | Pod settings, NetworkPolicy and limits only | No runtime overhead | Agents whose code you have reviewed |
| gVisor or Kata Containers through a RuntimeClass | Kernel and network isolation between the sandbox and the host | Node runtime setup, plus the overhead the RuntimeClass overhead field accounts for |
Untrusted or LLM-generated code |
Limit who can create sandboxes
The threat model says only authorized users should create Sandbox, SandboxClaim and SandboxTemplate resources, so restrict them with RBAC. The CRDs are sandboxes.agents.x-k8s.io, plus sandboxclaims, sandboxwarmpools and sandboxtemplates under extensions.agents.x-k8s.io.
Before you uninstall, check for live resources. The README warns that deleting the CRDs cascade-deletes all custom resources of those types across all namespaces.
kubectl get sandboxes -A
kubectl get crd sandboxclaims.extensions.agents.x-k8s.io >/dev/null 2>&1 && kubectl get sandboxclaims -A
A SandboxTemplate defaults automountServiceAccountToken to false when you omit it. A bare Sandbox gets no such default, so the threat model suggests a ValidatingAdmissionPolicy to require the field.
Enforcing this with Kyverno
Admission control is the right place to check the settings above for every sandbox, whoever creates it. The Kyverno policy library has a ClusterPolicy that requires every container to drop ALL capabilities. It matches Pods on create and update, and its sample runs in Audit mode, so it reports violations without blocking them. Test it on a development cluster first.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: drop-all-capabilities
annotations:
policies.kyverno.io/title: Drop All Capabilities in CEL expressions
policies.kyverno.io/category: Best Practices in CEL
policies.kyverno.io/severity: medium
kyverno.io/kubernetes-version: "1.26-1.27"
policies.kyverno.io/minversion: "1.11.0"
policies.kyverno.io/subject: Pod
spec:
validationFailureAction: Audit
background: true
rules:
- name: require-drop-all
match:
any:
- resources:
kinds:
- Pod
operations:
- CREATE
- UPDATE
validate:
cel:
variables:
- name: allContainers
expression: object.spec.containers + object.spec.?initContainers.orValue([]) + object.spec.?ephemeralContainers.orValue([])
expressions:
- expression: variables.allContainers.all(container, container.?securityContext.?capabilities.?drop.orValue([]).exists(capability, capability.upperAscii() == 'ALL'))
message: Containers must drop `ALL` capabilities.
Admission runs when a Pod is created or updated, and it cannot see what the agent does at runtime, so plan a runtime control for that layer too. See Runtime OSS for the product.
Sources
- Agent Sandbox README (install steps, Sandbox kind, prerequisites)
- Agent Sandbox threat model (risks, controls and SandboxTemplate defaults)
- Kubernetes Pod Security Standards and Pod Security admission
- Kubernetes security context
- Kubernetes NetworkPolicy and RuntimeClass
Frequently asked questions
Is a hardened container enough to sandbox an AI agent on Kubernetes?
It is a baseline, not a full answer. A hardened container gets the Pod settings you give it and nothing more. The Agent Sandbox threat model recommends a secure runtime such as gVisor or Kata Containers, selected with a RuntimeClass, for untrusted code. Pair that with a default-deny NetworkPolicy and resource limits, and treat the security context as the minimum for any agent you have not reviewed.
Does Agent Sandbox isolate the agent by itself?
No. The Agent Sandbox threat model says the project does not implement isolation. It manages the Sandbox and its Pods, and it supports configuring secure runtimes through RuntimeClass. The isolation comes from the runtime, the Pod security context and the NetworkPolicy you apply to the namespace.
Why does a sandbox Pod fail after I add runtimeClassName?
A Pod that names a RuntimeClass that does not exist, or whose handler the CRI cannot run, enters the Failed phase, and an event carries the error. Check that the handler name matches a runtime configured in containerd or CRI-O on the nodes. If only some nodes support the runtime, add scheduling settings so the Pod lands on them.
Will a default-deny egress policy break DNS?
Yes, unless you add a rule for it. The Kubernetes docs warn that a default deny-all egress policy also blocks DNS traffic. Add a separate NetworkPolicy that allows egress to your cluster's DNS service, then allow only the destinations the agent needs. NetworkPolicy rules select Pods, namespaces or IP blocks.
Should I use RBAC or admission control to protect agent sandboxes?
Use both. RBAC decides who can create Sandbox, SandboxClaim and SandboxTemplate resources. Admission control checks what the object contains. A SandboxTemplate defaults automountServiceAccountToken to false, but a bare Sandbox does not, so the threat model suggests a ValidatingAdmissionPolicy for that field.