Kubernetes Admission Control and Container Vulnerability Scanning: A Practical Setup
Kubernetes admission control is the final enforcement layer in a container security program. It sits between “an image is requested for deployment” and “the image is running.” Admission controllers that check scan results can prevent vulnerable images from running even if they slipped through earlier pipeline gates.
The practical implementation of admission control that checks vulnerability scan results is more nuanced than the concept suggests. The challenges: scan data must be available and current, the check must not be so sensitive that it blocks legitimate workloads, and there must be an automated path to bring non-compliant images into compliance.
How Admission Control and Scanning Interact?
The technical flow:
- A deployment is submitted to the Kubernetes API server
- The API server sends the admission request to configured webhook endpoints
- The webhook (Kyverno, OPA Gatekeeper, Starboard) evaluates the request against policies
- If scan data for the requested image exists and the image meets the CVE threshold, the deployment is allowed
- If scan data is missing or the image exceeds the CVE threshold, the deployment is blocked
The dependency on “scan data for the requested image exists” is the first practical challenge. If the image has not been scanned, the admission controller has two options: block the deployment (safe but operationally disruptive for first-time deployments) or allow the deployment with a warning (allows unscanned images, defeating the purpose).
The solution: scan at image push time, not on-demand at deployment time. Images are scanned when pushed to the registry. By the time a deployment is submitted, the image has been scanned and the results are available.
Stale Scan Data: The Silent Coverage Gap
Admission controllers that check “was this image scanned?” without checking “was this image scanned recently?” create a false sense of security.
An image scanned three months ago and found clean at that time may have accumulated 50 new CVEs since then as new vulnerabilities were disclosed against its packages. An admission controller checking only for scan existence allows this image through — correctly, in the technical sense that a scan record exists, incorrectly in the security sense that the scan record is outdated.
Effective admission control checks both existence and recency:
# Kyverno policy checking scan age
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-recent-scan
spec:
rules:
– name: check-scan-recency
match:
resources:
kinds: [Pod]
validate:
message: “Image must have been scanned within the last 30 days”
deny:
conditions:
– key: “{{ time_since(‘{{ request.object.metadata.annotations.\”security.company.com/last-scanned\”}}’) }}”
operator: GreaterThan
value: “720h” # 30 days
Images with scan records older than 30 days trigger a rescan requirement before deployment is allowed. This creates a forcing function for regular rescanning of all production images.
The Exception Accumulation Pattern
Admission control that blocks too aggressively creates exception pressure. When legitimate workloads are blocked by overly strict policies, teams request exceptions to unblock their deployments. Each exception makes the admission policy slightly less effective. Over time, exception lists grow long enough that the policy does not enforce much.
The exception accumulation pattern has a preventive solution: pair admission control with automated remediation that produces images that pass the policy before the deployment is attempted.
Container security hardening applied in the CI pipeline produces images with dramatically fewer CVEs — typically 80-90% fewer than the unhardened version. An admission policy set at “no Critical CVEs” that would block 30% of unhardened images blocks fewer than 5% of hardened images. The exception rate drops; the policy retains its effectiveness.
Setting Up Kyverno for Scan-Aware Admission
A practical Kyverno setup that checks scan results from a centralized scan database:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: container-security-gate
spec:
validationFailureAction: enforce
rules:
– name: check-critical-cves
match:
resources:
kinds: [Pod]
validate:
message: “Image has unacceptable Critical CVEs. See security.company.com/images/{{ request.object.spec.containers[0].image }} for remediation guidance.”
deny:
conditions:
– key: “{{ request.object.metadata.annotations.\”security.company.com/critical-cves\” }}”
operator: GreaterThan
value: “0”
– name: require-hardening-attestation
match:
resources:
kinds: [Pod]
validate:
message: “Image must have passed the automated hardening pipeline”
deny:
conditions:
– key: “{{ request.object.metadata.annotations.\”security.company.com/hardened\” }}”
operator: NotEquals
value: “true”
This policy requires that the deploying image has both a zero Critical CVE annotation and a hardening attestation. The hardening pipeline attaches these annotations automatically when an image passes through. Images that have not been through the pipeline lack the annotations and are blocked.
Frequently Asked Questions
How does Kubernetes admission control work with container vulnerability scanning?
When a deployment is submitted to the Kubernetes API server, it is forwarded to admission webhook endpoints — Kyverno, OPA Gatekeeper, or similar tools — which evaluate the request against configured policies. If the container vulnerability scanner has attached scan annotations to the image and the image meets the CVE threshold, the deployment is allowed. If scan data is missing, stale, or the image exceeds the threshold, the deployment is blocked before any containers start.
What happens when a container vulnerability scanner blocks a Kubernetes deployment?
When an image is blocked at admission, the team must resolve the blocking CVE findings and re-push the image before deployment can proceed. The most effective way to prevent this operational friction is to integrate automated hardening in the CI pipeline so images arrive at admission with CVEs already removed. Container security hardening typically reduces Critical CVE counts by 80–90%, which means the admission policy blocks fewer than 5% of hardened images compared to 30% of unhardened ones.
How do you prevent stale scan data from creating false security assurance in Kubernetes?
Admission controllers that check only for the existence of a scan record — not its age — will pass images scanned months ago, even though new CVEs may have accumulated since then. Effective admission control checks scan recency with a policy condition that blocks deployments if the scan annotation timestamp is older than 30 days, creating a forcing function for regular rescanning of all production images.
What is the best way to manage Kubernetes admission control exceptions for container security?
Exception lists grow when admission policies block legitimate workloads without providing a clear remediation path. The solution is pairing admission control with an automated hardening pipeline that produces images passing the policy on first attempt. Reserve formal exceptions only for Critical CVEs in actively-used packages with no available fix, and set those exceptions to expire automatically when a fix is released to keep the exception list small and justified.
The No-Exception Path
Container image tool hardening that produces admission-ready images creates the no-exception path that makes admission control sustainable. Instead of:
Old flow: Image fails admission → team requests exception → exception is reviewed → exception is granted → image deploys
New flow: Image is hardened by pipeline → image passes admission on first attempt → image deploys
The no-exception path removes the operational friction that causes exception lists to grow. Teams do not need exceptions because their images pass the policy. The admission control retains its effectiveness because the exception list stays short.
The one remaining exception scenario: Critical CVEs in actively-used packages where no fix is available. For these, document the risk acceptance formally with a time-bounded exception that expires when a fix becomes available. This keeps exceptions small and justified rather than growing into a policy bypass list.
