Kubernetes admission control decides what is allowed into the cluster, and runtime security watches what those workloads then do, but each has a blind spot the other was built to cover. Admission control evaluates every create and update request against policy before the API server stores it; it never sees a process, a file write or a network connection. Runtime security watches the kernel and the control plane on live nodes; it cannot stop a bad manifest being accepted in the first place. This guide compares the two inside a cluster: policy at deploy time vs eBPF detection, how each handles service account tokens and RBAC, configuration drift, and image signature verification at admission. It is for platform and cloud security engineers deciding where to put policy, what to enforce and what to watch. For the wider lifecycle debate see Shift-Left vs Runtime Security, and for vulnerability prioritisation from runtime data see Container Image Scanning vs Runtime Threat Detection; neither is repeated here.
Key takeaways
- Admission control acts on API objects; runtime security acts on behaviour. One rejects a privileged pod spec, the other notices a shell spawning in a pod that was admitted cleanly.
- Admission only sees writes. Kubernetes documents that admission controllers cannot block get, watch or list, and nothing that happens inside a running container passes through admission at all.
- Identity controls live in both. Admission can refuse token automount, the default service account and dangerous role bindings; only runtime and audit data can show a legitimate token being used by the wrong process.
- Drift is a runtime problem with an admission prerequisite. Admission can require a read-only root filesystem and pinned digests; runtime detection catches binaries written and executed after start.
- Signature checks at admission are a point in time. A verified image stays running after its key is revoked or its signature removed, so pair admission verification with periodic audit and runtime image inventory.
The short answer
Use admission control to stop known-bad configuration and unknown software entering the cluster, and runtime security to catch abuse of what was allowed in. Admission control is cheap, deterministic and preventive, and it is the only place to enforce rules about manifests, such as no privileged containers, no host namespaces, signed images only, no automounted tokens. Runtime security is the only place to see exploitation, credential misuse and drift. A cluster with only admission control has a clean inventory and no visibility; a cluster with only runtime security produces alerts for problems a five-line policy would have prevented. The Kubernetes Admission Control Guide and the Container Runtime Security Guide cover each in depth.
Side-by-side comparison
| Dimension | Admission control | Runtime security |
|---|---|---|
| Where it runs | In the API server request path: built-in plugins, ValidatingAdmissionPolicy (CEL, in process) or external webhooks | On each node (eBPF or kernel module sensor) and on the control plane audit log stream |
| What it inspects | API objects on create, update, delete and connect | System calls, process trees, file and network activity, audit events |
| Decision type | Allow, deny, warn or audit; mutating plugins can change the object | Alert; some tools can also kill a process or block a call in the kernel |
| When it acts | Before the object is persisted | While the workload runs |
| Typical tools | Pod Security Admission, ValidatingAdmissionPolicy, Gatekeeper, Kyverno | Falco, Tetragon, commercial runtime sensors |
| Failure cost | A failing webhook with failurePolicy Fail blocks deployments | A noisy sensor floods the queue; an enforcing sensor can kill legitimate processes |
| Cannot see | Reads, anything inside a running container, objects admitted before the policy existed | Intent of a manifest before it runs; configuration that is risky but not yet abused |
Open source projects are listed alphabetically; the order is not a ranking.
Threat model, architecture and failure modes: admission control
Threat model: someone with deploy rights (a developer, a CI pipeline token, a compromised service account) submits a workload that grants itself more power than it should: a privileged container or host path mount for escape to the node (ATT&CK T1611 Escape to Host), an image from an untrusted registry or a tampered internal image (T1525 Implant Internal Image), or a new container deployed for cryptomining or persistence (T1610 Deploy Container). Admission control stops these at the API.
Architecture in words: after authentication and authorisation, the request passes through mutating admission (which may set defaults, inject sidecars or rewrite image tags to digests), then validating admission (which can only accept or reject). Kubernetes documents that if any controller in either phase rejects, the whole request is rejected, and that validating webhooks are the right place for policy that must see the final object. Pod Security Admission applies the Pod Security Standards (privileged, baseline, restricted) by namespace label in enforce, audit or warn mode. ValidatingAdmissionPolicy, stable since Kubernetes v1.30, runs CEL expressions inside the API server without a webhook. Gatekeeper and Kyverno run as webhooks with richer policy languages, mutation and background audit.
Failure modes:
- Fail open or fail closed: the default failurePolicy for admission webhooks is Fail, with a default timeout of 10 seconds (range 1 to 30). Setting Ignore to avoid outages means a crashed webhook silently admits everything.
- Opt-out selectors: Kubernetes warns to use objectSelector only for opt-in webhooks, because end users can skip the webhook by setting labels themselves.
- Exemptions that leak: Pod Security Admission documents that exempting a controller service account, such as the ReplicaSet controller, implicitly exempts anyone who can create that workload type, and that enforce applies to pods, not Deployments, so a bad Deployment is accepted and only its pods fail.
- Policy added after the fact: admission does not re-evaluate running objects. Gatekeeper's audit evaluates existing resources (every 60 seconds by default) and Kyverno can run background scans, but those report, they do not evict.
Threat model, architecture and failure modes: runtime security
Threat model: a workload that passed every admission rule is exploited through its application, or a legitimate operator credential is stolen. The attacker runs a shell (T1059.004 Unix Shell), uses kubectl exec (T1609 Container Administration Command), reads the mounted service account token or queries the API (T1552.007 Container API, T1613 Container and Resource Discovery), escapes to the node through a kernel flaw (T1611), or mines cryptocurrency (T1496 Resource Hijacking). None of this is an API write that admission can see.
Architecture in words: a sensor on each node hooks the kernel, Falco through its default modern eBPF probe (or a kernel module) and Tetragon through eBPF programs defined by tracing policies, and enriches each event with container and Kubernetes metadata such as namespace, pod and image. Rules match behaviour and send alerts to stdout, files, syslog or HTTP endpoints, then on to a SIEM. Falco also ingests Kubernetes audit events through a plugin, joining API activity with node activity. Tetragon documents in-kernel enforcement, including killing a process before a system call completes; detection-only deployment is the safer starting point. See the eBPF Security Guide for the sensor's own risks.
Failure modes:
- Coverage gaps: sensors missing on new node pools, unsupported kernels or managed node types without kernel access.
- Noise: default rules firing on init containers, debug tooling and build jobs until nobody reads them.
- Enforcement without testing: a kill action on a rule that also matches a legitimate health check or migration job causes an outage.
- Sensor as target: the sensor runs privileged on every node; its DaemonSet, service account and configuration need the same protection as any tier 0 component.
Service account tokens and RBAC: what each layer checks
Every pod runs as a Kubernetes service account, a non-human identity whose token the pod can present to the API server. Kubernetes assigns the namespace's default service account to any pod that does not name one, and documents that default accounts get no permissions beyond API discovery, unless someone binds roles to them.
| Identity risk | Admission control check | Runtime or audit signal |
|---|---|---|
| Token mounted into pods that never call the API | Require automountServiceAccountToken: false unless the namespace or workload is annotated as needing API access | Process reading the token path in a workload that is not expected to |
| Workloads sharing the default service account | Reject pods without an explicit serviceAccountName, or using default | Audit log: API calls from default accounts |
| Escalation through role bindings | Reject RoleBindings or ClusterRoleBindings to cluster-admin or wildcard roles outside an approved list | Audit log: create or patch of bindings by a service account |
| Long-lived token Secrets | Reject Secrets of type kubernetes.io/service-account-token outside named exceptions | Use of a token whose credential ID does not match a live pod |
| Kubelet abuse of node credentials | NodeRestriction plugin limits kubelets to their own Node object and pods bound to them | Node identity calling APIs outside its node |
| Token stolen and used elsewhere | Not visible to admission | Service account calling APIs it never calls, from an unexpected source (T1528 Steal Application Access Token) |
Response differs too. Kubernetes documents that deleting a pod invalidates its projected token immediately for TokenReview, while clients that validate the token through OIDC discovery keep treating it as valid until it expires. Revocation is therefore a runtime action whose reach depends on how downstream services verify tokens. The Kubernetes Service Account Token Guide and Kubernetes RBAC Guide cover the identity side in detail.
Drift: from immutable spec to changed container
Drift here means the running container no longer matches the image and spec that were admitted. Admission control can make drift hard and runtime security can make it visible:
- Admission prerequisites: require
readOnlyRootFilesystem: true,allowPrivilegeEscalation: false, dropped capabilities and images referenced by digest. The restricted Pod Security Standard covers several of these; read-only root filesystems and digest pinning need a custom policy. - Runtime detection: alert on execution of a binary that was not in the image, writes to system paths, package manager runs and interactive shells in production namespaces.
- Exec as drift: kubectl exec is an API request (the pods/exec subresource), so an admission webhook can restrict it, and the audit log records it; what the operator does inside the shell is only visible at runtime.
- Image cache drift: Kubernetes documents that without the AlwaysPullImages plugin, once an image is pulled to a node any pod scheduled there can use it by name without registry authorisation; the plugin is not enabled by default.
Treat a drift alert on a workload with a read-only root filesystem as high severity: the admission rule means the change should have been impossible. For node and cluster configuration drift, see the Configuration Drift and Baseline Enforcement Guide.
Image signature verification at admission
Signature verification is the strongest admission control for supply chain risk: only images signed by your build system's identity may run. Kyverno's verifyImages rules, as a documented example, check Sigstore Cosign or Notary signatures and attestations; by default they rewrite tags to digests (mutateDigest), require a digest (verifyDigest) and require a verified annotation (required). The rule runs in the mutating webhook to insert the digest and again in the validating webhook. See the Sigstore Guide.
Three limits push part of the job to runtime:
- Point in time: verification happens when the pod is admitted. Revoking a key or deleting a signature does not stop pods already running.
- Caching: Kyverno caches successful verifications (default TTL 60 minutes), so changes in the registry are not reflected until the entry expires.
- Exemptions: system namespaces and third-party charts are commonly excluded, and those are exactly where unsigned images run.
Close the gap by comparing the running image digests reported by the runtime sensor or node inventory with the set of currently valid signed digests, on a schedule, and alerting on any running digest that no longer verifies.
Decision guidance
- Start with admission when: teams can deploy privileged pods, images come from public registries, there is no image provenance, or default service accounts carry roles. Pod Security Admission in warn and audit mode costs almost nothing to turn on.
- Prioritise runtime when: clusters run internet-facing or multi-tenant workloads, admission is already enforced, or you need evidence for investigations.
- Built-in vs webhook: use Pod Security Admission and ValidatingAdmissionPolicy where they are enough, because they run in process with no webhook availability risk; add Gatekeeper or Kyverno for mutation, image verification and audit of existing objects.
- Small teams on managed Kubernetes: restricted or baseline Pod Security Admission per namespace, a handful of CEL policies for service accounts and registries, control plane audit logs on, and one open source runtime sensor in detect mode on production nodes.
- Large or regulated estates: policy as code in Git with tests, signature verification for all first-party images, runtime detection with an owner and SLAs, and a reconciliation job that matches admitted state with running state.
Worked scenario
A platform team runs 40 namespaces. Pod Security Admission enforces baseline and warns on restricted; a Kyverno policy requires signed images from the internal registry for all application namespaces. A dependency in one web service is exploited. The attacker gets a shell inside the container: admission saw nothing, because nothing was written to the API. The runtime sensor alerts on a shell spawned by the web server process and, seconds later, on a read of the service account token. The pod's service account has no role bindings because an admission policy rejects bindings to the default account and the workload's own account is read-only, so the API probe returns forbidden, which the audit log records. The attacker tries to download a miner to /tmp; the root filesystem is read-only but /tmp is an emptyDir, and the runtime rule for execution of a new binary fires. Responders delete the pod, which invalidates its projected token, and patch the image. The review adds two admission rules (no writable emptyDir on internet-facing workloads without an exception, and automount disabled for this workload class) and one runtime rule tuned for the exploit's process pattern. Each layer did what only it could do.
How to implement
- Inventory: list namespaces, service accounts with role bindings, privileged workloads and image sources. Enable control plane audit logging.
- Pod Security Admission in warn and audit: label all namespaces with
pod-security.kubernetes.io/warn=restrictedandaudit=restricted; collect violations for two weeks. - Enforce in rings: set enforce=baseline cluster-wide, then restricted per namespace as teams fix findings. Keep exemptions explicit and owned.
- Identity policies: add rules for automount, default service account use and binding restrictions in audit mode, then deny.
- Signature verification: sign images in CI, verify at admission in audit mode, then enforce for application namespaces. Exclude system namespaces explicitly, never by label opt-out.
- Webhook resilience: run policy engines with multiple replicas, short timeouts and a documented break-glass procedure; exclude the engine's own namespace so it can recover.
- Runtime sensor in detect mode: deploy to production nodes first, tune default rules for a month, route alerts to an owned queue.
- Enforcement at runtime last: only for rules with a clean record, starting in non-critical namespaces. Roll back by switching the action to alert.
- Reconcile: schedule a comparison of running digests and service account use with admission policy and audit results.
Detection and measurement
- Admission coverage: percentage of namespaces with Pod Security Admission enforce set to baseline or restricted.
- Policy outcomes: denied, warned and exempted requests per week; exemption count and age.
- Audit backlog: existing resources violating current policy (Gatekeeper audit or Kyverno reports), trending down.
- Signed image share: percentage of running pods whose digest currently verifies.
- Sensor coverage: percentage of nodes reporting runtime events.
- Runtime response: mean time from a high-severity runtime alert to pod deletion or isolation.
Common failure patterns
- Webhook set to Ignore after an outage and never reverted: the policy engine fails silently and admits everything.
- Label-based opt-out: any team that can label its objects can skip the policy.
- Policy without audit: new rules apply to new pods while years of privileged workloads keep running unreviewed.
- Runtime alerts with no owner: the sensor detects the shell, nobody reads the alert until the bill for the miner arrives.
- Signature verification with broad exclusions: system and vendor namespaces run unsigned images with powerful service accounts.
- Treating admission as identity control: rejecting dangerous bindings does nothing about a legitimate token being stolen and used.
Framework mapping
| Framework | Admission control | Runtime security |
|---|---|---|
| NIST SP 800-53 Rev 5 | CM-6 Configuration Settings, CM-7 Least Functionality, CM-14 Signed Components, AC-6 Least Privilege | SI-4 System Monitoring, SI-7 Software, Firmware, and Information Integrity |
| NIST CSF 2.0 | PR.PS-01 (configuration management practices applied) | DE.CM-09 (runtime environments monitored), DE.AE-02 (adverse events analysed) |
| ISO/IEC 27001:2022 Annex A | A.8.9 Configuration management, A.8.32 Change management | A.8.16 Monitoring activities |
| MITRE ATT&CK v20 | Prevents T1610, T1611 via privileged specs, T1525 | Detects T1059.004, T1609, T1552.007, T1613, T1528, T1496, T1611 via kernel exploits |
Practitioner checklist
- Label every namespace for Pod Security Admission, with enforce at baseline or stricter.
- Prefer ValidatingAdmissionPolicy for simple rules; use a webhook engine where you need mutation, image verification or audit.
- Deny token automount, default service account use and cluster-admin bindings by policy, with owned exceptions.
- Require signed images by digest for application namespaces and reconcile running digests on a schedule.
- Keep webhook failurePolicy at Fail with replicas and a break-glass plan; never use label opt-outs.
- Run audit of existing resources and track the backlog.
- Deploy a runtime sensor on every production node in detect mode with an owned alert queue.
- Enable control plane audit logs and join them with runtime events.
- Move runtime rules to enforcement only after a clean detect record.
Standards and references
- Kubernetes: Admission Control in Kubernetes
- Kubernetes: Dynamic Admission Control
- Kubernetes: Validating Admission Policy
- Kubernetes: Pod Security Admission
- Kubernetes: Service Accounts
- Open Policy Agent: Gatekeeper Audit
- Kyverno: Verify Images
- Falco: Documentation
Related NHI Mgmt Group resources: Shift-Left vs Runtime Security · Container Image Scanning vs Runtime Threat Detection · Kubernetes Admission Control Guide · Container Runtime Security Guide · Falco Runtime Security Guide