Security¶
Kubernetes security follows the 4C's model: Cloud, Cluster, Container, and Code. This note covers cluster and container layers, focusing on identity, access control, pod isolation, secrets management, and supply chain security.
See also: infrastructure/kubernetes/index, infrastructure/kubernetes/architecture, infrastructure/kubernetes/architecture, infrastructure/kubernetes/operations
Authentication and Identity¶
Users and Service Accounts¶
Kubernetes distinguishes between two types of identities:
- Users: Managed externally (OIDC, LDAP, SAML). Kubernetes does not store user objects. Users authenticate via the API server using certificates, tokens, or external identity providers.
- Service Accounts: Kubernetes-managed identities for pods. Each namespace has a default service account. Custom service accounts are used to grant pods specific permissions.
Service Account Tokens
Since Kubernetes 1.24, service account tokens are no longer auto-mounted. Use automountServiceAccountToken: false unless a pod explicitly needs API access. Tokens are short-lived JWTs issued by the API server and projected into pods via volume mounts.
Authentication Methods¶
| Method | Use Case |
|---|---|
| X.509 client certs | Admin access, kubeconfig |
| OIDC (e.g., Okta, Entra ID) | Enterprise SSO for human users |
| Service account tokens | Pod-to-API-server communication |
| Webhook token auth | Custom token validation |
| Bootstrap tokens | Node bootstrapping (kubeadm) |
RBAC (Role-Based Access Control)¶
RBAC is the primary authorization mechanism in Kubernetes. It uses four API objects:
| Object | Scope | Purpose |
|---|---|---|
Role |
Namespace | Defines permissions within a single namespace |
ClusterRole |
Cluster-wide | Defines permissions across all namespaces or cluster-scoped resources |
RoleBinding |
Namespace | Grants a Role (or ClusterRole) to subjects within a namespace |
ClusterRoleBinding |
Cluster-wide | Grants a ClusterRole to subjects across all namespaces |
Subjects can be Users, Groups, or ServiceAccounts.
# Example: Read-only access to pods in a namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: production
subjects:
- kind: User
name: developer
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
RBAC Best Practices
- Avoid binding
cluster-adminbroadly. Use least-privilege Roles. - Audit existing bindings regularly with
kubectl get rolebindings,clusterrolebindings -o wide. - Use namespace isolation to limit blast radius.
Pod Security Standards and Admission¶
Pod Security Standards define three privilege levels applied at the namespace level:
| Level | Description |
|---|---|
| Privileged | Unrestricted; allows host access, privileged containers, all capabilities |
| Baseline | Minimally restrictive; prevents host namespace sharing, privileged ports, hostPath mounts |
| Restricted | Heavily restricted; requires non-root, dropped capabilities, read-only root FS, seccomp |
Enforcement is done via the built-in Pod Security Admission controller (GA since v1.25):
# Apply restricted policy to a namespace
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
The admission controller supports three modes per namespace: - enforce: Blocks non-compliant pods - audit: Logs audit events for non-compliant pods - warn: Triggers warnings but does not block
NetworkPolicy¶
NetworkPolicies control traffic flow at the pod level:
- Default: all pod-to-pod communication is allowed within a cluster
- NetworkPolicy can restrict ingress and egress traffic by label selectors, IP blocks, and ports
- Implemented by the CNI plugin (Calico, Cilium, Antrea, etc.)
- Without a CNI plugin that supports NetworkPolicy, the rules are ignored
# Example: Deny all ingress to a namespace, allow only from specific pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
protocol: TCP
Default Deny
A best practice is to create a default-deny-all NetworkPolicy in every namespace, then layer specific allow rules on top.
Secrets Management¶
Kubernetes Secrets¶
Kubernetes Secrets store sensitive data (passwords, tokens, keys) as base64-encoded objects:
- Stored in etcd; enable encryption at rest via
EncryptionConfiguration - Default: base64 encoding only (not encryption). Configure
--encryption-provider-configon the API server. - Mounted into pods as files (tmpfs) or environment variables
- Namespace-scoped; not accessible across namespaces without explicit sharing
Encryption at Rest¶
Enable encryption by configuring the API server:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc: # Deprecated since K8s 1.28; prefer aesgcm or secretbox
keys:
- name: key1
secret: <BASE64_ENCODED_SECRET>
- identity: {}
Deprecated Provider
The aescbc encryption provider is deprecated since Kubernetes 1.28. For new deployments, use aesgcm or secretbox providers instead. See Encrypting Secret Data at Rest for current recommendations.
External Secrets Management¶
For production, integrate with external secret stores:
- External Secrets Operator: Syncs secrets from AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault
- Sealed Secrets: Encrypts secrets with a public key; decrypts in-cluster with a private key
- Vault Agent Injector: Injects Vault secrets as files into pods via mutation
Admission Controllers¶
Admission controllers intercept API requests after authentication and authorization but before object persistence:
| Controller | Purpose |
|---|---|
| PodSecurity | Enforces Pod Security Standards |
| ResourceQuota | Limits resource consumption per namespace |
| LimitRanger | Sets default resource requests/limits |
| NamespaceLifecycle | Prevents creation in terminating namespaces |
| ServiceAccount | Automates service account token mounting |
Policy Engines (OPA Gatekeeper / Kyverno)¶
External admission webhooks enforce custom policies:
- OPA Gatekeeper: Uses Rego policies; audit mode for dry-run, enforcement via webhook
- Kyverno: Kubernetes-native policies (YAML, no new language); mutation and validation rules
- Both run as validating/mutating admission webhooks
- Policies can enforce: image registry restrictions, label requirements, resource limits, security contexts
# Kyverno example: Require non-root containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-non-root
spec:
validationFailureAction: Enforce
rules:
- name: check-runAsNonRoot
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Containers must run as non-root"
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
Image Policy and Supply Chain¶
Image Policy Webhook¶
The ImagePolicyWebhook admission controller can validate images against external policy:
- Restrict registries (e.g., only allow images from
registry.example.com) - Require signed images
- Enforce tag restrictions (e.g., disallow
:latest)
Sigstore / Cosign¶
- Sign container images with Cosign (Sigstore)
- Verify signatures at admission time using policy controllers
- Kyverno and OPA Gatekeeper both support Cosign verification
Audit Logging¶
Kubernetes audit logging records API server requests for forensic analysis:
- Configured via
--audit-log-pathandAuditPolicyon the API server - Four logging stages:
RequestReceived,ResponseStarted,ResponseComplete,Panic - Log levels:
None,Metadata,Request,RequestResponse - Essential for compliance (SOC 2, PCI-DSS) and incident investigation
# Audit policy example
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
- level: Metadata
resources:
- group: ""
resources: ["pods", "services"]
- level: None
users: ["system:kube-proxy"]
CIS Benchmarks¶
The Center for Internet Security publishes Kubernetes benchmarks covering:
- Control plane node configuration (API server, scheduler, controller-manager, etcd)
- Worker node configuration (kubelet, proxy)
- Network policies and CNI security
- RBAC and service account controls
- Secret encryption and audit logging
Automated scanning tools: - kube-bench: Open-source CIS benchmark scanner (Aqua Security) - Trivy operator: Scans running workloads for misconfigurations and CVEs
Security Checklist¶
| Area | Recommendation |
|---|---|
| Authentication | Use OIDC for human users; disable client cert auth if not needed |
| RBAC | Apply least-privilege; audit bindings regularly |
| Pod Security | Enforce baseline or restricted via Pod Security Admission |
| Network | Apply default-deny NetworkPolicy; restrict egress |
| Secrets | Enable encryption at rest; use external secret stores for production |
| Admission | Deploy Kyverno or OPA Gatekeeper for custom policies |
| Images | Sign and verify images; restrict allowed registries |
| Audit | Enable audit logging with appropriate policy levels |
| Kubelet | Disable anonymous auth; set --read-only-port=0 |
| etcd | Enable TLS client authentication; restrict network access |