Skip to content
Hosting Operations12 min read

Kubernetes Security Hardening: 6 Controls [2026]

Harden your Kubernetes cluster with RBAC, network policies, and pod security standards. Six production-ready controls that stop lateral movement.

Written by Abdul AbrorTechnical Hosting Support Engineer
A security and privacy dashboard with its status.
On this page

TL;DR — Key takeaways

  • Enable Pod Security Standards at the namespace level to block privileged containers and host path mounts by default.
  • Restrict service account tokens with automountServiceAccountToken: false and create dedicated accounts per workload.
  • Deploy a default-deny NetworkPolicy in every namespace, then whitelist only required pod-to-pod and egress traffic.
  • Audit RBAC bindings quarterly to remove stale ClusterRoleBindings that grant cluster-admin or list secrets permissions.
  • Use admission controllers like ResourceQuota and LimitRanger to prevent resource exhaustion attacks on shared nodes.

Most container breakouts I've investigated started with an overly permissive service account or a missing network policy. Kubernetes ships with sane defaults for availability, not security.

The six controls below block the attack paths that matter: lateral movement between pods, privilege escalation from containers to nodes, and unauthorized access to secrets. Enable them now before the next CVE turns a compromised web app into a cluster-wide incident.

Control 1: Enforce Pod Security Standards at the Namespace Level

Pod Security Standards replaced the deprecated PodSecurityPolicy API in Kubernetes 1.25. They define three profiles—Privileged, Baseline, and Restricted—that you apply to namespaces using labels.

The Restricted profile is your production target. It blocks privileged containers, host network/PID/IPC namespaces, hostPath volumes, and all capabilities except NET_BIND_SERVICE. Apply it with:

Label each production namespace before deploying workloads. Pods that violate the policy won't start, and you'll see the violation in kubectl describe pod output. Use the warn and audit labels alongside enforce during rollout to catch violations without blocking deployments.

Most legacy apps fail on two checks: missing runAsNonRoot and requesting privileged mode. Fix the pod securityContext first. Baseline profile works as a stepping stone if you have vendor-provided Helm charts you can't modify immediately.

  • kubectl label namespace production pod-security.kubernetes.io/enforce=restricted
  • kubectl label namespace production pod-security.kubernetes.io/audit=restricted
  • kubectl label namespace production pod-security.kubernetes.io/warn=restricted

Control 2: Disable Automatic Service Account Token Mounting

By default, every pod gets a service account token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token. That token has whatever RBAC permissions the account holds, often more than the workload needs.

Disable it unless the pod actually calls the Kubernetes API. Add automountServiceAccountToken: false to the pod spec or service account definition. Web apps, batch jobs, and most stateless services don't need it.

When you do need API access—operators, controllers, CI pipelines—create a dedicated service account per workload with a scoped Role. Never use the default service account for anything that touches the API.

Check current token usage by execing into running pods and looking for the token file. If it exists but the app never reads it, you can safely disable mounting.

Control 3: Deploy Default-Deny NetworkPolicies

NetworkPolicies are namespace-scoped firewall rules that control pod-to-pod and pod-to-external traffic. Without them, every pod can reach every other pod and any external IP.

Start with a default-deny rule in each namespace. This example blocks all ingress and egress traffic:

Then add explicit allow policies for required traffic. Most apps need egress to kube-dns (UDP 53), ingress from the ingress controller namespace, and egress to specific database or API endpoints. Use podSelector and namespaceSelector labels to target traffic flows.

Test by kubectl execing into a pod and attempting connections that should be blocked. If they succeed, your CNI plugin might not support NetworkPolicies—Flannel doesn't by default, but Calico and Cilium do.

  • apiVersion: networking.k8s.io/v1
  • kind: NetworkPolicy
  • metadata:
  • name: default-deny-all
  • spec:
  • podSelector: {}
  • policyTypes:
  • - Ingress
  • - Egress

Control 4: Audit and Restrict RBAC Bindings

RBAC permissions accumulate over time as teams deploy workloads and troubleshoot incidents. Six months in, you'll have stale ClusterRoleBindings granting cluster-admin to service accounts nobody remembers creating.

Run this quarterly audit: kubectl get clusterrolebindings -o wide. Look for cluster-admin bindings outside of break-glass admin accounts. Check for wildcard resource or verb permissions (resources: ["*"], verbs: ["*"]).

Service accounts should use namespace-scoped Roles, not ClusterRoles. The default ClusterRole edit grants write access to most resources but not RBAC objects or secrets—use it as a template for workload accounts.

Watch for the list secrets permission. It's often overlooked but allows reading all secret data in a namespace. Grant get secrets on specific secret names instead.

Control 5: Enable Admission Controllers for Resource Limits

ResourceQuota and LimitRanger prevent noisy neighbor and resource exhaustion attacks. Without them, a single pod can consume all node CPU or memory, crashing other workloads on the same node.

ResourceQuota sets hard limits per namespace—total CPU, memory, persistent volume claims, and object counts. Create one for each tenant namespace.

LimitRanger sets default requests and limits for pods that don't specify them. This matters because Kubernetes won't schedule pods without resource requests, and unbounded pods can starve others during load spikes.

Check which admission controllers are active on your API server: kubectl describe pod kube-apiserver-* -n kube-system | grep admission. If ResourceQuota or LimitRanger are missing, add them to the --enable-admission-plugins flag and restart the API server. Managed Kubernetes services (GKE, EKS, AKS) enable them by default.

Control 6: Block Host Path Volumes and Privileged Containers

Host path mounts and privileged mode are the two most common container breakout vectors. Privileged containers run with CAP_SYS_ADMIN and can load kernel modules, access all devices, and bypass seccomp.

The Restricted Pod Security Standard blocks both by default. If you're not ready to enforce Restricted, add these fields to every pod securityContext manually:

For logging and monitoring agents that need host access, scope hostPath mounts to specific read-only directories (/var/log, /proc) and run the agent in a dedicated namespace with a Baseline policy. Never mount the Docker socket or /etc unless the pod is part of cluster infrastructure.

Audit existing workloads with kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[].securityContext.privileged == true) | {namespace: .metadata.namespace, name: .metadata.name}'. Fix or isolate any matches.

  • securityContext:
  • runAsNonRoot: true
  • runAsUser: 1000
  • allowPrivilegeEscalation: false
  • capabilities:
  • drop:
  • - ALL

Testing and Rollback Strategy

Apply these controls in a staging namespace first. Use the pod-security.kubernetes.io/audit and warn labels to surface violations without blocking workloads. Check events with kubectl get events -n staging --sort-by='.lastTimestamp'.

For NetworkPolicies, deploy allow rules before the default-deny rule if you're retrofitting an existing namespace. Monitor ingress controller logs for connection failures.

Keep a rollback plan: store all policy YAML in Git and tag commits before changes. If a policy breaks production, kubectl delete -f policy.yaml removes it immediately. For Pod Security Standards, change the namespace label back to Baseline or remove it.

Most rollbacks I've handled were due to missing egress rules for external APIs or forgotten init containers that needed specific capabilities. Check init container specs separately—they often have different requirements than app containers.

Quick Reference: Security Checklist by Priority

If you're enabling these controls for the first time, follow this sequence to minimize disruption:

  • Week 1: Audit RBAC bindings and remove cluster-admin grants. No restart required, low risk.
  • Week 2: Add automountServiceAccountToken: false to pod specs for workloads that don't call the Kubernetes API. Deploy via rolling update.
  • Week 3: Enable Pod Security Standards in audit mode on one production namespace. Review violations for two weeks.
  • Week 4: Fix pod specs based on audit logs, then switch to enforce mode.
  • Week 5: Deploy default-deny NetworkPolicies in a new namespace first, validate traffic flows, then roll out to existing namespaces.
  • Week 6: Enable ResourceQuota and LimitRanger on tenant namespaces. Set generous initial limits and tune down based on actual usage.

Summary Table: Six Essential Kubernetes Security Controls

Control | Purpose | Risk If Missing | Enforcement Method

--- | --- | --- | ---

Pod Security Standards (Restricted) | Block privileged containers and host mounts | Container breakout to node | Namespace label: pod-security.kubernetes.io/enforce=restricted

Disable automountServiceAccountToken | Reduce API token exposure | Lateral movement via stolen tokens | Pod spec: automountServiceAccountToken: false

Default-Deny NetworkPolicy | Isolate pod-to-pod traffic | Unrestricted lateral movement | NetworkPolicy with empty ingress/egress arrays

RBAC Least Privilege | Limit API access per workload | Privilege escalation to cluster-admin | Namespace-scoped Roles, no wildcard permissions

ResourceQuota + LimitRanger | Prevent resource exhaustion | Noisy neighbor, denial of service | Admission controllers: ResourceQuota, LimitRanger

Block hostPath and privileged mode | Prevent node access from containers | Host compromise, kernel exploits | Pod Security Standards or manual securityContext fields

Quick troubleshooting checklist

  • Run kubectl get clusterrolebindings -o wide and remove any cluster-admin bindings not tied to break-glass accounts
  • Label namespaces with pod-security.kubernetes.io/enforce=restricted for production workloads
  • Create a default-deny NetworkPolicy in each namespace before deploying applications
  • Set automountServiceAccountToken: false in all pod specs unless the workload requires API access
  • Enable audit logging with --audit-policy-file and retain logs for 90 days minimum
  • Configure securityContext.runAsNonRoot: true and drop all capabilities except NET_BIND_SERVICE if needed

FAQ

What are Pod Security Standards and which level should I use?

Pod Security Standards replace PodSecurityPolicy with three profiles: Privileged (unrestricted), Baseline (blocks known privilege escalations), and Restricted (hardened best practices). Use Restricted for production namespaces to block privileged containers, host namespaces, and host path mounts. Apply it with a namespace label: pod-security.kubernetes.io/enforce=restricted. Baseline works for legacy workloads that need specific Linux capabilities but should still block the most dangerous configurations.

How do I implement least-privilege RBAC without breaking existing workloads?

Start by auditing current permissions with kubectl get clusterrolebindings,rolebindings --all-namespaces. Identify service accounts using cluster-admin or wildcard resource permissions. Create custom Roles that grant only the verbs and resources each workload actually uses—most apps need get/list/watch on specific resources, not create or delete. Test in a staging namespace first, then apply namespace-scoped RoleBindings instead of cluster-wide ones. Remove the default service account token from pods that don't call the Kubernetes API by setting automountServiceAccountToken: false in the pod spec.

What's a safe default NetworkPolicy configuration?

Deploy a default-deny policy in every namespace that blocks all ingress and egress, then whitelist only required traffic. Example: create a policy with podSelector: {} and empty ingress/egress arrays. Then add specific policies allowing ingress from your ingress controller namespace and egress to kube-dns, external APIs, and databases. Label pods consistently so selectors work (app, tier, version labels). Test with kubectl exec into a pod and try curling blocked endpoints to confirm the policy is enforced. Many clusters silently ignore NetworkPolicies if no CNI plugin with network policy support is installed—verify your CNI (Calico, Cilium, Weave) supports it.

Should I disable automountServiceAccountToken cluster-wide?

Disable it per-pod, not cluster-wide. Set automountServiceAccountToken: false in pod specs for workloads that don't need Kubernetes API access—most web apps, cron jobs, and batch processors fall into this category. Workloads like operators, controllers, and CI/CD agents need API access and should use dedicated service accounts with minimal RBAC permissions. Disabling it cluster-wide via an admission webhook can break system components and requires careful exclusion rules for kube-system and monitoring namespaces.

How do I prevent container breakout through volume mounts?

Block hostPath volumes entirely using Pod Security Standards at the Restricted level. If you must allow hostPath for logging agents or node monitoring, scope it tightly with readOnly: true and specific paths like /var/log instead of /. Use projected volumes for secrets and config instead of mounting host directories. Avoid mounting the Docker socket (/var/run/docker.sock) into pods—this grants root-equivalent cluster access. For persistent storage, use CSI drivers with storage classes that enforce permissions and encryption rather than binding host directories.

What admission controllers should be enabled for security?

Enable NodeRestriction (limits what kubelets can modify), PodSecurity (enforces Pod Security Standards), ResourceQuota (prevents resource exhaustion), and LimitRanger (sets default CPU/memory limits). Check enabled controllers with kubectl describe pod kube-apiserver-<node> -n kube-system and look for --enable-admission-plugins. Also enable AlwaysPullImages if your cluster runs untrusted workloads—it forces image pulls even if the image is cached, preventing tag manipulation attacks. DenyEscalatingExec blocks kubectl exec into privileged pods.

How do I audit who has access to secrets?

Run kubectl get clusterrolebindings,rolebindings -A -o json | jq '.items[] | select(.roleRef.name | contains("admin") or contains("edit")) | {namespace: .metadata.namespace, name: .metadata.name, subjects: .subjects}' to find broad permissions. Look for rules that grant get, list, or watch on secrets resources. Enable audit logging with an audit policy that logs all secret access at the RequestResponse level. In production clusters, store sensitive data in an external secret manager (Vault, AWS Secrets Manager, GCP Secret Manager) and use the Secrets Store CSI driver to inject them as volumes—this centralizes audit logs and rotation.

What's the fastest way to check if my cluster is hardened?

Run kube-bench, an open-source tool that checks clusters against CIS Kubernetes Benchmark controls. It outputs a scored report of failed checks with remediation steps. Also run kubectl get psp,podsecuritypolicies (deprecated but may still be active), kubectl get networkpolicies -A (should see policies in every namespace), and kubectl auth can-i --list --as=system:serviceaccount:default:default (should return minimal permissions). Check if anonymous auth is disabled on the API server with curl -k https://<api-server>:6443/api —it should return 401 or 403, not a resource list.

How do I roll back if a security policy breaks production?

Test all policies in a non-production namespace first using kubectl label namespace staging pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/audit=restricted pod-security.kubernetes.io/warn=restricted. Monitor audit logs for violations before enforcing. For NetworkPolicies, deploy them in audit mode if your CNI supports it, or add explicit allow rules before applying a default-deny. Keep the previous policy YAML in version control. If a pod fails to start after applying Pod Security Standards, check events with kubectl describe pod and look for policy violations—common issues are missing runAsNonRoot or disallowed volume types. Temporarily switch the namespace label to Baseline or add a policy exception while you fix the pod spec.

Do I need a service mesh for Kubernetes security?

No, a service mesh isn't required for core security hardening. NetworkPolicies, RBAC, and Pod Security Standards cover the essential controls. A service mesh (Istio, Linkerd) adds mTLS between pods, fine-grained authorization policies, and traffic observability—useful for zero-trust architectures and compliance requirements, but it introduces complexity and resource overhead. Start with the six controls in this guide. Add a mesh later if you need encrypted pod-to-pod traffic, request-level authorization, or advanced traffic shifting. Many production clusters run securely without a mesh.