Default-deny NetworkPolicy — Deny all traffic in a namespace first, then allow only what is needed. `podSelector: {}` with policyTypes [Ingress, Egress] and no rules blocks everything for every Pod in the namespace. Add narrow allow policies (and DNS egress) on top. Docs
kube-bench — Checks a node against the CIS Kubernetes Benchmark. Runs on control-plane and worker nodes and reports FAIL/WARN items with remediation — e.g. API server flags, kubelet authentication, file permissions on /etc/kubernetes. Docs
Ingress TLS — spec.tls with a kubernetes.io/tls Secret terminates HTTPS at the Ingress. `kubectl create secret tls web-tls --cert=tls.crt --key=tls.key`, then list the hosts and secretName under spec.tls. Docs
Node metadata protection — Keep Pods away from the cloud metadata endpoint (169.254.169.254). The metadata service can hand out node credentials. Block it with an egress NetworkPolicy (ipBlock except) and restrict access to kubelet/node endpoints. Docs
Verify platform binaries — Compare kubelet/kubectl/kubeadm checksums with the official release. Download the published .sha256 (or .sha512) and check it with `sha256sum --check`; release artifacts are also signed and can be verified with cosign. Docs
Cluster Hardening (15%)
Least-privilege RBAC, careful ServiceAccounts, restricted API access, upgrades
Least-privilege RBAC — Grant the exact verbs on the exact resources, in the narrowest scope. Avoid wildcards and cluster-admin bindings; prefer Role over ClusterRole. Watch for escalation verbs: bind, escalate, impersonate, and create on pods/exec. Docs
automountServiceAccountToken — Set to false so Pods that never call the API carry no token. Every namespace has a default ServiceAccount. Do not grant it permissions; create dedicated ServiceAccounts and disable token automounting where it is not needed. Docs
Restrict API access — No anonymous auth, authorization-mode=Node,RBAC, no public API endpoint. Check kube-apiserver flags such as --anonymous-auth=false and --authorization-mode=Node,RBAC, and keep the API endpoint off the public internet (firewall / private endpoint). Docs
Kubernetes upgrades — Stay on supported minor versions to receive security fixes. The project supports the three most recent minor releases. Upgrade control plane first, then nodes, one minor version at a time. Docs
System Hardening (10%)
Small host OS, least-privilege IAM, less network exposure, AppArmor and seccomp
Minimal host OS — Remove packages, services and open ports the node does not need. Fewer binaries and listening services = smaller attack surface. Check with `ss -tlnp` and `systemctl list-units`; disable what is unused. Docs
AppArmor — Kernel profiles restricting what a container may read, write and execute. Load the profile on the node (`apparmor_parser`), then reference it in securityContext.appArmorProfile with type Localhost and localhostProfile. Docs
seccomp — Filters which syscalls a container may make. securityContext.seccompProfile type RuntimeDefault applies the runtime’s sane default; Localhost points at a custom JSON profile under the kubelet’s seccomp directory. Docs
Least-privilege IAM — Nodes and workloads get only the cloud and OS permissions they need. Avoid broad node IAM roles that every Pod could inherit via the metadata endpoint; use per-workload identities and limit who can SSH or sudo on nodes. Docs
Minimize Microservice Vulnerabilities (20%)
Pod Security Standards, Secrets, isolation and sandboxes, Pod-to-Pod encryption
Pod Security Standards — privileged, baseline, restricted — enforced per namespace by Pod Security Admission. Label a namespace `pod-security.kubernetes.io/enforce=restricted` (plus warn/audit modes). Restricted requires non-root, no privilege escalation, dropped capabilities and a seccomp profile. Docs
Encryption at rest — An EncryptionConfiguration so Secrets are encrypted inside etcd. Pass --encryption-provider-config to kube-apiserver (aescbc, aesgcm, secretbox or kms providers; identity = none). Existing Secrets are only encrypted once rewritten: `kubectl get secrets -A -o json | kubectl replace -f -`. Docs
RuntimeClass — Runs selected Pods in a sandbox such as gVisor (runsc) or Kata. Create a RuntimeClass whose handler maps to a configured runtime, then set spec.runtimeClassName on the Pod. Sandboxes add a kernel boundary between untrusted code and the host. Docs
Pod-to-Pod encryption — Encrypt traffic in transit with a mesh (Istio mTLS) or the CNI (Cilium WireGuard/IPsec). Istio PeerAuthentication mode STRICT forces mTLS between sidecars or ztunnels; Cilium can transparently encrypt node-to-node traffic with WireGuard or IPsec. Docs
Supply Chain Security (20%)
Small base images, SBOMs, allowed registries, signatures, static analysis
Minimal base image — distroless / scratch / alpine: fewer packages, fewer CVEs. Multi-stage builds copy only the binary into a tiny final image; pin by digest, run as a non-root USER, and leave shells and package managers out. Docs
SBOM — Software Bill of Materials: every package inside an artifact. Generated in formats like SPDX or CycloneDX (e.g. `trivy image --format spdx-json`, `syft`). Lets you answer "are we affected?" the day a new CVE drops. Docs
Image signing (cosign) — Sign images and only admit signed ones from allowed registries. `cosign sign` / `cosign verify` with Sigstore. Enforce allowed registries and signatures at admission time with an ImagePolicyWebhook, Kyverno or Gatekeeper. Docs
Trivy — Scans images (and more) for known vulnerabilities. `trivy image --severity HIGH,CRITICAL nginx:1.27` lists CVEs per package and the version that fixes them. Docs
Static analysis (kubesec / KubeLinter) — Flags risky manifest settings before they are applied. `kubesec scan pod.yaml` scores a manifest (privileged, hostPath, capabilities); KubeLinter checks for missing limits, root users, latest tags and more. Docs
Falco — Detects suspicious behavior from syscalls at runtime. Rules such as "Terminal shell in container" or "Write below /etc" fire on behavior, not signatures. Custom rules go in falco_rules.local.yaml; output names the Pod and container. Docs
Audit logging — An audit Policy records who did what to which object, and when. kube-apiserver flags --audit-policy-file and --audit-log-path (plus maxage/maxbackup). Levels: None, Metadata, Request, RequestResponse; the first matching rule wins. Docs
Immutable containers — readOnlyRootFilesystem: true, no shells, no runtime package installs. A container that cannot write to its filesystem cannot be modified by an attacker; give it emptyDir mounts only where it really needs to write. Docs
MITRE ATT&CK — A shared map of attacker tactics: initial access → execution → persistence → …. Use it to place what you see (an unexpected shell, a new privileged Pod, a token read) in a phase of the attack and decide what the actor will try next. Docs
Practice questions
A namespace needs a default-deny policy for both directions. Which policyTypes?
Answer: [Ingress, Egress] with no ingress or egress rules. Empty rule lists with both types deny everything for the selected Pods.
kube-bench reports that the API server profiling endpoint is enabled. Which flag fixes it?
Answer: --profiling=false. Profiling exposes debugging data; the CIS benchmark recommends turning it off.
How does kube-bench typically run against a cluster’s nodes?
Answer: As a Job (or binary) on each node, reading local config files. It inspects files and process flags on the node itself.
An Ingress must serve HTTPS for shop.example.com. Which is required?
Answer: A kubernetes.io/tls Secret referenced in spec.tls with the host listed. The controller terminates TLS with that certificate.
Which command verifies a downloaded kubectl against its published checksum file?
Answer: echo "$(cat kubectl.sha256) kubectl" | sha256sum --check. The release publishes .sha256 files next to each binary.
Why restrict Pod access to the kubelet’s port 10250 from inside the cluster?
Answer: The kubelet API can run commands in any Pod on that node. Combine kubelet authn/authz with NetworkPolicies or host firewalls.
How do you check what a ServiceAccount can do?
Answer: kubectl auth can-i --list --as=system:serviceaccount:<ns>:<name>. ServiceAccounts are users named system:serviceaccount:<namespace>:<name>.
Which ClusterRole should never be bound to application ServiceAccounts?
Answer: cluster-admin. cluster-admin grants every verb on every resource.
Why is the RBAC verb "*" on resource "*" dangerous even in a namespace?
Answer: It includes creating Pods, reading Secrets and modifying RBAC in that namespace. Grant explicit verbs on explicit resources.
Which RBAC permission lets a user run commands in containers?
Answer: create on pods/exec. Subresources like pods/exec, pods/attach and pods/portforward need explicit grants.
How do you stop the default ServiceAccount’s token from being mounted into Pods?
Answer: Set automountServiceAccountToken: false on the ServiceAccount (or each Pod). The default SA is recreated if deleted.
Which apiserver flag restricts which admission plugins run beyond the defaults?
Answer: --enable-admission-plugins. NodeRestriction is a common addition.
Upgrading control-plane nodes from 1.33 to 1.35 directly with kubeadm is…
Answer: Not supported — upgrade one minor version at a time. kubeadm supports skipping no minor versions: go 1.33 → 1.34 → 1.35, upgrading kubeadm first each time.
Which command shows listening TCP ports and the owning processes on a node?
Answer: ss -tlnp. Close or firewall anything the node does not need.
How do you list AppArmor profiles loaded on a node?
Answer: aa-status (or apparmor_status). Profiles in enforce or complain mode are listed.
Where does the kubelet look for Localhost seccomp profiles by default?
Answer: /var/lib/kubelet/seccomp. localhostProfile is a path relative to that directory.
Which is a good way to reduce a node’s attack surface?
Answer: Remove unused packages and services; Restrict SSH and use least-privilege accounts. Minimal host OSes ship only what is needed to run containers.
A namespace is labelled pod-security.kubernetes.io/enforce=restricted. A Pod without a seccompProfile is created. Result?
Answer: Rejected. Restricted requires seccompProfile RuntimeDefault or Localhost.
Which Pod Security level blocks privileged Pods and hostPath but still allows running as root?
Answer: baseline. baseline blocks known escalations; restricted also requires non-root.
Which EncryptionConfiguration provider means "store as plaintext"?
Answer: identity. The first provider in the list is used for writes; identity last allows reading old plaintext.
In an EncryptionConfiguration, which provider encrypts new writes?
Answer: The first one in the providers list. Order matters: put the real provider first, identity after it during migration.
Which external approach avoids storing secret values in etcd at all?
Answer: A CSI secrets store driver or external secrets operator backed by a vault. Values are fetched from the external store at runtime.
Which RuntimeClass field names the runtime handler configured in containerd?
Answer: handler. handler: runsc maps to the gVisor runtime in the containerd config.
Which Istio resource makes mTLS strictly required in a namespace?
Answer: PeerAuthentication with mtls.mode: STRICT. STRICT rejects plaintext traffic to the selected workloads.
How does Cilium encrypt Pod traffic between nodes transparently?
Answer: WireGuard or IPsec transparent encryption. Enabled in Cilium’s configuration, no application changes.
Why separate tenants onto different nodes with taints and node affinity?
Answer: So a container escape on one tenant’s node cannot reach another tenant’s workloads. Node isolation strengthens multi-tenancy beyond namespaces.
Which Trivy command fails a CI job when HIGH or CRITICAL vulnerabilities are found?
Answer: trivy image --severity HIGH,CRITICAL --exit-code 1 <image>. --exit-code makes the scan return non-zero when findings match.
Which formats are common for SBOMs?
Answer: SPDX; CycloneDX. Both are widely supported by scanners and generators such as Syft and Trivy.
What does `cosign verify --key cosign.pub <image>` check?
Answer: That the image has a valid signature made with the matching private key. Signatures prove origin and integrity, not safety.
Why use image digests in Pod specs for production?
Answer: The exact image that was scanned and signed is the one that runs. A tag can be repointed after verification.
Which admission plugin delegates image allow/deny decisions to an external webhook backend?
Answer: ImagePolicyWebhook. It is configured with an admission configuration file pointing at the backend.
What does the AlwaysPullImages admission plugin protect against?
Answer: Pods reusing a private image already cached on a node without having pull credentials. It forces imagePullPolicy: Always so registry credentials are checked every time.
Which practice in a Dockerfile reduces what ends up in the final image?
Answer: A multi-stage build that copies only the built artifact into a minimal base. Build tools stay in the builder stage.
kubesec scores a Pod manifest. Which setting lowers the score?
An audit policy has a first rule level: None for get on pods, and a later rule level: Metadata for all. What is logged for a pod get?
Answer: Nothing. Audit rules are evaluated in order; the first match wins.
Which kube-apiserver flags are needed to write audit logs to a file?
Answer: --audit-policy-file; --audit-log-path. Add --audit-log-maxage/maxbackup/maxsize to rotate them; mount the paths into the static Pod.
How do you find which containers in a namespace can write to their root filesystem?
Answer: Check securityContext.readOnlyRootFilesystem for each container. Containers without readOnlyRootFilesystem: true are mutable.
A container must be immutable but needs to write temporary files. What do you do?
Answer: readOnlyRootFilesystem: true plus an emptyDir mounted at /tmp. Only the specific writable paths are allowed.
In the attack lifecycle, what comes right after initial access?
Answer: Execution. MITRE ATT&CK orders tactics from reconnaissance and initial access to impact.
A suspicious binary appeared in a running container. What should incident response do first?
Answer: Isolate the Pod (e.g. NetworkPolicy) and preserve evidence before deleting it. Contain first, then investigate; deleting destroys evidence.
Which signal reveals a kubectl exec into a production Pod after the fact?
Answer: An audit log entry for create on pods/exec. Audit logs record the user, Pod and time.
kube-bench reports that the kubelet allows anonymous requests. What do you change?
Answer: Set authentication.anonymous.enabled: false in the kubelet config and restart kubelet. kube-bench maps each failing CIS check to its remediation; the kubelet reads authentication settings from its config file.
Pods must not reach the cloud metadata service at 169.254.169.254. What do you create?
Answer: An egress NetworkPolicy allowing 0.0.0.0/0 except 169.254.169.254/32. ipBlock with except carves the metadata IP out of allowed egress. RBAC only governs the Kubernetes API, not network traffic.
How do you confirm a downloaded kubelet binary is the official one?
Answer: Compare its sha256sum (or sha512sum) with the checksum published for that release. A version string is trivial to fake; a checksum (or signature) from the release proves the bits are unchanged.
Which kube-apiserver settings close the most obvious doors?
Answer: --anonymous-auth=false; --authorization-mode=Node,RBAC. Disable anonymous access and authorize through Node + RBAC. AlwaysAllow and AlwaysAdmit disable the checks entirely.
A Pod never talks to the Kubernetes API. What is the hardening step for its token?
Answer: Set automountServiceAccountToken: false (on the Pod or its ServiceAccount). A token that is not mounted cannot be stolen from the Pod. Deleting the default ServiceAccount just gets it recreated.
Apply the container runtime’s default syscall filter to a Pod. Which field?
Answer: securityContext.seccompProfile.type: RuntimeDefault. RuntimeDefault is the runtime’s curated seccomp profile; Unconfined turns filtering off.
You wrote an AppArmor profile "k8s-deny-write". What else is needed before Pods can use it?
Answer: Load it on every node that may run the Pod (apparmor_parser) and reference it as a Localhost profile. AppArmor profiles live in the node kernel. Kubernetes only references them; a Pod with an unloaded profile fails to start.
Make every new Pod in namespace "payments" meet the restricted Pod Security Standard. How?
Answer: kubectl label ns payments pod-security.kubernetes.io/enforce=restricted. Pod Security Admission reads namespace labels. PodSecurityPolicy was removed in v1.25.
After enabling an EncryptionConfiguration, existing Secrets are still plaintext in etcd. Why, and what fixes it?
Answer: Only writes get encrypted — rewrite them: kubectl get secrets -A -o json | kubectl replace -f -. The API server encrypts on write, so existing objects stay as they were until they are written again.
Untrusted workloads should run under gVisor. Which object connects a Pod to that runtime?
Answer: A RuntimeClass with handler runsc, referenced by spec.runtimeClassName. RuntimeClass maps a name to a CRI handler configured on the node.
Which practices shrink an image’s attack surface?
Answer: A multi-stage build that copies only the binary into a distroless image; Running as a non-root USER. Fewer packages means fewer CVEs and fewer tools for an attacker; non-root limits what a compromise can do.
Only images from registry.corp.example and signed by your key may run. Where is this enforced?
Answer: At admission: an ImagePolicyWebhook or a policy engine (Kyverno/Gatekeeper) verifying registry and signature. Admission control sees every Pod before it is stored, so it can reject untrusted images.
Which tool alerts when someone opens a shell inside a running container?
Answer: Falco. Falco watches syscalls at runtime. kube-bench checks configuration, Trivy and kubesec scan artifacts before deploy.
You must record the full request and response of every change to Secrets. Which audit level?
Answer: RequestResponse for resources: secrets. Metadata logs only who/what/when; RequestResponse includes the bodies. Mind the log’s sensitivity.
Which setting stops an attacker from dropping new binaries into a container’s filesystem?
Answer: readOnlyRootFilesystem: true. A read-only root filesystem keeps the container immutable; provide emptyDir mounts only where writes are needed.