Kubernetes security is complex because the attack surface spans multiple layers: containers, clusters, networks, and cloud infrastructure. This guide covers the most impactful security practices for production Kubernetes deployments.


Pod Security Standards


Kubernetes deprecated PodSecurityPolicies in favor of Pod Security Admission (PSA), which enforces three security levels:


  • **Privileged**: No restrictions (for system-level pods).
  • **Baseline**: Prevents known privilege escalations.
  • **Restricted**: Strong pod hardening.

  • Apply PSA via namespace labels:


    
    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
    
    

    The `restricted` level enforces that containers cannot run as root, cannot use host networking, and cannot mount arbitrary host paths.


    Running Containers as Non-Root


    Never run containers as root in production:


    
    apiVersion: v1
    
    kind: Pod
    
    metadata:
    
      name: secure-app
    
    spec:
    
      securityContext:
    
        runAsNonRoot: true
    
        runAsUser: 1000
    
        runAsGroup: 3000
    
        fsGroup: 2000
    
        seccompProfile:
    
          type: RuntimeDefault
    
      containers:
    
      - name: app
    
        image: myapp:1.0.0
    
        securityContext:
    
          allowPrivilegeEscalation: false
    
          capabilities:
    
            drop: ["ALL"]
    
          readOnlyRootFilesystem: true
    
    

    Use `seccompProfile: RuntimeDefault` to apply the container runtime's default seccomp profile. Drop all capabilities and only add back what is absolutely necessary.


    Network Policies


    By default, all pods can communicate with each other. Network policies restrict this:


    
    apiVersion: networking.k8s.io/v1
    
    kind: NetworkPolicy
    
    metadata:
    
      name: api-network-policy
    
      namespace: production
    
    spec:
    
      podSelector:
    
        matchLabels:
    
          app: api
    
      policyTypes:
    
      - Ingress
    
      - Egress
    
      ingress:
    
      - from:
    
        - podSelector:
    
            matchLabels:
    
              app: frontend
    
        ports:
    
        - port: 3000
    
      egress:
    
      - to:
    
        - podSelector:
    
            matchLabels:
    
              app: database
    
        ports:
    
        - port: 5432
    
    

    Start with a deny-all policy and add allow rules incrementally. Use namespace isolation for multi-tenant clusters.


    Role-Based Access Control (RBAC)


    Apply the principle of least privilege to all service accounts:


    
    apiVersion: v1
    
    kind: ServiceAccount
    
    metadata:
    
      name: app-sa
    
      namespace: production
    
    ---
    
    apiVersion: rbac.authorization.k8s.io/v1
    
    kind: Role
    
    metadata:
    
      namespace: production
    
      name: app-role
    
    rules:
    
    - apiGroups: [""]
    
      resources: ["pods"]
    
      verbs: ["get", "list", "watch"]
    
    - apiGroups: [""]
    
      resources: ["configmaps"]
    
      verbs: ["get"]
    
    ---
    
    apiVersion: rbac.authorization.k8s.io/v1
    
    kind: RoleBinding
    
    metadata:
    
      namespace: production
    
      name: app-role-binding
    
    subjects:
    
    - kind: ServiceAccount
    
      name: app-sa
    
      namespace: production
    
    roleRef:
    
      kind: Role
    
      name: app-role
    
      apiGroup: rbac.authorization.k8s.io
    
    

    Service accounts should only have permissions required for their specific function. Use `Role` for namespace-scoped access and `ClusterRole` only for cluster-wide resources.


    Secrets Management


    Kubernetes Secrets are base64-encoded, not encrypted by default. Enable encryption at rest:


    
    # Create encryption config
    
    cat > encryption-config.yaml <<EOF
    
    apiVersion: apiserver.config.k8s.io/v1
    
    kind: EncryptionConfiguration
    
    resources:
    
      - resources:
    
          - secrets
    
        providers:
    
          - kms:
    
              name: myKMS
    
              endpoint: unix:///var/run/kmsplugin/socket.sock
    
          - aescbc:
    
              keys:
    
                - name: key1
    
                  secret: $(openssl rand -base64 32)
    
    EOF
    
    

    For production, use external secrets management:


    
    apiVersion: external-secrets.io/v1beta1
    
    kind: ExternalSecret
    
    metadata:
    
      name: db-credentials
    
    spec:
    
      secretStoreRef:
    
        name: aws-secret-store
    
        kind: SecretStore
    
      target:
    
        name: db-credentials
    
      data:
    
      - secretKey: password
    
        remoteRef:
    
          key: production/db/password
    
    

    External Secrets Operator syncs secrets from AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault into Kubernetes Secrets.


    Image Security


    Only run images from trusted registries:


    
    apiVersion: v1
    
    kind: Pod
    
    spec:
    
      containers:
    
      - name: app
    
        image: registry.example.com/myapp:1.0.0
    
        imagePullPolicy: Always
    
    

    Use image scanning in CI/CD:


    
    # Scan container images before deployment
    
    trivy image registry.example.com/myapp:1.0.0
    
    

    Configure admission controllers to reject images with critical vulnerabilities.


    Resource Limits


    Set resource limits on every container to prevent resource starvation:


    
    resources:
    
      requests:
    
        memory: "256Mi"
    
        cpu: "250m"
    
      limits:
    
        memory: "512Mi"
    
        cpu: "500m"
    
    

    CPU limits throttle excessive usage, while memory limits terminate containers that exceed their allocation.


    Runtime Security


    Use Falco for runtime threat detection:


    
    helm install falco falcosecurity/falco \
    
      --set falco.driver.kind=modern_bpf
    
    

    Falco detects anomalous behavior: shell in a container, unexpected file system access, suspicious network connections. It can trigger alerts or kill offending pods.


    Audit Logging


    Enable Kubernetes audit logging:


    
    # kube-apiserver configuration
    
    apiServer:
    
      audit:
    
        enabled: true
    
        logPath: /var/log/kubernetes/audit.log
    
        policy:
    
          rules:
    
            - level: RequestResponse
    
              resources:
    
                - group: ""
    
                  resources: ["secrets"]
    
            - level: Metadata
    
              resources:
    
                - group: ""
    
                  resources: ["pods", "deployments"]
    
    

    Ship audit logs to a SIEM for analysis. Monitor for unauthorized API access attempts.


    Summary


    Kubernetes security requires defense in depth: Pod Security Admission for pod hardening, network policies for segmentation, RBAC for access control, external secrets for sensitive data, and runtime security for threat detection. Start with pod security -- run containers as non-root, drop capabilities, and enable seccomp. Add network policies for isolation. Finally, implement RBAC and secrets management. Most Kubernetes compromises exploit misconfigurations rather than software vulnerabilities -- hardening these configurations is the highest-impact security investment.