Skip to content

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-admin broadly. 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-config on 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-path and AuditPolicy on 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

References