← All news
security4 min read

Overprivileged Kubernetes pods and service accounts: the blast radius of AI-generated configs

By Seaworthy · July 1, 2026

Server rack hardware in a dark data center room

Photo by Tyler on Unsplash

The problem with "it deploys" as a success metric

When you ask an AI coding tool to generate a Kubernetes deployment manifest, you usually get something that works. The pod starts. The demo looks fine.

But "it deploys" is not the same as "it's safe." AI models learn from the enormous body of Kubernetes configs posted on GitHub, StackOverflow, and in documentation examples. Those examples often include elevated permissions because the authors were trying to get something running quickly, not writing production-grade security configs. The AI faithfully reproduces those patterns.

The result is that AI-generated deployments often run with automountServiceAccountToken: true (the default), broad RBAC bindings, and occasionally full cluster-admin privileges. Each of those is a door left open.

What a dangerously permissive pod looks like

Here is a typical AI-generated manifest for a simple web service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          securityContext:
            privileged: true
          env:
            - name: DB_PASSWORD
              value: "supersecret"
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-app-admin
subjects:
  - kind: ServiceAccount
    name: my-app-sa
    namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin

Several problems here, each worse than the last. privileged: true gives the container root-level access to the host kernel. The service account is bound to cluster-admin, which means any code running in that pod can do anything to any resource in the cluster. And the database password is in plaintext in the env block, which will appear in kubectl describe pod output for anyone with read access.

The blast radius when something goes wrong

Here is the thing about blast radius: it is not hypothetical. It is just a measure of how badly you lose when an attacker gets a foothold.

If a containerised app gets compromised through an RCE in a dependency or a supply chain attack, the attacker's next move is to query the Kubernetes API using the service account token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token. With cluster-admin, that query succeeds for everything. They can enumerate secrets, spawn new pods, and pivot freely across namespaces. The privilege escalation is instantaneous.

Running as privileged makes it worse. From a privileged container an attacker can mount host filesystems, access host network interfaces, load kernel modules, and break out of the container entirely. At that point you are not dealing with a compromised pod. You are dealing with a compromised node.

This is the blast radius: one exploited dependency or one misconfigured route, and you hand over the entire cluster.

A safer alternative

The same deployment, written with least-privilege in mind:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      automountServiceAccountToken: false
      containers:
        - name: my-app
          image: my-app:latest
          securityContext:
            allowPrivilegeEscalation: false
            runAsNonRoot: true
            runAsUser: 1000
            capabilities:
              drop:
                - ALL
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: my-app-secrets
                  key: db-password
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: my-app-role
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-app-rolebinding
  namespace: default
subjects:
  - kind: ServiceAccount
    name: my-app-sa
    namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: my-app-role

The changes are not dramatic but the difference in blast radius is real. No privilege escalation, no capabilities, a non-root user. Service account token mounting disabled at both the pod and service account level (you need both; setting it on one and not the other is a common mistake). A namespaced Role instead of a ClusterRoleBinding with cluster-admin. Secrets referenced from a Secret object rather than hardcoded.

If this pod is compromised, the attacker has a shell in a container with no capabilities, no cluster API access, and no path to escalation. That is a breach. Still bad. But it is not the end of the game.

Service accounts deserve more attention

The service account is the most overlooked part of this. Most developers, when they deploy to Kubernetes for the first time, do not think about service accounts at all. They use the default one. They let Kubernetes automount the token. The token is scoped to whatever RBAC exists on the default service account, which varies wildly by cluster setup.

If your app does not need to talk to the Kubernetes API, disable the token mount. If it does need API access, create a dedicated service account and bind it to the minimum set of verbs on the minimum set of resources it actually uses. Every privilege you don't grant is an attack surface that doesn't exist.

AI-generated configs will not do this for you. The training data does not reward security. It rewards configs that work.

The audit you probably haven't done

If your deployment configs were AI-generated, you probably have not audited them for privilege escalation paths. Most teams don't. The configs pass CI, the pods run, and everyone moves on.

Go check. Look for privileged: true, cluster-admin bindings, automountServiceAccountToken not explicitly set to false, and secrets in plain env vars. These are not edge cases in AI-generated output. They are the defaults you get when you ask a model to generate a working config without telling it to be paranoid.

Seaworthy is a static analysis tool that scans your repo for exactly these issues, including overprivileged Kubernetes configs, alongside checks across security, dependency hygiene, and configuration.

This article was generated by AI and summarises publicly available sources.

From the makers of Seaworthy

Seaworthy scans your repo for the issues covered in articles like this one.

Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.

$ npx seaworthycode