← mohdarif.com
Kubernetes Security · OWASP Top 10

OWASP Kubernetes Top 10 (2025): A Practical Guide

What each risk means, a command to check your own cluster for it, and how to fix it.

The OWASP Kubernetes Top 10 lists the most common ways clusters get compromised. This post goes through the 2025 edition one risk at a time. Each one has the same layout: what it is, how to check for it, and how to fix it. Where a risk is covered in depth in my Kubernetes series, I link to it.

K01Insecure workloadconfigurationWorkloadsK02Over-permissiveauthorizationAccessK03SecretsmismanagementWorkloadsK04No cluster-levelpolicyPolicyK05No networksegmentationNetworkK06Overly exposedcomponentsNetworkK07Misconfigured orvulnerable partsPlatformK08Cluster-to-cloudlateral movementCloudK09BrokenauthenticationAccessK10Weak logging andmonitoringVisibility
Figure 1. The 2025 list. Most risks are about who can do what (access), what runs (workloads), and how far an attacker can go from one foothold (network, cloud).
IDRiskOne-line fix
K01Insecure workload configurationsNon-root, no privileged, drop capabilities
K02Overly permissive authorizationLeast-privilege RBAC
K03Secrets management failuresEncrypt, restrict, use a secrets manager
K04Lack of cluster-level policy enforcementAdmission policies, Pod Security Admission
K05Missing network segmentationDefault-deny NetworkPolicies
K06Overly exposed componentsKeep API server, kubelet, etcd private
K07Misconfigured and vulnerable componentsPatch, and run kube-bench
K08Cluster to cloud lateral movementWorkload identity, block metadata service
K09Broken authenticationSSO with MFA, short-lived tokens
K10Inadequate logging and monitoringAudit logs, central logging, runtime alerts

K01: Insecure workload configurations

Pods run with more power than they need: as root, privileged, with host access, or with a writable filesystem. If an attacker gets code execution in such a Pod, escaping to the node is easy. This is the most common finding in real clusters.

Check

# Pods running privileged
kubectl get pods -A -o json | jq -r '.items[]
  | select(.spec.containers[].securityContext.privileged==true)
  | .metadata.namespace + "/" + .metadata.name'

# Scan manifests for risky settings
kube-linter lint ./k8s/
kubesec scan deployment.yaml

Fix

securityContext:
  runAsNonRoot: true
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop: ["ALL"]
  seccompProfile:
    type: RuntimeDefault

K02: Overly permissive authorization

RBAC rules that give users or ServiceAccounts more than they need: cluster-admin everywhere, wildcards (*), or powerful verbs. A stolen ServiceAccount token is only as dangerous as its role.

These permissions are almost as good as admin: create pods (can start a privileged Pod), get or list secrets, pods/exec, escalate, bind, impersonate.

Check

# Who is bound to cluster-admin?
kubectl get clusterrolebindings -o json | jq -r '.items[]
  | select(.roleRef.name=="cluster-admin")
  | .metadata.name + " -> " + ([.subjects[]?.name] | join(","))'

# What can this ServiceAccount do?
kubectl auth can-i --list --as=system:serviceaccount:prod:web -n prod

# Who can read secrets? (krew plugin)
kubectl who-can get secrets -n prod

Fix

K03: Secrets management failures

Kubernetes Secrets are only base64-encoded, not encrypted. By default they sit in etcd in readable form, and anyone with get secrets permission can read them. Secrets also leak through Git repositories, environment variables printed in logs, and container images.

Check

# Long-lived token Secrets (should be rare)
kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token

# Secrets committed to your repo
gitleaks detect --source .

Fix

K04: Lack of cluster-level policy enforcement

Good practices that are only written in a document do not stop anyone. Without a policy that the cluster enforces, one team's mistake (a privileged Pod, an image from a public registry) goes straight through.

Check

# Is Pod Security Admission configured on your namespaces?
kubectl get ns --show-labels | grep pod-security

# Is a policy engine installed?
kubectl get pods -A | grep -Ei "kyverno|gatekeeper"

Fix

# Built in: enforce the "restricted" Pod Security Standard
kubectl label ns prod \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

K05: Missing network segmentation

By default, every Pod can talk to every other Pod, and out to the internet. One compromised Pod can then scan and attack everything else.

Check

# Namespaces with no NetworkPolicy at all
kubectl get networkpolicy -A
# Compare with: kubectl get ns

Fix

# Deny all traffic in the namespace, then allow only what is needed
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: prod
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]

K06: Overly exposed Kubernetes components

Control plane and node components reachable from places they should not be: an API server open to the internet, a kubelet that accepts anonymous requests, an etcd without authentication, an exposed dashboard, or an internal service published with a LoadBalancer or NodePort.

Check

# Services exposed outside the cluster
kubectl get svc -A | grep -E "LoadBalancer|NodePort"

# From outside your network (only on clusters you own):
nmap -p 6443,10250,2379 <node-or-api-ip>

Fix

K07: Misconfigured and vulnerable cluster components

Old Kubernetes versions, unpatched ingress controllers, container runtimes with known escapes, and insecure settings on the components themselves. Real examples include the runc escape CVE-2024-21626 and the ingress-nginx flaws known as IngressNightmare (CVE-2025-1974).

Check

# Versions of the cluster and nodes
kubectl version
kubectl get nodes -o wide

# Compare against the CIS Kubernetes Benchmark
kube-bench run

Fix

K08: Cluster to cloud lateral movement

A compromised Pod reaches the cloud account behind the cluster. The usual path: the Pod calls the node's metadata service (169.254.169.254) and gets the node's cloud credentials. If the node role is broad, the attacker now has access to storage, databases and other cloud services, well beyond the cluster.

Check

# From inside a test Pod: can it reach the metadata service?
kubectl run test --rm -it --image=curlimages/curl --restart=Never -- \
  curl -s -m 3 http://169.254.169.254/latest/meta-data/

If that returns data, any compromised Pod could do the same.

Fix

K09: Broken authentication mechanisms

Weak ways of proving who you are: shared kubeconfig files, static tokens, long-lived ServiceAccount token Secrets, client certificates (Kubernetes cannot revoke them), or anonymous access left on.

Check

# Can an unauthenticated user do anything?
kubectl auth can-i --list --as=system:anonymous
kubectl auth can-i --list --as=system:unauthenticated

# Legacy long-lived ServiceAccount tokens
kubectl get secrets -A --field-selector type=kubernetes.io/service-account-token

Fix

K10: Inadequate logging and monitoring

If you cannot see what happened, you cannot detect an attack or investigate one. Many Kubernetes breaches are found by someone outside the company, weeks later.

Check

Fix

A 30-minute audit

Run these on a cluster you own. They map to the risks above and show where you stand:

  1. kube-bench run for cluster settings (K07).
  2. The cluster-admin and can-i commands from K02, and kubectl who-can get secrets (K02, K03).
  3. kube-linter lint and trivy config on your manifests (K01).
  4. kubectl get networkpolicy -A and kubectl get ns --show-labels (K04, K05).
  5. kubectl get svc -A filtered for LoadBalancer and NodePort (K06).
  6. The metadata service test from K08.
  7. Ask: are audit logs on, and who gets the alerts? (K10)

What to fix first

  1. K06 and K09. Anything exposed to the internet, and weak ways to log in. These are the easiest for an attacker to find.
  2. K02 and K03. Over-broad RBAC and unprotected Secrets. They decide how bad a single compromise gets.
  3. K01, K04 and K05. Lock down workloads, enforce it with policy, and segment the network, so one Pod cannot reach everything.
  4. K08. Cut the path from the cluster to your cloud account.
  5. K07 and K10. Keep patching, and make sure you can see what is happening.

References