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 exec sessions 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.
  • kubectl 1.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

Agentic Access Architecture
Agentic Access Architecture

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:

KeyValue
UsernameService Key ID
GroupService 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.

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.

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/v1beta
kind: TwingateResourceAccess
metadata:
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/v1
kind: ClusterRole
metadata:
name: agent-reader
rules:
- 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/v1
kind: ClusterRole
metadata:
name: agent-operator
rules:
- 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/v1
kind: ClusterRoleBinding
metadata:
name: agent-readonly-binding
subjects:
- kind: Group
name: agent-readonly
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: agent-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: agent-platform-binding
namespace: <workload-namespace>
subjects:
- kind: Group
name: agent-platform
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: agent-operator
apiGroup: rbac.authorization.k8s.io

The 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.json
sudo twingate start
twingate status

Then, as the agent’s own user, sync the kubeconfig and select the context:

mkdir -p ~/.kube
twingate kube config sync
kubectl config get-contexts
kubectl 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 whoami

The 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=server

Fails with a Forbidden error.

List the agent’s permissions

kubectl auth can-i --list

Lists 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=server

Succeeds. 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=server

Fails 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=server

Fails with a Forbidden error. The agent can’t grant itself more permissions.

Check whether the agent can create bindings

kubectl auth can-i create clusterrolebindings

Returns no.

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:

→ Kubernetes Kubeconfig Sync

→ How Service Accounts Work

→ Linux Headless Client

Last updated