Kubernetes Security Best Practices 2026: Hardening Your K8s Cluster
Kubernetes misconfigurations drive a significant share of cloud security incidents. This guide covers essential hardening: RBAC, network policies, pod security standards, secrets management, and supply chain security with practical YAML examples.
Why Kubernetes Security Matters
Kubernetes is the default runtime for containerized workloads at scale - and consistently in the top sources of cloud security incidents. Most Kubernetes security problems are caused by misconfiguration, not novel attacks.
1. RBAC: Least Privilege at the API Level
Every API call to the cluster goes through RBAC authorization. The common mistake is giving service accounts cluster-admin or using wildcard permissions.
# Minimal role for an app that only reads ConfigMaps
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: configmap-reader
rules:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600">apiGroups: [""]</li>
</ul>
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["app-config"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-configmaps
namespace: production
subjects:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600">kind: ServiceAccount</li>
</ul>
name: my-app
namespace: production
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
Key practices: Audit ClusterRoleBindings quarterly, never bind cluster-admin to service accounts.
2. Pod Security Standards
Since Kubernetes 1.25, Pod Security Admission replaces PodSecurityPolicy:
| Profile | Use Case | What It Prevents |
|---|---|---|
| Privileged | System-level workloads | Nothing |
| Baseline | Standard applications | Privilege escalation, host namespace access |
| Restricted | Sensitive workloads | All of baseline + drops all capabilities |
kubectl label namespace production pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/warn=restrictedSet a compliant security context:
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
3. Network Policies: Default Deny
By default, all pods can communicate with all other pods. Fix this with a default deny policy and explicit allow rules for required traffic:
# Default deny all ingress and egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-2">Ingress</li>
<li class="text-gray-600 ml-2">Egress</li>
</ul>
---
# Allow app to reach its database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-app-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-2">Egress</li>
</ul>
egress:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-2">to:</li>
<li class="text-gray-600 ml-4">podSelector:</li>
</ul>
matchLabels:
app: postgres
ports:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-4">protocol: TCP</li>
</ul>
port: 5432Note: Requires a CNI plugin that supports Network Policies (Calico, Cilium).
4. Secrets Management
Kubernetes Secrets are base64-encoded, not encrypted, by default. Use External Secrets Operator:
This gives you centralized management, audit logging, and automatic rotation.
5. Image Security and Supply Chain
Scan in CI/CD with Trivy:<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600">name: Scan image for vulnerabilities</li>
</ul>
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
exit-code: '1'
severity: 'CRITICAL,HIGH'
ignore-unfixed: true
Block non-compliant images with Kyverno:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-approved-registry
spec:
validationFailureAction: Enforce
rules:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-2">name: check-registry</li>
</ul>
match:
resources:
kinds: [Pod]
validate:
message: "Images must be from mycompany.azurecr.io"
pattern:
spec:
containers:
<ul class="list-disc pl-6 mb-4 space-y-2">
<li class="text-gray-600 ml-8">image: "mycompany.azurecr.io/*"</li>
</ul>
6. Runtime Threat Detection
Falco monitors system calls for suspicious behavior. For AKS, Microsoft Defender for Containers provides managed runtime protection.Priority Order: Where to Start
- RBAC audit - find and remove wildcard/cluster-admin bindings
- Encryption at rest for Secrets or migrate to external secrets
- Pod Security Standards at baseline level
- Default-deny Network Policies in sensitive namespaces
- Image scanning in CI/CD and admission control
- Runtime detection (Falco or Defender for Containers)
Benchmark with kube-bench against the CIS Kubernetes Benchmark:
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-benchQuestions & Answers
Related Articles
Need Help with Your Security?
Our team of security experts can help you implement the strategies discussed in this article.
Contact Us