CKS cheat sheet
CKS · Keep the key ideas within reach.
Cluster Setup
A pod is isolated for ingress only when a NetworkPolicy's podSelector matches it; any ingress not matched by an allow rule is then denied.
An empty podSelector {} selects every pod in the policy's namespace; an empty namespaceSelector {} selects every namespace, so both together mean all pods everywhere.
Cloud instance credentials are reachable at 169.254.169.254 from pods unless an egress NetworkPolicy denies that address; IMDSv2-only mode adds another layer.
kube-bench executes the CIS Kubernetes Benchmark checks against control plane binaries, config files and node settings, and exits non-zero when a check fails.
Encryption at rest requires an EncryptionConfiguration listing providers (aesgcm, aescbc, secretbox, identity) referenced by kube-apiserver --encryption-provider-config; pre-existing Secrets are only re-encrypted after a rewrite.
--anonymous-auth=false must be set on both kube-apiserver and kubelet; otherwise unauthenticated requests map to system:anonymous.
An Ingress TLS entry needs a Secret of type kubernetes.io/tls holding tls.crt and tls.key, referenced from spec.tls[].secretName.
Correct upgrade order: kubeadm upgrade plan, kubeadm upgrade apply on the first control plane node, drain and kubeadm upgrade node on remaining nodes, then upgrade kubelet and kubectl packages.
Only one minor version skew is supported between kube-apiserver and kubelet, and kubelet may never be newer than kube-apiserver.
Cluster Hardening
--authorization-mode=Node,RBAC delegates kubelet requests to the Node authorizer and all other requests to RBAC; ABAC and AlwaysAllow should not appear in the list.
The NodeRestriction admission plugin restricts a kubelet to its own Node object and to Pods already bound to that node, blocking label and taint tampering elsewhere.
A ClusterRoleBinding to cluster-admin grants unrestricted cluster-wide control; namespaced RoleBindings and narrowly scoped ClusterRoles keep blast radius small.
automountServiceAccountToken: false at Pod or ServiceAccount level removes the projected API token and therefore the credential an attacker can steal.
kubeadm certs check-expiration reports remaining validity, kubeadm certs renew all refreshes control plane certificates; CA certificates default to 10 years, other kubeadm certificates to 1 year.
Kubelet hardening baseline: --anonymous-auth=false, --authorization-mode=Webhook, --read-only-port=0, and client CA configured for certificate auth.
The Kubernetes Dashboard requires a service account token and has had privilege-escalation CVEs; if kept, expose it only behind an authenticating proxy.
system:masters group membership bypasses RBAC entirely, so audit who holds it and map external identity-provider groups carefully.
System Hardening
securityContext.seccompProfile.type=RuntimeDefault applies the runtime's default syscall allowlist; Localhost points to a JSON profile on the node.
AppArmor is selected with the container.apparmor.security.beta.kubernetes.io/ annotation on older clusters and securityContext.appArmorProfile from Kubernetes 1.30.
hostPath, hostNetwork, hostPID and hostIPC all cross the pod/node boundary; the baseline Pod Security level rejects hostPath, host namespaces and privileged containers.
The kubelet read-only port 10255 served pod specs without authentication when enabled; --read-only-port=0 closes it.
Node SSH should be key-based, reachable only from a bastion or session manager, with password auth and root login disabled.
Cloud node IAM roles should grant only the permissions the node needs, because any pod that reaches the metadata endpoint inherits them.
Disable unused kernel modules and filesystems via /etc/modprobe.d and modprobe blacklists, and apply sysctl settings such as kernel.kptr_restrict and net.ipv4.conf.all.accept_redirects=0.
A minimal node OS image with unused packages and services removed reduces both the patch burden and the exploitable surface.
Minimize Microservice Vulnerabilities
Baseline container SecurityContext: runAsNonRoot: true, allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities.drop: ["ALL"], and seccompProfile RuntimeDefault.
Pod Security Admission defines three levels (privileged, baseline, restricted) and three modes (enforce, audit, warn) configured by namespace labels such as pod-security.kubernetes.io/enforce=restricted.
restricted PSA requires non-root execution, dropped capabilities, RuntimeDefault or Localhost seccomp and no privilege escalation; privileged containers only pass under the privileged level.
Secrets are stored base64-encoded in etcd unless an encryption provider is configured; volume mounts avoid exposing them in env, process listings and crash dumps.
A default-deny NetworkPolicy per namespace plus explicit allow rules limits lateral movement; mTLS or a service mesh authenticates pod-to-pod traffic.
OPA Gatekeeper uses ConstraintTemplates containing Rego plus Constraints that select target resources; Kyverno expresses validate, mutate and generate rules directly in YAML.
RuntimeClass with gVisor or Kata Containers inserts an extra kernel or user-space boundary between the container and the host.
Resource requests and limits prevent a single workload from exhausting node CPU or memory and degrading neighbours.
Supply Chain Security
Image scanning (Trivy, Grype) should run in CI and continuously on registry contents; builds fail when fixable HIGH or CRITICAL CVEs are present.
cosign signs an image digest and stores the signature in the registry as an OCI artifact; admission policies such as Kyverno verifyImages or Connaisseur reject unsigned images.
Referencing image@sha256: pins the exact bytes, whereas tags are mutable and can be repointed at a different image.
ImagePullPolicy defaults to Always when the tag is :latest or omitted and to IfNotPresent otherwise; digest references with IfNotPresent avoid repeated pulls.
Distroless or scratch base images contain no shell or package manager, removing the tooling an attacker needs after code execution.
Multi-stage Dockerfiles keep compilers and build caches out of the runtime layer, and the final stage should run as a non-root USER.
Image pull credentials belong in a namespaced imagePullSecret or a node-level credential provider; registry push rights should be limited to the CI identity.
SBOM generation (syft) and provenance attestations (SLSA, cosign attest) let you trace which dependencies and build systems produced a deployed image.
Monitoring, Logging and Runtime Security
Falco is a CNCF runtime security project that reads syscalls from eBPF or a kernel module and fires alerts when events match its rules.
Falco's default ruleset flags a shell spawned inside a container, writes under /etc or /bin, access to /etc/shadow, and outbound connections to unexpected destinations.
Audit policy rule levels are None, Metadata, Request and RequestResponse, in increasing order of recorded request and response body detail.
Audit stages are RequestReceived, ResponseStarted, ResponseComplete and Panic; every emitted event carries its stage in the auditID-scoped record.
Audit backends are the log file (--audit-log-path with --audit-log-maxbackup, --audit-log-maxsize, --audit-log-maxage) and a webhook (--audit-webhook-config-file) for SIEM shipping.
kubectl logs shows only container stdout and stderr; node-level tooling such as journalctl or sysdig is needed for process and system call evidence.
sysdig captures live syscalls and container events on a node and is used for post-incident forensics after a suspected runtime compromise.
A read-only root filesystem combined with an image lacking a shell makes post-exploitation persistence and tooling drops much harder.