Kyverno CVE Scanning Gates¶
Requires vulnerability scan attestations before deployment. Blocks images with critical or high severity CVEs from reaching production environments.
Scan Attestations Require Image Signatures
CVE scanning gates depend on cosign attestations. Combine with image signature verification policies to ensure attestation authenticity.
Template 5: CVE Scanning Gates¶
Requires vulnerability scan attestations before deployment. Blocks images with critical or high severity CVEs from reaching production.
Complete Policy¶
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-vulnerability-scan
namespace: kyverno
spec:
validationFailureAction: enforce
background: false # Attestation verification only works on admission
webhookTimeoutSeconds: 30
failurePolicy: Fail
rules:
- name: verify-vulnerability-scan-attestation
match:
resources:
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
- Job
- CronJob
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8xVwU5R3...
-----END PUBLIC KEY-----
attestations:
- predicateType: https://cosign.sigstore.dev/attestation/vuln/v1
conditions:
- all:
# Require trivy scanner
- key: "{{ attestation.predicate.scanner.name }}"
operator: Equals
value: "trivy"
# Block critical vulnerabilities
- key: "{{ attestation.predicate.metadata.scanResult.vulnerabilities[?severity=='CRITICAL'] | length(@) }}"
operator: LessThanOrEquals
value: 0
# Block high vulnerabilities in production
- key: "{{ attestation.predicate.metadata.scanResult.vulnerabilities[?severity=='HIGH'] | length(@) }}"
operator: LessThanOrEquals
value: 0
# Scan must be recent (within 24 hours)
- key: "{{ attestation.predicate.metadata.scanFinishedOn }}"
operator: GreaterThan
value: "{{ time_now_utc() - '24h' }}"
- name: allow-medium-cves-in-dev
match:
resources:
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
- Job
- CronJob
namespaces:
- development
- staging
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: "{{ configmap.kyverno.data.cosign-pub }}"
attestations:
- predicateType: https://cosign.sigstore.dev/attestation/vuln/v1
conditions:
- all:
# Block only critical in dev/staging
- key: "{{ attestation.predicate.metadata.scanResult.vulnerabilities[?severity=='CRITICAL'] | length(@) }}"
operator: LessThanOrEquals
value: 0
Customization Variables¶
| Variable | Default | Purpose |
|---|---|---|
validationFailureAction |
enforce |
Block images with CVEs |
webhookTimeoutSeconds |
30 |
Timeout for attestation verification |
max-critical-cves |
0 |
Max allowed CRITICAL vulnerabilities |
max-high-cves |
0 |
Max allowed HIGH vulnerabilities |
scan-freshness |
24h |
Maximum age of vulnerability scan |
scanner-name |
trivy |
Required vulnerability scanner |
dev-namespaces |
development, staging |
Allow more CVEs in non-prod |
Validation Commands¶
# Apply policy
kubectl apply -f cve-scanning-policy.yaml
# Scan image and generate attestation
trivy image --format cosign-vuln --output vuln-report.json registry.example.com/app:v1.2.3
cosign attest --predicate vuln-report.json --key cosign.key --type vuln registry.example.com/app@sha256:abc123
# Verify attestation locally
cosign verify-attestation --key cosign.pub --type vuln registry.example.com/app@sha256:abc123
# Test image without scan (should fail)
kubectl run test --image=registry.example.com/unscanned@sha256:abc123
# Test image with passing scan (should pass)
kubectl run test --image=registry.example.com/scanned@sha256:def456
# Check CVE gate violations
kubectl logs -n kyverno deployment/kyverno | grep "vulnerability scan"
# List images in cluster without scan attestations
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.spec.containers[*].image}{"\n"}{end}'
Use Cases¶
- Zero-Day Protection: Block images with newly discovered critical CVEs
- Compliance Requirements: Meet PCI-DSS, SOC2 requirements for vulnerability scanning
- Shift-Left Security: Catch vulnerabilities before production deployment
- Risk Management: Different CVE thresholds for dev, staging, production
- Supply Chain Transparency: Require SBOM and vulnerability scan attestations
CI/CD Integration¶
Generate vulnerability scan attestations in GitHub Actions:
name: Build, Scan, and Attest
on:
push:
branches: [main]
jobs:
build-scan-attest:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Build image
run: |
docker build -t registry.example.com/app:${{ github.sha }} .
docker push registry.example.com/app:${{ github.sha }}
- name: Install trivy and cosign
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
- name: Scan for vulnerabilities
run: |
trivy image --format cosign-vuln --output vuln-report.json registry.example.com/app:${{ github.sha }}
# Fail if critical or high CVEs found
trivy image --severity CRITICAL,HIGH --exit-code 1 registry.example.com/app:${{ github.sha }}
- name: Attest vulnerability scan
run: |
IMAGE_DIGEST=$(docker inspect registry.example.com/app:${{ github.sha }} --format='{{index .RepoDigests 0}}' | cut -d'@' -f2)
cosign attest --yes --predicate vuln-report.json --type vuln registry.example.com/app@${IMAGE_DIGEST}
- name: Generate SBOM
run: |
trivy image --format spdx-json --output sbom.spdx.json registry.example.com/app:${{ github.sha }}
IMAGE_DIGEST=$(docker inspect registry.example.com/app:${{ github.sha }} --format='{{index .RepoDigests 0}}' | cut -d'@' -f2)
cosign attest --yes --predicate sbom.spdx.json --type spdx registry.example.com/app@${IMAGE_DIGEST}
CVE Severity Mapping¶
Different environments can have different CVE tolerances:
# Production: Zero tolerance
- namespaces: [production, prod-*]
max-critical: 0
max-high: 0
max-medium: 5
# Staging: Allow medium, block critical/high
- namespaces: [staging, stage-*]
max-critical: 0
max-high: 0
max-medium: 999
# Development: Block only critical
- namespaces: [development, dev-*]
max-critical: 0
max-high: 10
max-medium: 999
Vulnerability Scan Report Format¶
Example Trivy cosign-vuln output:
{
"predicateType": "https://cosign.sigstore.dev/attestation/vuln/v1",
"predicate": {
"invocation": {
"uri": "https://github.com/aquasecurity/trivy",
"parameters": ["--severity", "CRITICAL,HIGH"]
},
"scanner": {
"name": "trivy",
"version": "0.48.0",
"db": {
"version": "2024-01-15"
}
},
"metadata": {
"scanStartedOn": "2024-01-15T10:00:00Z",
"scanFinishedOn": "2024-01-15T10:05:00Z",
"scanResult": {
"vulnerabilities": [
{
"id": "CVE-2024-1234",
"severity": "CRITICAL",
"package": "openssl",
"version": "1.1.1",
"fixedVersion": "1.1.1w"
}
]
}
}
}
}
Combining with Base Image Policies¶
For maximum security, combine CVE scanning with base image enforcement:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: comprehensive-image-security
spec:
validationFailureAction: enforce
background: false
rules:
- name: approved-base-and-scanned
match:
resources:
kinds:
- Pod
validate:
message: "Images must use approved base image"
pattern:
spec:
containers:
- image: "registry.example.com/base/*"
verifyImages:
- imageReferences:
- "registry.example.com/*"
attestors:
- count: 1
entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
attestations:
# Require vulnerability scan
- predicateType: https://cosign.sigstore.dev/attestation/vuln/v1
conditions:
- all:
- key: "{{ attestation.predicate.metadata.scanResult.vulnerabilities[?severity=='CRITICAL'] | length(@) }}"
operator: Equals
value: 0
# Require SBOM
- predicateType: https://spdx.dev/Document
Troubleshooting¶
Common Issues¶
Error: no matching attestations found
# Verify image has attestation
cosign verify-attestation --key cosign.pub --type vuln registry.example.com/app@sha256:abc123
# Check attestation format matches policy
cosign verify-attestation --key cosign.pub --type vuln registry.example.com/app@sha256:abc123 | jq '.payload | @base64d | fromjson'
Error: scan too old
# Check scan timestamp
cosign verify-attestation --key cosign.pub --type vuln registry.example.com/app@sha256:abc123 | jq '.payload | @base64d | fromjson | .predicate.metadata.scanFinishedOn'
# Re-scan and re-attest
trivy image --format cosign-vuln --output vuln-report.json registry.example.com/app@sha256:abc123
cosign attest --yes --predicate vuln-report.json --type vuln registry.example.com/app@sha256:abc123
Error: critical vulnerabilities found
# View full vulnerability report
trivy image registry.example.com/app@sha256:abc123
# Fix vulnerabilities and rebuild
# Update base image or dependencies
docker build --no-cache -t registry.example.com/app:fixed .
Related Resources¶
- Kyverno Image Security → - Base image enforcement
- Kyverno Image Signing → - Cosign signature verification
- Kyverno Image Validation → - Digest requirements and registry allowlists
- Kyverno Pod Security → - Security contexts and capabilities
- Template Library Overview → - Back to main page
External Documentation¶
- Trivy Vulnerability Scanner - Official Trivy documentation
- Cosign Attestations - Attestation format specification
- CVE Numbering Authority - CVE database
- NIST NVD - National Vulnerability Database