· Updated · 4 min read
Five Kubernetes security mistakes we see in every audit
The same handful of misconfigurations show up again and again. Here's what to fix first and why each one matters.
Kubernetes is powerful, but its defaults are tuned for flexibility, not safety. A fresh cluster lets any pod run as root, talk to any other pod, and use whatever the API allows. Across the clusters we review, a small set of issues account for most of the real risk. None of them needs a big project — most are an afternoon of work each — and together they move a cluster from "trust everything" to "trust only what was declared".
Here are the five we find most often, in the order we usually fix them.
1. Running containers as root
Most images still run as root by default, and Kubernetes will happily schedule them. If an attacker gets code execution in such a container, a root user with a writable filesystem and the default set of Linux capabilities makes escaping or persisting far easier.
The fix is a few lines in each workload's security context:
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefaultSome images need adjusting first: a writable /tmp through an emptyDir volume, or a numeric user set in the Dockerfile. Rather than chasing workloads one by one, enable Pod Security Admission with the restricted profile on application namespaces. Start with the warn and audit modes to see what would break, then switch to enforce.
2. Over-permissive RBAC
Wildcard roles and cluster-admin bindings are convenient and dangerous. They usually arrive with a Helm chart, a CI service account that needed "just enough to deploy", or an engineer who was blocked late at night. Start by finding who holds the most power:
- List every ClusterRoleBinding to
cluster-adminand ask why each one exists. - Search Roles and ClusterRoles for
*in verbs, resources, or API groups. - Check which service accounts can read Secrets, create pods, or exec into them — any of those is close to full access to the namespace.
- Run
kubectl auth can-i --list --as=system:serviceaccount:<namespace>:<name>to see what an identity can really do.
Then replace wildcards with explicit verbs and resources, prefer namespaced Roles over ClusterRoles, and set automountServiceAccountToken: false for pods that never call the Kubernetes API — which is most of them.
3. No network policies
By default every pod can talk to every other pod in the cluster, across namespaces. One compromised workload can reach your databases, internal admin services, and the cloud metadata endpoint. A default-deny policy per namespace, followed by explicit allow rules, turns lateral movement from trivial into hard:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes: ["Ingress", "Egress"]Remember to allow DNS egress to kube-dns, or everything in the namespace breaks at once. Check that your network plugin actually enforces NetworkPolicy, too — some CNIs, Flannel for example, silently ignore it. Roll out one namespace at a time, starting with the ones that hold the most sensitive data.
4. Secrets in plain manifests
Base64 is not encryption. A Secret committed to Git, or rendered into a Helm values file, is readable by anyone with access to the repository — and stays in its history long after the file is deleted.
- Keep secrets in a real secrets manager and sync them into the cluster with an operator such as External Secrets, or encrypt them for Git with Sealed Secrets.
- Enable encryption at rest for Secrets in etcd. On managed Kubernetes this is usually a KMS setting.
- Restrict
getandliston Secrets in RBAC. Note thatlistreturns the secret values too. - Scan your Git history for leaked credentials, and rotate anything you find rather than only deleting the file.
5. Skipping admission control
Every fix above can be undone by the next deploy unless something checks it. Policy engines like OPA Gatekeeper or Kyverno enforce rules at admission time, so insecure workloads never reach the cluster in the first place.
Good first policies encode the fixes above: require non-root containers, forbid privileged pods and host namespaces, require resource limits, and only allow images from your own registry. Start in audit mode, read the report, fix the offenders, then enforce.
Where to start
- Turn on Pod Security Admission in
warnmode and read what it reports. - Audit cluster-admin bindings and wildcard roles.
- Add a default-deny network policy to your most sensitive namespace.
- Move secrets out of Git and enable encryption at rest.
- Add a policy engine in audit mode, then enforce.
Each step is small, but together they meaningfully raise your security baseline. This is core to our security and compliance work, and it was a large part of the SOC 2 readiness engagement we ran for a healthtech startup. If you want a second set of eyes on your cluster, get in touch.