Kubernetes Security Overview
On this page
Coverage checklist
- Kubernetes threat model and STRIDE analysis
- 4C security model (Cloud, Cluster, Container, Code)
- Attack surface: API server, etcd, kubelet, supply chain
- Defense-in-depth layer diagram
- AuthN: X.509 / OIDC / ServiceAccount tokens / webhook
- AuthZ: RBAC, ABAC, Node, Webhook modes
- Admission control pipeline
- Pod Security Admission (privileged/baseline/restricted)
- Network policy default-deny pattern
- Secrets: etcd encryption, external secret stores
- Service account token security (bound tokens)
- Image scanning: Trivy, Grype, Snyk
- Supply chain: SBOM, Sigstore/cosign, provenance
- Runtime security: Falco, seccomp, AppArmor, SELinux
- Audit logging: levels, backend, retention
- CIS Kubernetes Benchmark top controls
- Security checklist 30 items
- Best practices 8 items
- mTLS between components: kube-apiserver TLS
- etcd TLS client auth requirement
- Anonymous auth disable
- NodeRestriction admission plugin
- Kubelet authorization mode: Webhook
- read-only port 10255 disable
- Privileged container risks
- hostNetwork/hostPID/hostIPC risks
- Capability drop ALL + add specific
- seccompProfile RuntimeDefault
- runAsNonRoot enforcement
- readOnlyRootFilesystem
- Resource limits as security control
- IRSA / Workload Identity patterns
- Namespace-based tenancy limitations
- Links to all detailed sub-pages
Kubernetes Threat Model
Kubernetes clusters are complex distributed systems with a large attack surface. Understanding what can go wrong drives the security architecture. The Kubernetes threat model covers adversaries who are:
- External attackers — no access to the cluster; attempting to reach exposed services or the API server
- Internal network attackers — compromised a node or pod; attempting lateral movement within the cluster
- Malicious tenants — legitimate cluster users attempting privilege escalation to other namespaces or the host
- Supply chain attackers — compromised a container image, Helm chart, or operator to gain execution inside the cluster
STRIDE Analysis
| Threat | Kubernetes Context | Primary Mitigations |
|---|---|---|
| Spoofing | Impersonating a service account, forging kubeconfig credentials | OIDC short-lived tokens, cert rotation, audit logging |
| Tampering | Modifying etcd data directly, patching running pods without audit trail | etcd TLS+auth, etcd encryption at rest, audit log immutability |
| Repudiation | Denying that a workload made a change; no record of API calls | Audit logging with Request/Response levels retained off-cluster |
| Information Disclosure | Secrets in env vars, overly permissive RBAC reads, pod spec dumps | Encryption at rest, external secret stores, RBAC least privilege |
| Denial of Service | Resource exhaustion via unconstrained workloads, etcd overload | ResourceQuota, LimitRange, PriorityClass, rate limiting |
| Elevation of Privilege | Container escape via privileged pod, hostPath write, RBAC cluster-admin grant | Pod Security Admission (restricted), no privileged pods, RBAC audit |
The 4C Security Model
The 4C model (Cloud, Cluster, Container, Code) describes a layered security approach. Each outer layer protects the layers within it, but a vulnerability at any layer can undermine the protections of the layers it contains.
┌────────────────────────────────────────────────────────────────┐ │ CLOUD │ │ IAM roles, VPC network ACLs, node firewall rules, │ │ cloud provider encryption, managed control plane TLS │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ CLUSTER │ │ │ │ API server auth/authz, RBAC, admission controllers, │ │ │ │ etcd encryption, network policies, audit logging │ │ │ │ │ │ │ │ ┌────────────────────────────────────────────────────┐ │ │ │ │ │ CONTAINER │ │ │ │ │ │ Pod Security (seccomp, AppArmor, capabilities), │ │ │ │ │ │ non-root execution, read-only rootfs, Falco │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ │ │ CODE │ │ │ │ │ │ │ │ SAST, dependency scanning, image signing, │ │ │ │ │ │ │ │ SBOM, Sigstore provenance, secret scanning │ │ │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ └────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────────┘
Attack Surface Map
EXTERNAL CLUSTER INTERNAL
───────── ──────────────────
kubectl / CI/CD ──► API Server ──► etcd (CRITICAL — encrypt + TLS client auth)
│
├──► Controller Manager (in-cluster RBAC)
├──► Scheduler (in-cluster RBAC)
└──► Kubelet API (port 10250)
│
┌──────┴──────┐
│ Node OS │
│ Container │
│ Runtime │
└─────────────┘
│
┌──────▼──────┐
│ Pods │
│ ServiceAcct │──► cloud IAM via IRSA/Workload Identity
└─────────────┘
High-Value Targets
| Target | Why Critical | Key Hardening |
|---|---|---|
| etcd | All cluster state including secrets; direct write bypasses all admission | TLS client cert auth, encryption at rest, firewall to API server only |
| API server | Single entry point for all cluster operations | Disable anonymous auth, enable audit, RBAC, admission plugins |
| Kubelet | Port 10250 allows exec/logs for all pods on node | Webhook authorization, disable read-only port 10255, NodeRestriction |
| Service account tokens | Auto-mounted; long-lived tokens give persistent API access | Bound tokens (expiry), automountServiceAccountToken: false default |
| Container images | Backdoored images run attacker code at cluster scale | Image signing (cosign), admission policy (image policy webhook) |
Defense-in-Depth Layers
Authentication
Who can talk to the API server? X.509 client certs, OIDC tokens (Dex, Okta), ServiceAccount bound tokens, webhook token authenticators.
Authorization (RBAC)
What are they allowed to do? Role, ClusterRole, RoleBinding, ClusterRoleBinding — least-privilege per workload. Covered in 01-rbac.html.
Admission Control
Mutating and validating webhooks, OPA/Gatekeeper, Kyverno, Pod Security Admission. Final gate before objects are persisted. Covered in 06-admission-controllers.html.
Network Policies
Default-deny east-west traffic; explicit allow rules per service. Requires a CNI that enforces policies (Cilium, Calico, Weave). Covered in 03-network-policies.html.
Pod Security
seccomp, AppArmor, Linux capabilities, non-root, read-only rootfs, no privilege escalation. Covered in 02-pod-security.html.
Secrets Management
etcd encryption at rest, external secret stores (Vault, AWS SSM, CSI driver), short rotation windows. Covered in 04-secrets-management.html.
Image & Supply Chain
Vulnerability scanning (Trivy), image signing (cosign/Sigstore), SBOM generation, admission policy enforcement. Covered in 07-image-security.html and 10-supply-chain-security.html.
Runtime Security
Falco behavioral detection, eBPF-based syscall monitoring, anomaly alerting. Covered in runtime section.
Audit Logging
All API server requests recorded at configurable levels; shipped off-cluster for immutability. Covered in 08-audit-logging.html.
Authentication & Authorization
Authentication Methods
| Method | Use Case | Token Lifetime | Notes |
|---|---|---|---|
| X.509 client certs | Admin users, control-plane components | Up to cert expiry | Revocation requires CRL or cert rotation; hard to revoke individual users |
| OIDC tokens | Human users via Dex, Okta, Auth0, Azure AD | Typically 1h (JWT exp) | Short-lived; requires --oidc-issuer-url API server flag |
| ServiceAccount tokens (bound) | In-cluster workloads (1.20+ projected volumes) | Configurable (default 1h) | Audience + expiry + pod binding; replaces legacy static tokens |
| Bootstrap tokens | Node join (kubeadm) | 24h default | system:bootstrappers group; deleted after join |
| Webhook token authenticator | External identity providers | Provider-defined | API server calls webhook; flexible but adds latency |
| Static token file | Testing only | Permanent until restart | Never use in production |
Authorization Modes
The API server evaluates authorization modes in the order specified by --authorization-mode. A request is allowed if any mode permits it (short-circuit on first Allow).
| Mode | Description | Production Use |
|---|---|---|
RBAC | Role-based; fine-grained verb+resource rules | ✅ Always enable |
Node | Restricts kubelet to only read/write resources belonging to its own node | ✅ Always enable (with NodeRestriction admission) |
Webhook | Delegates to external HTTP endpoint (OPA, custom systems) | Optional; for complex policies |
ABAC | File-based attribute policies; requires restart to update | ❌ Legacy; superseded by RBAC |
AlwaysAllow | No authorization check | ❌ Testing only |
AlwaysDeny | All requests denied | ❌ Testing only |
--authorization-mode=Node,RBAC at minimum. Node mode + NodeRestriction admission prevents a compromised kubelet from reading secrets for pods not running on its node.
Admission Control Pipeline
After a request passes authentication and authorization, it passes through the admission control pipeline before being persisted to etcd:
Request → AuthN → AuthZ → Mutating Admission → Object Schema Validation → Validating Admission → etcd
│ │
MutatingWebhookConfigurations ValidatingWebhookConfigurations
Pod Security Admission (mutate) Pod Security Admission (validate)
LimitRanger OPA/Gatekeeper
ServiceAccount token injection Kyverno
NamespaceLifecycle ResourceQuota
Key built-in admission plugins (enabled by default or recommended):
| Plugin | Purpose |
|---|---|
NamespaceLifecycle | Prevents creating objects in terminating namespaces |
LimitRanger | Enforces LimitRange defaults and constraints |
ServiceAccount | Injects default service account token |
NodeRestriction | Limits kubelet to mutate only its own Node/Pod objects |
PodSecurity | Enforces Pod Security Standards (privileged/baseline/restricted) |
ResourceQuota | Enforces namespace resource quotas |
MutatingAdmissionWebhook | Calls registered mutating webhooks |
ValidatingAdmissionWebhook | Calls registered validating webhooks |
Workload Isolation
Pod Security Standards
Pod Security Admission (PSA) enforces three built-in policy levels per namespace, applied via the label pod-security.kubernetes.io/enforce. See 02-pod-security.html for the full field-by-field breakdown.
| Level | Who Should Use It | What It Allows |
|---|---|---|
| privileged | System namespaces (kube-system, node agents) | No restrictions — full host access possible |
| baseline | General workloads needing some elevated permissions | No privileged containers; no host namespaces; restricted capabilities |
| restricted | Untrusted or hardened production workloads | Enforces seccomp RuntimeDefault, non-root, no privilege escalation, drop ALL caps |
Namespace Tenancy Model
Namespaces provide a soft isolation boundary — they scope RBAC, ResourceQuota, NetworkPolicy, and PSA. They do not provide hard multi-tenancy; a privileged workload in any namespace can reach the host. For stronger isolation, use separate clusters or hierarchical namespace controllers (HNC).
kubectl label namespace my-app \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/warn=restrictedUse
enforce to block, audit to log violations, warn to surface warnings without blocking.
Key Pod-Level Security Fields
securityContext: # Pod-level
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault # or Localhost with custom profile
sysctls: [] # avoid privileged sysctls
containers:
- securityContext: # Container-level (overrides pod-level)
allowPrivilegeEscalation: false # prevent setuid/sudo escalation
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"] # only if port < 1024 needed
seccompProfile:
type: RuntimeDefault
Network Security
Default-Deny Pattern
Without Network Policies, all pods can communicate freely within a cluster. The correct default is to deny all ingress and egress, then explicitly allow required flows. See 03-network-policies.html for full examples.
# Apply to every tenant namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {} # matches all pods
policyTypes:
- Ingress
- Egress
# no ingress/egress rules = deny all
mTLS for Service-to-Service
NetworkPolicy operates at L3/L4 (IP + port). For L7 identity-based traffic control and end-to-end encryption, use a service mesh (Istio, Linkerd, Cilium's mesh mode). mTLS provides:
- Mutual authentication — both sides verify identity via SPIFFE SVID certs
- Encryption in transit — all service traffic encrypted, even within the same node
- Fine-grained AuthorizationPolicy — "service A may only call service B's /api/v1 endpoint"
Secrets & Supply Chain
Secret Storage Risk Hierarchy
| Storage Method | Risk Level | Notes |
|---|---|---|
| Plain text in source code / ConfigMap | Critical | Never do this |
| Kubernetes Secret (base64, no etcd encryption) | High | Readable by anyone with etcd access or broad RBAC |
| Kubernetes Secret + etcd encryption at rest | Medium | Protects stolen etcd backup; API server still decrypts on read |
| External Secret Operator (Vault / AWS SSM) | Low | Secrets never reside in etcd; centralized rotation; audit trail |
| CSI secrets store driver (SSCD) | Low | Mounted as files, not env vars; ephemeral, rotated by provider |
Supply Chain Attack Vectors
- Compromised base image — malicious code in public Docker Hub image
- Dependency confusion — internal package name shadowed by public malicious package
- Typosquatting —
alpine-3.18vsalipne-3.18in Dockerfile - Compromised CI pipeline — attacker injects code during build, before signing
- Untrusted Helm charts — public chart installs ClusterRoleBinding with cluster-admin
See 10-supply-chain-security.html for Sigstore/cosign signing workflow, SBOM generation with Syft, and admission policy enforcement with Kyverno image verification.
Runtime Security
Linux Kernel Security Mechanisms
| Mechanism | What It Does | Kubernetes Integration |
|---|---|---|
| seccomp | Restricts allowed syscalls; blocks dangerous calls like ptrace, mount, unshare | securityContext.seccompProfile; RuntimeDefault or custom JSON profile |
| AppArmor | File/capability access profiles per process | container.apparmor.security.beta.kubernetes.io/<container> annotation (GA in 1.30) |
| SELinux | Mandatory access control; label-based policy enforcement | securityContext.seLinuxOptions; typically set by runtime on RHEL/CoreOS |
| Linux capabilities | Split root privileges into fine-grained tokens | capabilities.drop: ["ALL"] + explicit add of needed caps |
| Namespaces | Isolate process view: PID, net, IPC, UTS, mount | Container runtime manages; hostPID/hostNetwork/hostIPC: false |
Falco — Behavioral Runtime Detection
Falco (CNCF) monitors kernel syscalls via eBPF/kernel module and alerts on policy violations at runtime. Key default rules:
- Shell spawned in container (exec of bash/sh/zsh)
- Write to /etc or /usr directories
- Network connection from unexpected process
- Sensitive file reads (/etc/shadow, /etc/kubernetes/pki/...)
- Container privilege escalation attempt
- K8s audit event for sensitive operations (secret reads, exec into pods)
Compliance & Audit
Kubernetes audit logging records every API server request at configurable verbosity levels. Logs should be shipped to immutable storage (S3, Elasticsearch, Splunk) off-cluster immediately. See 08-audit-logging.html for audit policy examples.
Compliance Frameworks
| Framework | Kubernetes Relevance | Key Controls |
|---|---|---|
| CIS Kubernetes Benchmark | Prescriptive hardening for control plane, nodes, RBAC | API server flags, etcd TLS, RBAC audit, node config |
| NSA/CISA Kubernetes Hardening Guide | Government/defense-grade guidance | Pod security, network policies, audit, image provenance |
| SOC 2 Type II | Audit trail completeness, access control | Audit logging, RBAC reviews, secret management, MFA for API access |
| PCI DSS | Cardholder data environment isolation | Network segmentation (policies), encryption at rest+transit, audit, vulnerability scanning |
| HIPAA | PHI in containers | Encryption at rest, audit, BAA with cloud provider, access control |
| FedRAMP | US federal cloud authorization | FIPS 140-2 crypto, STIG compliance, continuous monitoring |
See 11-compliance.html for compliance automation tools (kube-bench, Polaris, Kubescape).
Security Hardening Checklist
Control Plane
| Check | Command / Verification | Priority |
|---|---|---|
| Disable anonymous auth | --anonymous-auth=false on kube-apiserver | Critical |
| Enable Node+RBAC authorization | --authorization-mode=Node,RBAC | Critical |
| Enable audit logging | --audit-log-path + --audit-policy-file | Critical |
| etcd TLS + client cert auth | --etcd-cafile --etcd-certfile --etcd-keyfile | Critical |
| etcd encryption at rest | --encryption-provider-config with aescbc or KMS | Critical |
| Enable NodeRestriction admission | --enable-admission-plugins=...,NodeRestriction | Critical |
| Disable insecure HTTP port | --insecure-port=0 (default disabled since 1.20) | Critical |
| TLS cipher restriction | --tls-min-version=VersionTLS13 | High |
| Enable PodSecurity admission | --enable-admission-plugins=...,PodSecurity | High |
| Rotate certificates regularly | kubeadm certs renew all; automate via cert-manager | High |
Nodes
| Check | Verification | Priority |
|---|---|---|
| Kubelet authorization mode: Webhook | --authorization-mode=Webhook in kubelet config | Critical |
| Disable read-only port | --read-only-port=0 in kubelet config | Critical |
| Disable anonymous kubelet auth | authentication.anonymous.enabled: false | Critical |
| Enable kubelet TLS | --tls-cert-file --tls-private-key-file | Critical |
| Restrict hostPath mounts | Admission policy: deny hostPath.path: / or /etc | High |
| CIS-hardened node image | Use CIS-hardened AMI/image; run kube-bench to verify | High |
Workloads
| Check | Implementation | Priority |
|---|---|---|
| No privileged containers | PSA restricted or Kyverno deny privileged: true | Critical |
| No hostNetwork / hostPID / hostIPC | PSA baseline+ or admission policy | Critical |
| Drop ALL capabilities | capabilities.drop: ["ALL"] | High |
| seccomp RuntimeDefault | seccompProfile.type: RuntimeDefault | High |
| runAsNonRoot | runAsNonRoot: true + runAsUser: 1000 | High |
| readOnlyRootFilesystem | readOnlyRootFilesystem: true | High |
| No allowPrivilegeEscalation | allowPrivilegeEscalation: false | High |
| automount SA token only when needed | automountServiceAccountToken: false on SA or Pod | High |
| Resource limits set on all containers | LimitRange defaults + explicit limits | Medium |
| Network policies: default deny | Apply default-deny NetworkPolicy to all tenant namespaces | High |
CIS Benchmark Key Controls Summary
The CIS Kubernetes Benchmark (Center for Internet Security) is the most widely adopted prescriptive hardening guide. Run kube-bench to automatically check your cluster against the benchmark. Key sections:
| Section | Focus Area | Example Checks |
|---|---|---|
| 1 — Control Plane Components | kube-apiserver, etcd, scheduler, controller-manager flags | --anonymous-auth=false, --audit-log-path, etcd TLS |
| 2 — Etcd | etcd security configuration | Peer TLS, client cert auth, encryption at rest |
| 3 — Control Plane Config | kubeconfig file permissions, certificates | admin.conf permissions 600, cert expiry monitoring |
| 4 — Worker Node Security | Kubelet configuration | Webhook authn/authz, read-only port disabled |
| 5 — Kubernetes Policies | RBAC, Pod Security, Network Policies, Secrets | No cluster-admin bindings for SA, PSA enabled, secrets encrypted |
# Run kube-bench in-cluster
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench
# Or run as a pod targeting specific benchmark
kubectl run kube-bench --image=aquasec/kube-bench:latest \
--restart=Never -- node --benchmark cis-1.8
Best Practices
- Apply the 4C model systematically. Don't rely on cluster security alone. Harden the cloud layer (IAM, VPC), the container layer (seccomp, capabilities), and the code layer (SAST, image scanning) independently.
- Enforce Pod Security Standards at the restricted level for all non-system namespaces. Use
enforcemode (not justwarn) on all workload namespaces. Reserveprivilegedlevel forkube-systemand node agent namespaces only. - Use short-lived, bound service account tokens. Disable legacy auto-mounted tokens (
automountServiceAccountToken: falseon default ServiceAccount per namespace). Inject tokens only for pods that need API server access, using projected volumes with a 1h audience-bound expiry. - Encrypt secrets at rest and prefer external secret stores. etcd encryption at rest protects stolen backups; an external store (Vault, AWS SSM) additionally prevents secrets from residing in etcd at all. Use IRSA/Workload Identity for cloud-native secret retrieval without static credentials.
- Implement default-deny NetworkPolicy in every tenant namespace. Then explicitly allow required ingress/egress. This prevents east-west lateral movement after a container compromise.
- Sign all images with cosign and enforce policy in admission. A Kyverno ClusterPolicy or OPA Gatekeeper constraint should reject pods using unsigned images from unauthorized registries. This blocks most supply chain attacks.
- Ship audit logs off-cluster immediately to immutable storage. A compromised cluster admin can delete on-cluster audit logs. Ship to an external SIEM (Splunk, Elasticsearch, CloudWatch) with a write-once policy.
- Run CIS kube-bench and address all Level 1 failures before production. Automate kube-bench as part of cluster provisioning CI. Re-run after every control plane upgrade. Track benchmark drift in a dedicated Grafana dashboard.