Secure Kubernetes Sandbox Access for AI Agents
Give AI agents a secure sandbox to access Kubernetes. No cluster credential or network access required.
AI agents are more useful when they can work with your Kubernetes clusters directly: reading logs, checking deployments, and restarting pods. The typical way to give them that access is a kubeconfig on the agent’s host with a long-lived token or client certificate, plus network access to the API server.
A standing credential on a VM is a risk before any agent is involved. More people usually have access to a VM through cloud IAM than you’d expect, and anyone who can reach the VM can use the credential or copy it somewhere else. Agents add a second problem. They’re persistent about finishing a task, so they’ll find credentials left in places that look harmless, or reach the cluster through network access or a cloud CLI. That can lead to actions against critical infrastructure that nobody intended, and prompt injection makes it easier to trigger.
You can sandbox the agent with Twingate instead. The agent holds no cluster credential and has no network access to the cluster. Its only path to the API server is through the Twingate Gateway. Authorization happens outside the agent’s environment, so the controls hold even when the agent misbehaves. With Privileged Access for Kubernetes:
- The agent holds no cluster credential. Its host runs a Twingate Headless Client with a Service Key, and the Twingate Gateway injects a pre-configured cluster identity associated with the key’s Service Account.
- The API server is private. The agent reaches it only through Twingate, and only if its Service Account has been granted access to the Kubernetes Resource in Twingate.
- Kubernetes RBAC ensures each agent can only take the actions you allow. The agent can’t change the identity the Gateway injects, and it can’t grant itself more permissions.
- The Gateway logs each request and records
kubectl execsessions on your infrastructure, outside the agent’s reach. - Revoking a Service Key, or removing a Service Account’s access to the Kubernetes Resource, cuts the agent off from the cluster at the network level.
This guide sets up the infrastructure needed for two agents at different access levels. One agent can only read. The other can also delete pods, scale deployments, and run kubectl exec.
For the Gateway architecture and general setup, see Privileged Access for Kubernetes.
Prerequisites
- A Kubernetes Identity Firewall deployment with the Kubernetes Operator, Gateway, at least one Connector, and a Kubernetes Resource.
- A Kubernetes API server with no public endpoint. The Connector should be the only network path to it from the agent hosts.
- Connector version 1.82.0 or later.
- Twingate Client version 2025.175 or later, installed in headless mode on each agent host.
- Admin access to the cluster to create RBAC objects.
kubectl1.26 or later on each agent host.
Isolate the agent host
The Gateway only controls requests that go through Twingate. It can’t close other paths from the agent host to the cluster. Before you give an agent access, check that its host:
- Can’t reach the API server over another route, such as a shared VPC, VPC peering, or a VPN.
- Has no cloud identity with permissions on the cluster. An instance role or cloud service account attached to the VM lets a cloud CLI fetch cluster credentials or change the cluster’s endpoint settings without going through the Gateway.
- Has no leftover kubeconfig files or cloud CLI credentials.
The exact network and IAM configuration depends on your cloud provider.
How it works
When an agent runs a kubectl command, the request is forwarded through the Connector to the Gateway. The Gateway then injects the associated Service Account details necessary for Kubernetes RBAC to authorize the request. The following table shows what is included as the identity:
| Key | Value |
|---|---|
| Username | Service Key ID |
| Group | Service Account name, for example agent-readonly |
| Service Account ID | |
twingate:service-accounts | |
twingate:authenticated |
Kubernetes also adds the system:authenticated group to every authenticated request, so it appears alongside these groups in kubectl auth whoami.
Kubernetes RBAC then authorizes the request against that identity based on Group membership. The agent is not able to modify its injected identity or escalate its permissions because the Gateway injects the identity, outside of the agent’s control.
RBAC should be bound to the Service Account rather than the Service Key to support stable and consistent access control, even when Service Keys rotate. The binding matches on the Service Account name, so renaming a Service Account changes which binding applies. Update the RBAC subjects whenever you rename one. Any existing binding that uses the same group name also applies to the agent, so choose Service Account names that aren’t already used as RBAC subjects in the cluster.
Neither ClusterRole in this guide grants an rbac.authorization.k8s.io verb like bind or escalate. Without one of those verbs, Kubernetes blocks a principal from creating or editing a binding to permissions it doesn’t already hold. Make sure not to grant these verbs. This is the key to preventing agents from escalating their privileges.
Create the Service Accounts
Create one Service Account per access level. This guide uses agent-readonly for the read-only agent and agent-platform for the agent that can modify workloads, matching the Group subjects in the RBAC bindings later in this guide. You can create these with Terraform, as shown below.
locals { agents = { readonly = "agent-readonly" platform = "agent-platform" }}
resource "twingate_service_account" "agent" { for_each = local.agents
name = each.value}
resource "twingate_service_account_key" "agent" { for_each = local.agents
service_account_id = twingate_service_account.agent[each.key].id name = "${each.value} Headless Client"}
output "agent_service_account_keys" { value = { for k, v in twingate_service_account_key.agent : k => v.token } sensitive = true}Each key’s token attribute is the Service Key JSON document. Save it to a file and pass it to twingate setup --headless on the matching agent host.
A Service Key grants network access through Twingate, not access to the cluster. This is what allows the cluster to be completely isolated from the internet. Secure the key with a secrets manager, restrictive file permissions, and a rotation policy.
You can also create Service Accounts and keys from the Admin Console. See How Service Accounts Work.
Grant the Service Accounts access to the cluster
The Operator grants access with a TwingateResourceAccess custom resource. Create one per Service Account, referencing the Kubernetes TwingateResource that the Gateway chart created and the Service Account’s ID:
apiVersion: twingate.com/v1betakind: TwingateResourceAccessmetadata: name: agent-readonly-access namespace: <operator-namespace>spec: resourceRef: name: <kubernetes-cluster-twingateresource-name> principalId: "<agent-readonly-service-account-id>"Create a second CR for agent-platform, pointing at the same resourceRef and using its own Service Account ID. Use principalId rather than a name-based reference: the ID stays valid if you rename the Service Account later, where a name-based reference would have to resolve again on every reconcile.
Configure Kubernetes RBAC
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: agent-readerrules: - apiGroups: [""] resources: ["pods", "pods/log", "services", "namespaces"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: agent-operatorrules: - apiGroups: [""] resources: ["pods", "pods/log", "services"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["pods"] verbs: ["delete"] - apiGroups: ["apps"] resources: ["deployments/scale"] verbs: ["get", "patch", "update"] - apiGroups: [""] resources: ["pods/exec"] verbs: ["create", "get"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: agent-readonly-bindingsubjects: - kind: Group name: agent-readonly apiGroup: rbac.authorization.k8s.ioroleRef: kind: ClusterRole name: agent-reader apiGroup: rbac.authorization.k8s.io---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: agent-platform-binding namespace: <workload-namespace>subjects: - kind: Group name: agent-platform apiGroup: rbac.authorization.k8s.ioroleRef: kind: ClusterRole name: agent-operator apiGroup: rbac.authorization.k8s.ioThe read-only agent is bound cluster-wide with a ClusterRoleBinding. The elevated agent is bound with a RoleBinding, so its permissions apply only in <workload-namespace>. Create one RoleBinding per namespace the agent needs to operate in.
Configure the agent hosts
On each agent host, install and configure the Twingate Client in headless mode with that host’s Service Key:
sudo twingate setup --headless /path/to/service_key.jsonsudo twingate starttwingate statusThen, as the agent’s own user, sync the kubeconfig and select the context:
mkdir -p ~/.kubetwingate kube config synckubectl config get-contextskubectl config use-context <context>twingate kube config sync writes a context for the Kubernetes Resource but doesn’t set it as current, so kubectl falls back to localhost:8080 until you select the context explicitly. The synced kubeconfig has no embedded credential: its token field is the literal placeholder void, because the cluster authenticates the connection through Twingate rather than through anything in the file.
Verify access
Confirm the identity Twingate asserts on each agent host
kubectl auth whoamiThe Groups list includes agent-readonly on the read-only agent and agent-platform on the elevated agent.
Read-only agent
Run the following commands on the agent host to verify the read-only agent’s access.
Read pods in the workload namespace
kubectl get pods -n <workload-namespace>Succeeds and lists the pods in the namespace.
Try to delete a pod
kubectl delete pod <pod> -n <workload-namespace> --dry-run=serverFails with a Forbidden error.
List the agent’s permissions
kubectl auth can-i --listLists the permissions granted by agent-reader.
Elevated agent
Run the following commands on the agent host to verify the elevated agent’s access.
Delete a pod in the workload namespace
kubectl delete pod <pod> -n <workload-namespace> --dry-run=serverSucceeds. The --dry-run=server flag checks the request without deleting the pod.
Try to delete a pod outside the workload namespace
kubectl delete pod <pod> -n <operator-namespace> --dry-run=serverFails with a Forbidden error, because the elevated agent has no RoleBinding in that namespace.
Try to grant the agent cluster-admin
kubectl create clusterrolebinding self-admin --clusterrole=cluster-admin --group=agent-platform --dry-run=serverFails with a Forbidden error. The agent can’t grant itself more permissions.
Check whether the agent can create bindings
kubectl auth can-i create clusterrolebindingsReturns no.
The Gateway logs every API call from both agents to stdout on your own infrastructure, attributed to the Service Key ID. It also records kubectl exec sessions as recordings you can replay. See Privileged Access for Kubernetes for how audit logs and session recording work.
Revoke agent access
Twingate offers two ways to cut an agent off, with different scopes. Both act before RBAC is evaluated, so they apply regardless of what the agent’s bindings allow, and neither affects other Service Accounts.
- Revoke the Service Key when one key is exposed or one agent host is compromised. Only Clients using that key lose access, to every Resource the Service Account can reach. The Service Account, its Resource access, and its RBAC binding stay in place, so you restore the agent by issuing a new key. A revoked key can’t be reactivated.
- Remove the Service Account’s access to the Kubernetes Resource to stop that access level from reaching the cluster at all, for example during an incident or when retiring an agent. Every key on the Service Account loses access to the cluster, and access to any other Resources is unchanged.
For related setup steps, see:
Last updated