Search…

Kubernetes security

In this series (14 parts)
  1. Why Kubernetes exists
  2. Kubernetes architecture
  3. Core Kubernetes objects
  4. Kubernetes networking
  5. Storage in Kubernetes
  6. Kubernetes configuration and secrets
  7. Resource management and autoscaling
  8. Kubernetes workload types
  9. Kubernetes observability
  10. Kubernetes security
  11. Helm and package management
  12. GitOps with ArgoCD
  13. Kubernetes cluster operations
  14. Service mesh concepts

Prerequisite: Kubernetes observability.

A running cluster is an attack surface. Every pod, service account, and open port is a potential entry point. Kubernetes ships with security primitives, but none enforce least privilege by default. You configure them deliberately or they do nothing.

This article covers RBAC, PodSecurity Standards, network policies, admission controllers, and audit logging. For a broader perspective, see K8s security in depth.


RBAC: Role-Based Access Control

RBAC binds subjects (users, groups, service accounts) to permissions defined in Roles or ClusterRoles.

flowchart LR
  U["User / ServiceAccount"] -->|bound by| RB["RoleBinding"]
  RB -->|references| R["Role"]
  R -->|grants| P["Permissions on Resources"]
  U2["User / ServiceAccount"] -->|bound by| CRB["ClusterRoleBinding"]
  CRB -->|references| CR["ClusterRole"]
  CR -->|grants| CP["Cluster-wide Permissions"]

Roles are namespace-scoped. ClusterRoles apply across the entire cluster.

A Role defines permissions within a namespace. A RoleBinding attaches it to a subject.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: app
  name: pod-reader
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: app
  name: read-pods-binding
subjects:
  - kind: User
    name: developer
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

The user developer can now list pods in the app namespace. Nothing else. ClusterRoles work identically but span all namespaces. Use them for cluster-wide resources like nodes. Grant the minimum verbs required. Avoid wildcards in production.


ServiceAccounts

Every pod runs as a ServiceAccount. If you skip specifying one, the pod uses the default account, which may carry more permissions than intended.

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: app
  name: backend-sa
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: app
  name: backend-configmap-reader
subjects:
  - kind: ServiceAccount
    name: backend-sa
    namespace: app
roleRef:
  kind: Role
  name: configmap-reader
  apiGroup: rbac.authorization.k8s.io

Setting automountServiceAccountToken: false prevents the token from being mounted automatically. This limits blast radius if a pod is compromised. Override it per-pod only when the workload actually needs API access.


PodSecurity Standards

PodSecurity Standards replace the deprecated PodSecurityPolicy. Three profiles control what a pod is allowed to do.

ProfilePurpose
PrivilegedNo restrictions. System-level workloads only.
BaselineBlocks known privilege escalations. Good default.
RestrictedMaximum lockdown. Non-root, dropped capabilities.

Enforce at the namespace level with labels:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Any pod violating the restricted profile is rejected. The audit and warn modes log violations without blocking, useful during migration.


Network Policies: Zero-Trust Networking

By default, every pod can reach every other pod. Network policies change that. Start with deny-all, then open specific paths.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  namespace: app
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  namespace: app
  name: allow-frontend-to-backend
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

The first policy blocks everything. The second opens one path: frontend pods can reach backend on port 8080. Network policies require a CNI plugin that supports them, such as Calico or Cilium. The default kubenet plugin ignores them silently.


Admission Controllers

Admission controllers intercept API requests after authentication but before persistence. They validate or mutate objects.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: deny-root-pods
webhooks:
  - name: deny-root.example.com
    admissionReviewVersions: ["v1"]
    sideEffects: None
    clientConfig:
      service:
        name: admission-webhook
        namespace: kube-system
        path: /validate
      caBundle: <base64-encoded-ca-cert>
    rules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
    failurePolicy: Fail

Setting failurePolicy: Fail rejects requests when the webhook is unreachable. Safer than Ignore. Tools like OPA Gatekeeper and Kyverno let you write custom policies as code.


Audit Logging

Audit logs record every request to the API server. Who did what, when, to which resource.

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: Metadata
    resources:
      - group: ""
        resources: ["secrets", "configmaps"]
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["pods"]
    verbs: ["create", "delete"]
  - level: None
    resources:
      - group: ""
        resources: ["events"]

Four levels: None, Metadata, Request, RequestResponse. Log secrets at Metadata only so you track access patterns without recording values. Pass the policy to the API server with --audit-policy-file.


What comes next

RBAC, PodSecurity Standards, network policies, and admission controllers form the foundation. Layer them for defense in depth. Security is continuous, not a one-time configuration.

The next article covers Helm and package management, where you will template and distribute these manifests as reusable charts.

Start typing to search across all content
navigate Enter open Esc close