Introduction
Kubernetes has become the de facto standard for container orchestration, but with its complexity comes significant security challenges. A misconfigured Kubernetes cluster can expose applications to attacks, data breaches, and compliance violations. In 2026, securing Kubernetes requires a defense-in-depth approach spanning multiple layersโfrom container images to runtime behavior.
This comprehensive guide explores Kubernetes security in depth, covering authentication, authorization, network policies, pod security, secrets management, and runtime protection.
Kubernetes Security Architecture
Defense in Depth
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Kubernetes Security Layers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Image Security โ โ
โ โ โข Scan for vulnerabilities โ โ
โ โ โข Use minimal base images โ โ
โ โ โข Sign and verify images โ โ
โ โ โข Store images in secure registries โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 2. Cluster Security โ โ
โ โ โข RBAC for access control โ โ
โ โ โข etcd encryption โ โ
โ โ โข API server authentication โ โ
โ โ โข Network policies โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 3. Node Security โ โ
โ โ โข OS hardening โ โ
โ โ โข Kernel parameters โ โ
โ โ โข Container runtime security โ โ
โ โ โข Node access control โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 4. Pod Security โ โ
โ โ โข Pod Security Standards โ โ
โ โ โข Security contexts โ โ
โ โ โข Resource limits โ โ
โ โ โข Service account management โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 5. Runtime Security โ โ
โ โ โข Falco for behavioral monitoring โ โ
โ โ โข Seccomp profiles โ โ
โ โ โข AppArmor/SELinux โ โ
โ โ โข Admission controllers โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Security Context
# pod-security-context.yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
fsGroup: 10000
seccompProfile:
type: RuntimeDefault
sysctls:
- name: net.ipv4.ping_group_range
value: "0 0"
containers:
- name: app
image: secure-app:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
privileged: false
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "64Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
Role-Based Access Control (RBAC)
ClusterRoles and Roles
# cluster-role-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: admin-role
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
- nonResourceURLs: ["*"]
verbs: ["*"]
---
# role-developer.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: development
name: developer-role
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
# role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-role-binding
namespace: development
subjects:
- kind: User
name: john.doe
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer-role
apiGroup: rbac.authorization.k8s.io
Service Account Best Practices
# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
annotations:
description: "Service account for payment service"
---
# role-service-account.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: service-account-role
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["payment-db-credentials"]
verbs: ["get"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["payment-config"]
verbs: ["get", "watch"]
---
# role-binding-service-account.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: service-account-binding
namespace: production
subjects:
- kind: ServiceAccount
name: app-service-account
namespace: production
roleRef:
kind: Role
name: service-account-role
apiGroup: rbac.authorization.k8s.io
Pod Security Standards
PSS Policies
class PodSecurityStandards:
@staticmethod
def restricted():
return {
"policy": {
"name": "restricted",
"description": "Most restrictive policy",
"enforce": "Restricted",
"enforce_version": "latest",
"warn": "Restricted",
"warn_version": "latest",
"audit": "Restricted",
"audit_version": "latest"
},
"restrictions": [
"Cannot run as root",
"Cannot use privileged containers",
"Must drop all capabilities",
"Cannot mount hostPath volumes",
"Must use seccomp profile or RuntimeDefault"
]
}
@staticmethod
def baseline():
return {
"policy": {
"name": "baseline",
"description": "Baseline policy with minimal restrictions",
"enforce": "Baseline",
"enforce_version": "latest"
},
"allowed": [
"Running as non-root user",
"Specific capabilities allowed",
"HostPath with restrictions"
],
"forbidden": [
"Privileged containers",
"Host network access",
"Host PID namespace"
]
}
Applying PSS
# pss-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
---
# pss-exception.yaml
apiVersion: v1
kind: Namespace
metadata:
name: privileged
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
Network Policies
Basic Network Policies
# default-deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# allow-dns-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
# allow-app-communication.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
Advanced Network Policies
# complex-network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: complex-app-policy
namespace: production
spec:
podSelector:
matchLabels:
app: payment-service
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: api-gateway
- podSelector:
matchLabels:
app: admin-panel
ports:
- protocol: TCP
port: 8080
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
Secrets Management
Kubernetes Secrets
# secret-generic.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
stringData:
database-url: postgres://user:password@db:5432/mydb
api-key: sk_live_xxxxxxxxxxxxx
jwt-secret: verylongsecretkey
data:
# base64 encoded values
username: YWRtaW4=
password: cGFzc3dvcmQ=
---
# secret-tls.yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: production
type: kubernetes.io/tls
data:
# base64 encoded
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...
External Secrets Operator
# external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: aws-secrets-manager
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: app-secrets-from-aws
creationPolicy: Owner
data:
- secretKey: database-password
remoteRef:
key: prod/database
property: password
- secretKey: api-key
remoteRef:
key: prod/api-keys
property: payment-api
---
# secret-store.yaml
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
namespace: external-secrets
Admission Controllers
Validating Webhooks
# validating-webhook.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: pod-security-validation
webhooks:
- name: pods.security.example.com
clientConfig:
service:
name: security-webhook
namespace: kube-system
path: "/validate-pod"
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
failurePolicy: Fail
timeoutSeconds: 10
OPA Gatekeeper
# gatekeeper-constraint-template.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": required_labels}] {
provided := input.review.object.metadata.labels
required := input.parameters.labels
missing := required - provided
count(missing) > 0
msg := sprintf("Missing required labels: %v", [missing])
}
---
# gatekeeper-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: require-app-label
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod", "Deployment", "StatefulSet"]
parameters:
labels:
- app
- environment
Runtime Security
Falco Configuration
# falco-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: falco-config
namespace: falco
data:
falco.yaml: |
json_output: true
program_priority: "INFO"
log_level: info
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/k8s_audit_rules.yaml
plugins:
- name: k8saudit
library_path: libk8saudit.so
init_config:
.
.
default_rules_for_emitter: false
syscall_event_drops:
threshold: .1
actions:
- log
- alert
priority: warning
# Custom rules
falco_rules.local.yaml: |
- rule: Detect shell in container
desc: A shell was spawned inside a container
condition: >
evt.type = execve and
container.id != host and
(proc.name in (sh, bash, zsh, dash) or
proc.name = "sh" or
proc.aname in (sh, bash, zsh, dash))
output: "Shell detected in container (user=%user.name container_id=%container.id shell=%proc.name)"
priority: WARNING
tags: [container, shell]
- rule: Detect privilege escalation
desc: Privilege escalation detected
condition: >
evt.type = setuid or evt.type = setgid or
evt.type = setxid or evt.type = setresuid or
evt.type = setfsuid or evt.type = setreuid
output: "Privilege escalation detected (user=%user.name uid=%user.uid)"
priority: CRITICAL
tags: [container, privilege]
Seccomp Profiles
# seccomp-profile.yaml
apiVersion: v1
kind: Pod
metadata:
name: workload-with-seccomp
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/custom-seccomp.json
---
# custom-seccomp.json
{
"defaultAction": "SCMP_ACT_LOG",
"syscalls": [
{
"names": [
"read",
"write",
"close",
"exit_group"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": [
"connect",
"accept",
"listen",
"sendto",
"recvfrom"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Image Security
Image Scanning Pipeline
class ImageSecurityScanner:
def __init__(self):
self.scanner = "trivy"
def scan_image(self, image: str) -> dict:
return {
"image": image,
"vulnerabilities": {
"critical": 0,
"high": 2,
"medium": 5,
"low": 10
},
"scanned_at": "2026-03-12T10:00:00Z",
"scan_result": "FAILED"
}
def enforce_scan_policy(self, image: str) -> bool:
result = self.scan_image(image)
critical_threshold = 0
high_threshold = 5
if result["vulnerabilities"]["critical"] > critical_threshold:
return False
if result["vulnerabilities"]["high"] > high_threshold:
return False
return True
Image Policy Enforcement
# image-policy-webhook.yaml
apiVersion: imagepolicy.k8s.io/v1alpha1
kind: ImagePolicy
metadata:
name: image-policy
spec:
repositories:
- name: "registry.example.com/*"
policy:
requirement:
oneOf:
- namespaceLabel: trusted
template:
metadata:
annotations:
imagetag: ".*"
injectAnnotations:
- name: image.approved
---
# kubewarden-policy.yaml
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicy
metadata:
name: require-readonly-rootfs
spec:
module: registry://ghcr.io/kubewarden/policies/readonly-rootfs-psp:v0.1.5
settings:
runAsNonRoot: true
rules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
operations: ["CREATE", "UPDATE"]
Best Practices
Security Checklist
KUBERNETES_SECURITY_CHECKLIST = {
"cluster_setup": [
"Enable RBAC and restrict permissions",
"Enable etcd encryption at rest",
"Use dedicated service accounts",
"Configure API server authentication",
"Enable audit logging",
"Secure etcd with TLS and authentication",
"Use network policies to isolate namespaces"
],
"container_security": [
"Use minimal base images",
"Scan images for vulnerabilities",
"Run containers as non-root user",
"Use read-only root filesystem",
"Drop all capabilities",
"Enable seccomp profiles",
"Set appropriate resource limits"
],
"runtime_security": [
"Enable runtime protection (Falco)",
"Implement admission controllers",
"Use Pod Security Standards",
"Enable image signing verification",
"Implement secrets encryption",
"Monitor for suspicious activity"
],
"network_security": [
"Implement NetworkPolicies",
"Use mTLS between services",
"Restrict egress traffic",
"Enable DNS policies",
"Use service mesh for traffic encryption"
],
"monitoring": [
"Enable audit logging",
"Set up Falco alerts",
"Monitor API server requests",
"Track pod security events",
"Implement alerting for anomalies"
]
}
Hardening Guide
HARDENING_GUIDE = {
"api_server": [
"--anonymous-auth=false",
"--authorization-mode=Node,RBAC",
"--enable-admission-plugins=NodeRestriction",
"--encryption-provider-config=encryption-config",
"--audit-log-path=/var/log/kubernetes/audit.log",
"--audit-log-maxsize=100",
"--audit-log-maxbackup=10",
"--request-timeout=60s"
],
"kubelet": [
"--anonymous-auth=false",
"--authorization-mode=Webhook",
"--client-ca-file=/etc/kubernetes/pki/ca.crt",
"--read-only-port=0",
"--protect-kernel-defaults=true",
"--make-iptables-util-chains=true"
],
"containerd": [
"Enable selinux",
"Disable live restore",
"Use user namespace remapping",
"Configure container defaults"
]
}
Resources
Conclusion
Kubernetes security requires a comprehensive, defense-in-depth approach spanning the entire container lifecycle. From securing container images to implementing runtime protection, every layer must be carefully configured and continuously monitored.
This guide covered authentication and authorization with RBAC, Pod Security Standards, network policies, secrets management, admission controllers, runtime security with Falco and seccomp, and image security best practices. By implementing these security measures, you can significantly reduce the attack surface of your Kubernetes clusters and protect your applications from common threats.
Comments