Enforce strong authentication for all API server requests.
Set `kube-apiserver` flags: `--anonymous-auth=false` to reject unauthenticated requests, and `--client-ca-file` to enforce client certificate validation (mTLS).
Why: These flags are fundamental controls to eliminate anonymous access and enforce authenticated, encrypted communication with the API server.
Secure the cluster's etcd state store from unauthorized access and data theft.
Configure etcd with mTLS for all client and peer communication (`--cert-file`, `--key-file`, `--peer-cert-file`). Enable secrets-at-rest encryption via the API server's `--encryption-provider-config` flag.
Why: etcd contains all cluster secrets. Encrypting data in transit (mTLS) and at rest is critical to protect sensitive information even if etcd nodes are compromised.
Rotate etcd encryption-at-rest keys with zero downtime.
1. Add the new key as the first entry in the `EncryptionConfiguration` file. 2. Restart all API servers. 3. Force re-encryption of all secrets (`kubectl get secrets -A -o json | kubectl replace -f -`). 4. After verification, remove the old key from the configuration and restart API servers again.
Why: Changing the configuration only affects new writes. Existing data must be rewritten to be encrypted with the new key. Removing the old key prematurely will lock you out of your data.
Implement a zero-trust network model within a namespace.
Apply a `NetworkPolicy` with an empty `podSelector: {}` and `policyTypes: [Ingress, Egress]` but no `ingress` or `egress` rules. This selects all pods and denies all traffic.
Why: This policy establishes a "deny-all" baseline, forcing explicit "allow" rules for all required communication, which is the foundation of zero-trust networking.
Egress NetworkPolicies are blocking DNS resolution for pods.
Add a specific egress rule to allow traffic to the cluster DNS service. Allow egress to port 53 on both UDP and TCP protocols. Select the kube-dns pods via `namespaceSelector` and `podSelector` if possible.
Why: NetworkPolicies are granular. A general egress rule to an IP block might not cover the specific protocol (UDP) required for DNS, leading to resolution failures.
Secure external access to services exposed via an Ingress.
Configure the Ingress resource by adding a `tls` section that references a Kubernetes Secret of type `kubernetes.io/tls`. The secret must contain the TLS certificate and private key.
Why: This centralizes TLS termination at the Ingress controller, encrypting traffic from clients to the cluster boundary and simplifying certificate management for backend services.
Cluster Hardening
Grant users or applications only the minimum required permissions.
Use namespace-scoped `Roles` and `RoleBindings` where possible. Avoid `cluster-admin` and wildcards (`"*"`) in `verbs` or `resources`. Grant specific permissions like `["get", "list"]` on `["pods"]`.
Why: This minimizes the blast radius if an account or token is compromised, preventing lateral movement and privilege escalation.
Reduce the attack surface for pods that do not need to interact with the Kubernetes API.
Disable automatic mounting of service account tokens by setting `automountServiceAccountToken: false` on the ServiceAccount or in the Pod spec.
Why: If a pod is compromised, an attacker cannot leverage a mounted token to access the API server, preventing cluster-level attacks originating from the compromised pod.
Verify if a specific user or service account has permission to perform an action.
Use `kubectl auth can-i <verb> <resource> --as=<user>` or `kubectl auth can-i <verb> <resource> --as=system:serviceaccount:<ns>:<sa-name>`.
Why: This command allows impersonation to accurately check effective permissions without needing to manually parse all Roles and Bindings.
Maintain cluster security by regularly rotating control plane and kubelet certificates.
For kubeadm clusters, use `kubeadm certs renew all`. For others, follow the documented manual or automated rotation procedure. Enable kubelet client/server certificate rotation via its configuration.
Why: Regular rotation limits the time window an attacker can use a compromised certificate. It is a critical security hygiene practice.
A worker node is suspected to be compromised and must be immediately isolated.
First, use `kubectl cordon <node-name>` to prevent new pods from being scheduled. Then, use `kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data` to safely evict running workloads.
Why: Cordoning and draining is the standard, non-destructive procedure to remove a node from service, allowing workloads to be rescheduled elsewhere while preserving the compromised node for forensic analysis.
System Hardening
Restrict the system calls a container can make to the host kernel.
In the Pod or Container `securityContext`, set `seccompProfile.type` to `RuntimeDefault` for a safe baseline, or `Localhost` with a path to a custom-defined JSON profile for stricter control.
Why: Seccomp reduces the kernel attack surface from within a container, preventing exploits against kernel vulnerabilities by blocking unused or dangerous syscalls.
Confine container processes by restricting access to files, network capabilities, and other resources.
Apply an AppArmor profile to a container via the annotation: `container.apparmor.security.beta.kubernetes.io/<container_name>: localhost/<profile_name>`. The profile must be pre-loaded on the node.
Why: AppArmor provides Mandatory Access Control (MAC), adding a crucial layer of defense-in-depth to contain a compromised application and prevent it from accessing unauthorized resources.
A container needs a specific privileged operation (e.g., binding to port 80) without running as root.
In the `securityContext.capabilities`, `drop: ["ALL"]` and then `add: ["NET_BIND_SERVICE"]`.
Why: This follows the principle of least privilege by granting only the specific Linux capability required, avoiding the broad permissions of running as root.
Prevent a container from gaining full access to the host system.
Set `securityContext.privileged: false` (which is the default). Use Pod Security Standards or OPA/Kyverno to enforce this cluster-wide.
Why: A privileged container has nearly all host capabilities and device access, effectively disabling container isolation. It is a primary vector for container escape.
Minimize Microservice Vulnerabilities
Enforce baseline or strict security configurations for pods at a namespace level.
Apply labels to the namespace, e.g., `pod-security.kubernetes.io/enforce: restricted`. The modes are `enforce`, `audit`, and `warn`.
Why: Pod Security Standards (PSS) provide a built-in, multi-level security policy that replaces the deprecated PodSecurityPolicy, making it easy to enforce security best practices.
Harden a pod by applying multiple security controls simultaneously.
Configure a `securityContext` that combines `runAsNonRoot: true`, `allowPrivilegeEscalation: false`, and `readOnlyRootFilesystem: true`.
Why: This defense-in-depth approach layers multiple protections: preventing root execution, blocking privilege escalation vectors (like setuid), and making the container filesystem immutable.
Run untrusted or multi-tenant workloads with stronger isolation than standard containers.
Define a `RuntimeClass` resource pointing to a sandboxed runtime handler (e.g., gVisor, Kata Containers). Assign pods to it using `spec.runtimeClassName`.
Why: Sandboxed runtimes use a user-space kernel or lightweight VMs to intercept syscalls, providing an extra layer of isolation between the container and the host kernel.
Enforce complex, custom security policies that are not covered by standard Kubernetes controls.
Deploy OPA Gatekeeper. Define policies using `ConstraintTemplate` (the Rego logic) and apply them with `Constraint` resources.
Why: Gatekeeper acts as a validating admission webhook, allowing you to enforce arbitrary rules, such as requiring specific labels, disallowing host paths, or enforcing resource limits.
Provide secrets to pods in the most secure manner.
Mount secrets as files into a volume. For even greater security, use a secrets-store CSI driver to mount secrets from an external vault (e.g., HashiCorp Vault, AWS Secrets Manager) directly into the pod.
Why: Mounting as files is more secure than environment variables (which can be logged or exposed). A CSI driver avoids storing the secret in etcd at all.
Encrypt and authenticate all pod-to-pod network traffic automatically.
Deploy a service mesh like Istio or Linkerd. The mesh injects a sidecar proxy into each pod to handle mTLS encryption, authentication, and policy enforcement.
Why: A service mesh provides transparent, zero-trust networking without requiring any application code changes, securing all internal service communication.
Supply Chain Security
Prevent deployment of container images with known vulnerabilities (CVEs).
Integrate a scanner like Trivy or Grype into the CI/CD pipeline. Fail the build if vulnerabilities exceed a defined severity threshold (e.g., HIGH or CRITICAL).
Why: This "shift-left" approach catches vulnerabilities early, before they reach production, drastically reducing the attack surface of running applications.
Ensure that only trusted, unmodified container images are deployed to the cluster.
Sign images with `cosign` during the CI build process. Use a policy engine (Kyverno, OPA Gatekeeper) as an admission controller to verify the signature against a public key before allowing a pod to be created.
Why: Cryptographic signing provides strong guarantees of image integrity (it hasnβt been tampered with) and authenticity (it came from a trusted source).
Minimize the attack surface within a container image itself.
Use minimal base images (e.g., distroless, Alpine). Use a multi-stage Dockerfile to discard build tools. Set a non-root user with the `USER` instruction. Use `.dockerignore` to exclude sensitive files.
Why: A minimal image contains fewer packages and tools, offering fewer potential vulnerabilities and making it harder for an attacker to pivot if the container is compromised.
Enforce a policy that all deployed images must originate from the organization's private registry.
Use an admission controller (like OPA Gatekeeper or Kyverno) to create a policy that validates the `image` field of all container specs against an allow-list of registry hostnames.
Why: This prevents developers from pulling untrusted or un-scanned images from public repositories like Docker Hub, ensuring all code has passed through internal security checks.
Maintain an inventory of all software components and dependencies within a container image.
Integrate a tool like `Syft` into the CI/CD pipeline to generate a Software Bill of Materials (SBOM) in a standard format like SPDX or CycloneDX.
Why: An SBOM is essential for supply chain security, enabling rapid identification of all affected assets when a new vulnerability is discovered in a dependency.
Identify security misconfigurations in Kubernetes YAML manifests before they are applied.
In the CI pipeline, use a tool like `trivy config` or `kubesec` to scan Kubernetes manifest files for risky configurations, such as running as root, allowing privilege escalation, or mounting sensitive host paths.
Why: This proactive check catches security issues in infrastructure-as-code before they create vulnerabilities in the running cluster.
Monitoring, Logging and Runtime Security
Detect and alert on suspicious activity inside running containers or on cluster nodes.
Deploy Falco as a DaemonSet. Falco uses eBPF or a kernel module to monitor system calls and alerts on anomalous behavior based on its ruleset (e.g., shell in container, unexpected network connections).
Why: Falco provides real-time visibility into runtime behavior, enabling detection of threats like container escapes, cryptomining, or data exfiltration that static scanning cannot see.
A default Falco rule is generating too many false positives.
Create a custom Falco rules file to override the default rule. Add exceptions to the rule's `condition` to exclude known-good behavior, such as specific processes or container images (e.g., `and not container.image.repository contains "debug"`).
Why: Tuning rules is critical for operationalizing runtime security. Reducing noise ensures that security teams can focus on actionable, high-priority alerts.
Record a chronological, immutable log of all actions taken against the Kubernetes API.
Enable audit logging on the `kube-apiserver` by providing `--audit-policy-file` and `--audit-log-path` flags. Configure the policy to define what gets logged and at what level.
Why: Audit logs are essential for security analysis, incident investigation, and compliance. They provide a definitive record of who did what, and when.
Audit access to sensitive resources like Secrets without logging the secret content itself.
Configure the audit policy rule for Secrets to use `level: Metadata`. This logs the user, timestamp, resource, and verb, but omits the request and response bodies.
Why: This provides accountability for who is accessing secrets without creating a new security risk by writing sensitive data into the audit logs.
Aggregate logs from all cluster components and applications for centralized analysis.
Deploy a log collection agent (e.g., Fluentd, Vector) as a DaemonSet to collect logs from nodes and forward them to a centralized SIEM or log management system (e.g., Elasticsearch, Splunk).
Why: Centralized logging is crucial for correlating events across the cluster during an incident investigation and for maintaining long-term records for compliance.
Forward Falco security alerts to an external system for notification and response.
Deploy `Falcosidekick` alongside Falco. Configure it to receive alerts from Falco and forward them to outputs like Slack, PagerDuty, or a SIEM.
Why: Falcosidekick provides a flexible and robust mechanism for integrating Falco's real-time alerts into existing operational and security workflows.
Detect if a running container has been modified, which could indicate a compromise.
Enforce immutable containers with `readOnlyRootFilesystem: true`. Use a runtime security tool like Falco to monitor for and alert on any file writes to unexpected locations.
Why: In an immutable model, containers are never changed at runtime; they are replaced. Any deviation from this pattern is a strong indicator of a potential security breach.