# Kubernetes Security Essentials

> **CNPA Domain:** Platform Observability, Security, and Conformance (20%) **Topic:** Kubernetes Security Essentials

## Overview

Kubernetes is a powerful platform, but its defaults are not production-secure. Platform engineering teams are responsible for hardening Kubernetes clusters and ensuring that every workload runs with the minimum necessary privileges. This article covers the essential Kubernetes security primitives: **RBAC**, **Pod Security Standards**, **Secrets management**, and **admission webhooks**.

***

## The 4Cs of Cloud Native Security

```
┌────────────────────────────────────────────────────────┐
│  Code (application vulnerabilities)                    │
│                                                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Container (image vulnerabilities, base images)  │  │
│  │                                                  │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │  Cluster (RBAC, PSS, network policies)     │  │  │
│  │  │                                            │  │  │
│  │  │  ┌──────────────────────────────────────┐  │  │  │
│  │  │  │  Cloud (IAM, VPC, node security)     │  │  │  │
│  │  │  └──────────────────────────────────────┘  │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────┘  │
└────────────────────────────────────────────────────────┘
```

Platform engineers own the **Cluster** layer and influence the **Container** and **Code** layers.

***

## Role-Based Access Control (RBAC)

RBAC controls **who can do what** in a Kubernetes cluster.

### Core RBAC Objects

| Object               | Scope     | Purpose                                        |
| -------------------- | --------- | ---------------------------------------------- |
| `Role`               | Namespace | Set of allowed API operations in one namespace |
| `ClusterRole`        | Cluster   | Set of allowed API operations cluster-wide     |
| `RoleBinding`        | Namespace | Grants a Role to a user/group/service account  |
| `ClusterRoleBinding` | Cluster   | Grants a ClusterRole cluster-wide              |

### Least Privilege Example

```yaml
# Grant payment team read-only access to their namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer-readonly
  namespace: payments-staging
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: payments-developers
  namespace: payments-staging
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: developer-readonly
subjects:
  - kind: Group
    name: payments-team   # From identity provider (OIDC)
    apiGroup: rbac.authorization.k8s.io
```

### Service Account Security

Every pod runs as a `ServiceAccount`. Restrict service accounts to minimum required permissions:

```yaml
# Dedicated service account with no default token
apiVersion: v1
kind: ServiceAccount
metadata:
  name: payment-service
  namespace: payments-production
automountServiceAccountToken: false  # Disable unless needed

---
# Grant only needed permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: payment-service-role
  namespace: payments-production
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["payment-config"]
    verbs: ["get"]
```

***

## Pod Security Standards (PSS)

**Pod Security Standards** replace the deprecated PodSecurityPolicy and define three security profiles:

| Profile        | Use Case                             | Key Restrictions                  |
| -------------- | ------------------------------------ | --------------------------------- |
| **Privileged** | System-level workloads (node agents) | No restrictions                   |
| **Baseline**   | General workloads                    | Blocks known privilege escalation |
| **Restricted** | High-security workloads              | Enforces security best practices  |

### Enforcing PSS via Namespace Labels

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: payments-production
  labels:
    # Enforce restricted profile — reject non-compliant pods
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    
    # Warn about violations in staging (informational only)
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    
    # Audit violations for reporting
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest
```

### What `restricted` Profile Requires

```yaml
spec:
  securityContext:
    runAsNonRoot: true          # ✅ Required
    runAsUser: 1000             # ✅ Non-zero UID
    seccompProfile:
      type: RuntimeDefault      # ✅ Required
  containers:
    - securityContext:
        allowPrivilegeEscalation: false  # ✅ Required
        capabilities:
          drop: [ALL]                    # ✅ Required
        readOnlyRootFilesystem: true     # ✅ Recommended
```

***

## Secrets Management

Kubernetes `Secrets` are base64-encoded (not encrypted) by default. Platform teams must ensure secrets are protected:

### Encryption at Rest

Enable etcd encryption for Secret resources:

```yaml
# /etc/kubernetes/enc/enc.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources: [secrets]
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-32-byte-key>
      - identity: {}
```

### External Secrets Operator

The best practice is to **not store secrets in Kubernetes at all** — reference them from an external secrets manager:

```yaml
# ExternalSecret: pull from AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-db-credentials
  namespace: payments-production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: payment-db-secret   # Creates this Kubernetes Secret
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/payments/db
        property: password
```

***

## Admission Webhooks for Security

Admission webhooks (covered in [Policy Engines](https://blog.htunnthuthu.com/getting-started/fundamentals/platform-engineering-101/platform-engineering-101-policy-engines)) enforce security policies at the API layer. Key security-focused policies:

```yaml
# Kyverno: disallow privileged containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged-containers
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-privileged
      match:
        any:
          - resources:
              kinds: [Pod]
      validate:
        message: "Privileged containers are not allowed."
        pattern:
          spec:
            =(initContainers):
              - =(securityContext):
                  =(privileged): "false"
            containers:
              - =(securityContext):
                  =(privileged): "false"
```

***

## Node Security

Platform teams also harden the nodes themselves:

| Control               | Implementation                                                          |
| --------------------- | ----------------------------------------------------------------------- |
| **Node IAM**          | Use IRSA (AWS) / Workload Identity (GCP) instead of node instance roles |
| **Node hardening**    | CIS Kubernetes Benchmark, minimal OS (Bottlerocket, Flatcar)            |
| **Node isolation**    | Taints and node selectors for sensitive workloads                       |
| **Container runtime** | Use gVisor or Kata Containers for strong isolation                      |

***

## Security Scanning

| Tool          | Scans                                        | Notes               |
| ------------- | -------------------------------------------- | ------------------- |
| **Trivy**     | Container images, Helm charts, IaC           | CNCF project        |
| **Kubescape** | Cluster configuration against NSA/MITRE/CIS  | Platform assessment |
| **Falco**     | Runtime threat detection (syscall-level)     | CNCF graduated      |
| **Checkov**   | IaC files (Terraform, Helm, Kubernetes YAML) | CI integration      |

***

## Key Takeaways

* **RBAC** controls who can access what — always enforce least privilege via `Role`/`RoleBinding`, not `ClusterRoleBinding`
* **Pod Security Standards** (`restricted` profile) prevent privilege escalation, require non-root containers, and enforce security contexts
* **Secrets** should be encrypted at rest in etcd and ideally managed by an external secrets manager (Vault, AWS Secrets Manager)
* **Admission webhooks** (Kyverno/Gatekeeper) enforce security policies at the API layer before resources are created
* Use **Trivy** and **Kubescape** for continuous security scanning of images and cluster config

***

## Further Reading

* [Kubernetes RBAC Documentation](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
* [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
* [External Secrets Operator](https://external-secrets.io/)
* [Falco Documentation](https://falco.org/docs/)
* → Next: [Security in CI/CD Pipelines](https://blog.htunnthuthu.com/getting-started/fundamentals/platform-engineering-101/platform-engineering-101-security-cicd)
