A misconfigured Kubernetes cluster is one of the fastest ways to hand an attacker a foothold in production. Container security in 2026 is no longer a checkbox for compliance audits, it is the difference between a contained incident and a full cluster compromise. This tutorial walks through hardening a real Kubernetes cluster running Kubernetes 1.36.3, the current stable release, using the tools most security teams already run: Trivy, Cosign, Kyverno, Falco, and cert-manager. By the end you will have a working, layered container security stack you can deploy today.
This is a hands-on tutorial, not a theory piece. You’ll run twelve concrete steps against a live cluster, from an initial RBAC audit through runtime threat detection, and finish with a complete manifest set you can drop into a real namespace. Expect to spend around 90 minutes end to end if you’re working through it for the first time, less if you’re already comfortable with kubectl and Helm. Each tool version below is the current stable release as of August 20, 2026, pinned deliberately, since container security tooling itself has shipped CVE fixes and even a supply-chain incident this year, which we cover in Step 6.
Why Container Security Matters for Kubernetes 1.36 Clusters
Kubernetes 1.36.3 is the latest patch on the current stable line, with active support running through June 28, 2027, according to the Kubernetes release schedule. The next minor release, 1.37, is scheduled for August 26, 2026, which means most production clusters today are still running 1.34, 1.35, or 1.36. That spread matters for security teams because each of those versions carries a different baseline of built-in protections.
Since Kubernetes 1.30, several security-relevant features have graduated to general availability: AppArmor as a native field in securityContext, CEL-based admission control, and CEL match conditions for webhooks that shrink the attack surface of custom admission logic. None of these features protect you automatically. You still have to configure Pod Security Admission, write network policies, scan every image, and watch runtime behavior. Container security fails most often not because the tooling is missing, but because teams install a scanner or policy engine once and never operationalize it. This guide treats container security as a pipeline, not a one-time task, and covers the full loop: prevent, detect, and respond.
The OWASP Kubernetes Top 10 and the official Kubernetes security checklist both converge on the same core controls covered here: RBAC least privilege, Pod Security Standards, network segmentation, image provenance, and runtime detection. We will implement each one with current tool versions.
It also helps to think about where each control sits in the request lifecycle. Some tools act once, at build time, and never touch the cluster again. Others sit in the admission chain and evaluate every single object before it’s persisted to etcd. A third group runs continuously once a pod is already scheduled, watching behavior long after the initial checks have passed. Teams that only invest in the first category, build-time scanning, end up with a false sense of coverage, because nothing stops a compromised process from doing damage once it’s already running. This tutorial deliberately spans all three categories so you don’t end up with that gap.
Prerequisites: Tools and Versions You’ll Need
Before you start, provision a test or staging cluster. Do not run these steps against production on the first pass. You’ll need cluster-admin access to apply RBAC changes, admission policies, and namespace labels.
| Tool | Version used in this guide | Purpose |
|---|---|---|
| Kubernetes | 1.36.3 | Cluster runtime, active support to Jun 2027 |
| kubectl | Matches cluster minor version | CLI cluster management |
| Trivy | 0.71.2 | Image and IaC vulnerability scanning |
| Cosign | 3.0.6 | Image signing and verification (Sigstore) |
| Kyverno | 1.18.2 | Policy-as-code admission control |
| OPA Gatekeeper | 3.23.0 | Alternative policy engine (constraint-based) |
| Falco | 0.44.1 | Runtime threat detection |
| cert-manager | 1.21.1 | TLS certificate automation |
| Helm | 3.x (latest) | Package manager for installing the above |
You’ll also need a container registry you control (Docker Hub, ECR, GCR, or a private registry), a CI/CD pipeline where you can wire in scanning gates, and roughly 90 minutes if you follow every step in order. Each step builds on the last, so don’t skip the RBAC and Pod Security Admission steps just because scanning feels more urgent. A perfectly scanned image running as root with a wildcard ClusterRoleBinding is still a serious risk.
A note on scope before you start: this guide assumes a single cluster you administer directly, whether that’s a local kind or minikube cluster for practice, or a real staging environment. If you’re on a managed offering, you can skip the control-plane flags in Step 2, since your provider manages those, but every other step, RBAC, Pod Security Standards, network policy, scanning, signing, policy enforcement, secrets, and runtime detection, is still entirely on you. None of it comes configured by default.
Step 1: Audit Your Cluster’s Current Security Posture
Start by measuring where you stand. Run the CIS Kubernetes Benchmark against your cluster using kube-bench, which checks control plane and node configuration against the published CIS controls. Pair it with a quick RBAC audit to catch the most common misconfiguration: overly broad permissions granted during initial setup and never revisited.
kubectl get clusterrolebindings -o json | \
jq -r '.items[] | select(.subjects != null) | select(.roleRef.name=="cluster-admin") | .metadata.name'
kubectl get pods --all-namespaces -o json | \
jq -r '.items[] | select(.spec.securityContext.runAsNonRoot != true) | .metadata.namespace + "/" + .metadata.name'
The first command lists every ClusterRoleBinding tied to cluster-admin, which should be a short, deliberate list, not something a CI service account picked up by accident. The second flags pods that don’t explicitly set runAsNonRoot, a strong early indicator of weak container security hygiene. Save the output. You’ll use it as your baseline to measure progress after applying the rest of this guide.
Most teams running this audit for the first time find at least one surprise: a debugging ClusterRoleBinding nobody removed after an incident two years ago, or a batch job namespace where every pod runs as root because it was faster to ship that way under a deadline. That’s normal. The point of this step isn’t to fix everything immediately, it’s to get an honest inventory before you start layering in enforcement, so you know which namespaces need the most attention first.
Step 2: Harden the API Server and Control Plane
The API server is the front door to your cluster. Per the current Kubernetes security checklist, anonymous authentication should be disabled, the API server should not be reachable from the public internet, and audit logging should be enabled with a policy that captures at minimum request metadata for all verbs. If you manage your own control plane rather than using a managed service, check these flags on the kube-apiserver process:
--anonymous-auth=false
--audit-log-path=/var/log/kubernetes/audit.log
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
--authorization-mode=Node,RBAC
--profiling=false
Also confirm the kube-controller-manager runs with --use-service-account-credentials enabled and that the system:masters group is never used for day-to-day authentication beyond initial cluster bootstrap. If you’re on a managed offering like EKS, AKS, or GKE, most of these are set by default, but verify with your provider’s security documentation since defaults do shift between versions. Managed control planes still leave RBAC, network policy, and workload security entirely up to you.
If you do run your own control plane, protect etcd as carefully as the API server itself. Isolate it on a private network, require mutual TLS for client connections, and enable encryption at rest for Secrets specifically, since etcd stores every Secret’s value in plaintext by default unless you configure an EncryptionConfiguration. A compromised etcd backup with no encryption at rest hands an attacker every credential in the cluster in one file.
Step 3: Enforce Pod Security Standards
Pod Security Admission replaced the deprecated PodSecurityPolicy and is enforced through namespace labels rather than a separate resource type. There are three defined levels, and every namespace in your cluster should have one applied deliberately rather than inheriting the cluster default by accident.
| Level | What it allows | Recommended use |
|---|---|---|
| Privileged | Unrestricted, full host access | System namespaces only (kube-system) |
| Baseline | Blocks known privilege escalations | Minimum for any application namespace |
| Restricted | Enforces current pod hardening best practices | Production workloads, especially internet-facing |
Apply the Restricted profile to a namespace with three labels, no separate controller needed:
kubectl label namespace payments \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restricted
Roll this out namespace by namespace, starting with the Baseline profile if Restricted breaks existing workloads immediately. Fixing pods to run non-root with dropped capabilities usually takes longer than flipping a label, so budget time for it rather than enforcing Restricted everywhere on day one.
Step 4: Lock Down RBAC and Service Accounts
Every pod gets a default ServiceAccount token mounted automatically unless you disable it, even if the application never calls the Kubernetes API. That token becomes a lateral-movement path the moment a container is compromised. Disable auto-mounting for workloads that don’t need API access, and scope every custom ServiceAccount to the narrowest role it actually requires.
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-app
namespace: payments
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: payments
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
Never grant a Role with a wildcard verb ("*") or wildcard resource unless you have a documented reason, and audit every ClusterRoleBinding at least twice a year. A single overlooked cluster-admin binding on a CI service account is a common root cause in incident reports, because it turns a compromised build pipeline into a full cluster compromise in one step.
Step 5: Apply Default-Deny Network Policies
By default, every pod in Kubernetes can talk to every other pod, across namespaces, with no restriction. That flat network is one of the biggest gaps in container security for teams that only think about the container layer and skip the network layer. Start every namespace with a default-deny policy, then explicitly allow the traffic paths your application actually needs.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-api
namespace: payments
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8443
This requires a CNI plugin that actually enforces NetworkPolicy resources, such as Calico, Cilium, or the network policy engine bundled with most managed Kubernetes offerings. Some default CNI configurations accept NetworkPolicy objects without enforcing them, silently, so verify enforcement works by testing a blocked connection before you rely on it in production.
Test enforcement with a throwaway pod before you trust the policy in production: kubectl run test --rm -it --image=busybox -n payments -- wget -T 3 http://some-other-service should time out once default-deny is active, unless you’ve explicitly allowed that path. If the connection succeeds anyway, your CNI isn’t enforcing NetworkPolicy objects and the YAML above is giving you a false sense of security. Fix that gap before moving on, since every later step assumes network segmentation is actually in force.
Step 6: Scan Container Images for Vulnerabilities with Trivy
Trivy 0.71.2 is the current stable release, and its recent history is itself a container security lesson worth knowing. Earlier in 2026, malicious binaries were briefly published under the v0.69.4 to v0.69.6 tags in a supply-chain compromise of Trivy’s own release pipeline. The project responded with v0.70.0, which rotated GPG signing keys, and 0.71.x now ships build provenance attestations for release artifacts. Pin your scanner version and verify its signature just like you would any other dependency, the tool that checks your supply chain is part of your supply chain.
trivy image --severity HIGH,CRITICAL --exit-code 1 \
registry.example.com/payments/web-app:1.4.2
Sample output from a scan with real findings looks like this:
registry.example.com/payments/web-app:1.4.2 (alpine 3.20.3)
=============================================================
Total: 3 (HIGH: 2, CRITICAL: 1)
+-------------+----------------+----------+----------+---------------+
| Library | Vulnerability | Severity | Installed| Fixed Version |
+-------------+----------------+----------+----------+---------------+
| openssl | CVE-2026-XXXXX | CRITICAL | 3.3.1-r0 | 3.3.2-r0 |
| libcurl | CVE-2026-XXXXX | HIGH | 8.9.0-r1 | 8.9.1-r1 |
+-------------+----------------+----------+----------+---------------+
The --exit-code 1 flag is what makes this a real gate rather than a report nobody reads. Wire that command into your CI pipeline so a build fails on HIGH or CRITICAL findings before the image ever reaches a registry, not after it’s already running in a namespace.
Step 7: Sign and Verify Images with Cosign
Scanning tells you what’s inside an image. Signing tells you the image actually came from your pipeline and wasn’t swapped for something else between build and deploy. Cosign 3.0.6, part of the Sigstore project, is largely compatible with the earlier v2.6.x line but turns on the newer bundle format by default, storing attestation data directly in the OCI registry alongside the image.
# Sign an image during CI, using keyless (OIDC-based) signing
cosign sign registry.example.com/payments/web-app:1.4.2
# Verify before deploy
cosign verify \
--certificate-identity="https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
registry.example.com/payments/web-app:1.4.2
Keyless signing avoids the operational headache of managing long-lived private keys, binding the signature instead to your CI identity provider. Once signing is in place, enforce it at admission time so unsigned images can’t be deployed at all, which is exactly what the next step covers.
If your organization still relies on long-lived key pairs for compliance reasons, Cosign supports that path too, through cosign generate-key-pair and a KMS-backed private key. Just be aware that key-based signing adds a rotation burden keyless signing avoids entirely, so most teams migrating in 2026 are moving toward the OIDC-based flow shown above rather than away from it.
Step 8: Enforce Policy-as-Code with Kyverno
Kyverno 1.18.2, released after the project’s graduation within the Cloud Native Computing Foundation, gives you a native Kubernetes policy engine that writes policies as regular YAML instead of a separate policy language. Version 1.17 promoted its CEL-based policy engine to general availability, which means policies now evaluate faster and integrate more directly with the API server’s own admission chain. Use it to block unsigned images and enforce the securityContext rules from Step 3 at the cluster level, so a developer can’t bypass them by editing a manifest directly.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds: ["Pod"]
verifyImages:
- imageReferences:
- "registry.example.com/payments/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/org/repo/.github/workflows/build.yml@refs/heads/main"
issuer: "https://token.actions.githubusercontent.com"
If you’d rather use OPA Gatekeeper instead, version 3.23.0 works the same way through Rego constraints and constraint templates, and the choice mostly comes down to whether your team prefers Kubernetes-native YAML (Kyverno) or Rego (Gatekeeper). Both now support ValidatingAdmissionPolicy alignment, so enforcement scope stays consistent whether you use the policy engine or the native Kubernetes admission mechanism.
Step 9: Harden securityContext and Runtime Isolation
Even with Restricted Pod Security Standards enforced, you should set an explicit securityContext on every workload rather than relying purely on namespace-level defaults. This is where the AppArmor GA feature from Kubernetes 1.30 onward becomes directly useful, since AppArmor profiles now live in the same block as the rest of your pod hardening settings.
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
appArmorProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
Dropping all Linux capabilities and adding back only the specific ones a container needs (rare, but sometimes NET_BIND_SERVICE for binding to a low port) cuts off entire classes of container escape techniques. Combine this with a minimal, non-root, distroless base image, and you remove most of the tools an attacker would need even after landing inside a container.
A read-only root filesystem trips up more application teams than any other setting here, because plenty of frameworks write temporary files or cache data to disk by default. Rather than dropping the setting, mount an explicit emptyDir volume at the specific path the application needs to write to, such as /tmp or a cache directory. That keeps the rest of the container’s filesystem immutable while still letting the one legitimate write path function normally.
Step 10: Encrypt Secrets and Automate TLS with cert-manager
cert-manager 1.21.1, released July 29, 2026, is the current supported line and fixed a controller panic regression from 1.21.0 affecting certificates with a disabled renewal policy. The version before it, 1.20, updated its Go runtime to 1.26.4 specifically to remediate CVE-2026-27145, CVE-2026-42504, and CVE-2026-42507, a reminder that your certificate automation tooling carries its own patch cadence you need to track.
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager --create-namespace \
--version v1.21.1 \
--set crds.enabled=true
Beyond TLS automation, treat Kubernetes Secrets as sensitive by default. Enable encryption at rest for etcd, and for anything beyond low-value config, move to an external secrets manager (Vault, or an External Secrets Operator pointed at your cloud provider’s secret store) rather than storing plaintext values inside base64-encoded Secret objects, which are not actually encrypted, just encoded.
Step 11: Deploy Runtime Threat Detection with Falco
Everything up to this point is preventive. Falco 0.44.1, released June 11, 2026, is where container security shifts to detection, watching kernel-level syscalls for behavior that shouldn’t happen even in a container that passed every scan and policy check. A container spawning an unexpected shell, writing to a system binary directory, or opening a raw network socket are all classic post-compromise signals Falco catches in real time.
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--namespace falco --create-namespace \
--version 0.44.1 \
--set falco.jsonOutput=true
Falco ships with a default rule set covering common attack patterns, but tune it for your environment quickly, an untuned Falco deployment produces enough noise that teams start ignoring alerts within weeks. Route Falco output to your existing SIEM or alerting stack rather than letting it sit in pod logs nobody watches.
Step 12: Monitor, Audit, and Respond to Incidents
Close the loop by connecting audit logs, Falco alerts, and policy violation events into one place your team actually watches. At minimum, alert on: new ClusterRoleBindings, any pod created outside your standard CI pipeline, Falco critical-severity events, and Kyverno or Gatekeeper policy denials that spike in volume, which often signal a misconfigured deployment or an active probing attempt.
Write down an actual incident response runbook before you need one. Decide in advance who gets paged on a Falco critical alert, how you isolate a pod (cordon the node, apply an emergency NetworkPolicy, or delete the pod outright), and who has authority to pull a workload from production at 2am. Container security incidents move fast, a reverse shell inside a pod can pivot to credential theft within minutes if the ServiceAccount token wasn’t scoped down in Step 4, so the runbook needs to exist before the alert fires, not get improvised while it’s firing.
Re-run the audit commands from Step 1 monthly and compare against your baseline. Container security drifts. A namespace that started with Restricted Pod Security Standards can quietly end up with an exception carved in six months later because a deploy failed and someone widened the policy to unblock it under deadline pressure. Track those exceptions in a ticket, not just in cluster state, so they get revisited instead of forgotten.
Putting It All Together: A Complete Hardened Namespace
Reading twelve steps in isolation makes it hard to see how the pieces fit together. Here’s a single, complete bootstrap for one namespace that combines everything above: Pod Security Standards, a locked-down ServiceAccount, default-deny networking, an image signature policy, and a hardened workload. Save this as namespace-bootstrap.yaml and apply it with kubectl apply -f namespace-bootstrap.yaml against a namespace you control.
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-app
namespace: payments
automountServiceAccountToken: false
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes: ["Ingress", "Egress"]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: payments
spec:
podSelector: {}
policyTypes: ["Egress"]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: payments
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
serviceAccountName: web-app
containers:
- name: web-app
image: registry.example.com/payments/web-app:1.4.2
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
This manifest alone won’t stop everything, it’s the workload half of the picture. Layer in the Kyverno ClusterPolicy from Step 8 to enforce image signatures cluster-wide, the Falco DaemonSet from Step 11 for runtime detection, and cert-manager from Step 10 for any Ingress that needs TLS, and you have the full stack running end to end. Test the whole thing by deploying a deliberately non-compliant pod (one running as root, with no resource limits) into the namespace, it should be rejected at admission time, not caught later by a scan you have to remember to run.
Common Pitfalls When Hardening Container Security
- Enforcing Restricted everywhere on day one. This breaks legacy workloads immediately and trains teams to request blanket exceptions. Roll out Baseline first, then Restricted namespace by namespace.
- Scanning images but not gating the build. A Trivy report nobody reads is not container security, it’s a compliance artifact. Use
--exit-code 1and fail the build. - Leaving default ServiceAccount tokens mounted. Most pods never call the Kubernetes API. Disable automount by default and opt in per workload.
- Writing NetworkPolicies without verifying enforcement. Some CNI configurations accept the objects but don’t enforce them. Test with a deliberately blocked connection.
- Treating signing and scanning as separate, disconnected steps. An image can pass a scan and still be swapped before deploy if you don’t verify its signature at admission time.
- Ignoring your security tooling’s own patch cadence. Trivy’s 2026 supply-chain incident and cert-manager’s CVE-driven Go upgrades both show your scanners and certificate tools need patching too.
Troubleshooting Container Security Problems
Even a correctly designed rollout hits friction. Here are the issues that come up most often once these controls are live in a real cluster, in the order teams tend to encounter them.
Pods stuck in Pending after enforcing Restricted Pod Security Standards. Check kubectl describe pod for admission errors. Usually a missing runAsNonRoot or a dropped capability the app still needs. Fix the manifest, don’t downgrade the namespace policy.
Trivy scan hangs or times out in CI. Usually a stale vulnerability database. Run trivy image --download-db-only as a separate cached step, or point Trivy at a private DB mirror if your CI runners have restricted egress.
Cosign verify fails with “no matching signatures.” Confirm the certificate identity and OIDC issuer in your verify command exactly match the workflow that signed the image, including the branch ref. A mismatch here is the most common cause, not an actual missing signature.
Kyverno policy blocks legitimate deployments unexpectedly. Set validationFailureAction: Audit first to log violations without blocking, review a week of audit data, then switch to Enforce once you’ve confirmed no false positives.
NetworkPolicy default-deny breaks DNS resolution. Your default-deny egress policy is blocking traffic to kube-dns. Add an explicit allow rule for UDP/TCP port 53 to the kube-system namespace before applying default-deny broadly.
Falco generates excessive alert volume. Disable or tune the noisiest default rules for your workload type rather than disabling Falco entirely. Package managers running during image build, for instance, trigger rules meant to catch runtime shell spawning.
cert-manager Certificate stuck in “Ready=False, InvalidSolver”. This regression appeared in 1.21.0 when a referenced ACME DNS-01 solver Secret was recreated. Upgrade to 1.21.1, which fixes it directly.
RBAC audit shows service accounts with unused permissions. Use kubectl auth can-i --list --as=system:serviceaccount:namespace:name to see the actual effective permission set, then trim the bound Role to match only verbs and resources the workload’s logs show it actually calling.
Gatekeeper constraints don’t apply after upgrading to 3.22 or later. The sync-vap-enforcement-scope setting is enabled by default starting in 3.22.0 and changes which namespaces ValidatingAdmissionPolicy enforcement applies to. Review your constraint scope explicitly after any Gatekeeper upgrade.
Advanced Tips for Production-Grade Hardening
Once the twelve steps above are running, a few refinements separate a good setup from a genuinely resilient one. Isolate sensitive workloads onto dedicated node pools using taints and tolerations, so a compromised low-trust workload can’t even schedule near your payments or auth services. Enable API server tracing, stable since Kubernetes 1.30, to get detailed request-level visibility during incident investigations without standing up a separate observability pipeline.
For multi-tenant clusters, combine ResourceQuotas with strict NetworkPolicy isolation per tenant namespace, and consider a service mesh for mutual TLS between services if your compliance requirements call for encryption in transit beyond what NetworkPolicy alone provides. Finally, review your RBAC bindings on a fixed schedule rather than reactively. The Kubernetes security checklist recommends periodic access review as a standing control, not a one-time setup task, and teams that skip this step tend to accumulate stale permissions that outlive the projects they were created for.
Container Security Tools Compared
Each tool in this stack covers a different layer, and none of them substitute for another. Use this table to map coverage when you’re deciding what to add next.
| Tool | Layer covered | When it acts |
|---|---|---|
| Trivy | Image and dependency vulnerabilities | Build time / CI |
| Cosign | Image provenance and integrity | Build time and admission time |
| Kyverno / Gatekeeper | Policy enforcement on Kubernetes objects | Admission time |
| NetworkPolicy | Pod-to-pod network segmentation | Runtime, continuous |
| Falco | Runtime behavior and anomaly detection | Runtime, continuous |
| cert-manager | TLS certificate lifecycle | Runtime, continuous |
Notice that only two of the six act at admission time, and only three run continuously at runtime. If your current container security setup is scan-only, you have coverage at exactly one point in the pipeline. The steps above close the other gaps.
If you’re prioritizing under time pressure and can’t roll out all six at once, sequence them by where an attacker actually spends the most time. Image scanning and signing stop the largest volume of low-effort attacks (known CVEs, tampered images) for the least engineering cost, so most teams start there. RBAC and NetworkPolicy come next because they bound the damage of anything that gets past the first layer. Runtime detection with Falco is the most valuable long-term investment but also the one that takes the longest to tune well, so treat it as a parallel track rather than something to defer until everything else is finished.
Frequently Asked Questions
What is container security in Kubernetes?
Container security in Kubernetes covers the controls that protect workloads across the full lifecycle: scanning images for known vulnerabilities before deploy, enforcing Pod Security Standards and RBAC to limit what a running container can do, segmenting network traffic between pods, and detecting anomalous runtime behavior after deployment. No single tool covers all of it, which is why this guide layers six different tools across prevention, admission, and detection.
Is Kubernetes 1.36 secure by default?
Kubernetes 1.36.3 ships with strong building blocks, RBAC, Pod Security Admission, and CEL-based admission control among them, but none of it is enforced by default in a new namespace. You have to apply Pod Security Standard labels, write NetworkPolicies, and configure RBAC deliberately. Out of the box, a fresh namespace allows unrestricted pod-to-pod traffic and permissive pod security settings.
Do I need both Trivy and Falco?
Yes, they cover different points in time. Trivy scans images before they run, catching known CVEs in dependencies. Falco watches containers while they run, catching behavior that a scan can never predict, like a compromised process spawning a reverse shell. Skipping either one leaves a real gap: pre-deploy scanning alone misses zero-days and runtime compromise, while runtime detection alone means known, patchable vulnerabilities ship to production unchecked.
Should I use Kyverno or OPA Gatekeeper?
Both are current, actively maintained policy engines (Kyverno 1.18.2 and Gatekeeper 3.23.0 as of mid-2026). Kyverno policies are written as native Kubernetes YAML, which most platform teams find faster to adopt. Gatekeeper uses Rego, the Open Policy Agent language, which is more expressive for complex cross-resource logic but has a steeper learning curve. If your team already uses OPA elsewhere, Gatekeeper keeps you consistent. Otherwise, Kyverno’s YAML-native approach usually ships faster.
How often should I rotate signing keys and re-audit RBAC?
Keyless signing through Cosign and Sigstore sidesteps long-lived key rotation entirely by binding signatures to short-lived OIDC identities from your CI provider. For RBAC, review bindings at least every six months, and immediately after any major reorg or offboarding event. Stale ClusterRoleBindings tied to departed team members’ service accounts are a recurring finding in cluster security audits.
Can I apply Restricted Pod Security Standards to an existing production cluster safely?
Yes, but not all at once. Set the label with warn mode first to surface violations without blocking anything, review the warnings for a week, fix the flagged workloads, then switch to enforce. Jumping straight to enforcement on a namespace with legacy workloads will take down pods that don’t meet the profile.
What broke in Trivy’s supply chain in 2026, and is it fixed now?
Malicious binaries were briefly published under Trivy tags v0.69.4 through v0.69.6 earlier in 2026 in a compromise of the project’s release pipeline. The Trivy team responded with v0.70.0, rotating GPG signing keys for its package repositories. As of v0.71.2, releases include build provenance attestations and additional CI hardening. If you’re running anything from the 0.69.4-0.69.6 range, upgrade immediately and verify the binary signature.
Does managed Kubernetes (EKS, AKS, GKE) handle container security for me?
Managed control planes handle API server hardening, etcd encryption, and control plane patching for you. They do not handle RBAC design, Pod Security Standards, NetworkPolicy, image scanning, signing, or runtime detection, all of which remain entirely your responsibility under the shared responsibility model every cloud provider publishes. Every step in this guide still applies on a managed cluster.
What’s the fastest single change I can make to improve container security today?
Disable automountServiceAccountToken for every workload that doesn’t call the Kubernetes API, and add a default-deny NetworkPolicy to your most sensitive namespace. Both take minutes to apply, neither requires new tooling, and together they remove the two easiest lateral-movement paths an attacker has after compromising a single container.
Related Coverage
- Kubernetes Ingress-Nginx Flaw: CVSS 8.8, Still Unpatched [2026]
- Azure Kubernetes Security GA as Backlog Hits $678B [2026]
- ECS vs EKS: $0 vs $438/Mo Control Plane [2026]
- AWS Lambda Serverless Tutorial: 12 Steps, 45 Min [2026]
- Cloudflare Workers Setup: 12 Steps, 30 Min [2026]
- More Cloud Computing Coverage




