Skip to main content
Version: 1.3

Vulnerability Report

This document outlines how Ratify can be used to verify vulernability reports. The vulnerabilityreport verifier is added as a plugin to the Ratify verification framework. Currently the vulnerability report verifier supports the following report types:

  • Reports attached to the subject image as a referrer artifact
  • Reports in SARIF format
  • Reports generated by Trivy or Grype

Table of Contents

Example Scenario

Alice has a Kubernetes cluster. She wants to enable vulnerability report verification. In particular, she wants to make sure all container images used in K8s resources have at least one valid vulnerability report attached to the image. She only wants to consider the latest vulnerability report that was generated. The report must:

  • be in SARIF format
  • generated from Trivy or Grype
  • created less than 24 hours ago
  • has no HIGH or CRITICAL severity level vulnerabilities in the report
  • does not contain the log4shell CVE

Recording

asciicast

Walkthrough

First, only follow the first step of the manual quickstart, which installs Gatekeeper on the cluster.

Second, install Ratify with the vulnerability report verifier enabled and configured.

helm repo add ratify https://ratify-project.github.io/ratify
helm install ratify \
ratify/ratify --atomic \
--namespace gatekeeper-system \
--set featureFlags.RATIFY_CERT_ROTATION=true \
--set vulnerabilityreport.enabled=true \
--set vulnerabilityreport.maximumAge="24h" \
--set vulnerabilityreport.disallowedSeverities="{"high","critical"}" \
--set vulnerabilityreport.denylistCVEs={"CVE-2021-44228"}

Next, install the vulnerability report constraint template and constraint. The Constraint Template defines the policy "all container images used in K8s resources have at least one valid most recent vulnerability report attached to the image and the report has a valid Notary Project signature"

kubectl apply -f https://raw.githubusercontent.com/deislabs/ratify/23b143d07a53fd61557703c9836e486353959530/library/vulnerability-report-validation/template.yaml

kubectl apply -f https://raw.githubusercontent.com/deislabs/ratify/v1.1.0/library/vulnerability-report-validation/samples/constraint.yaml

An image myregistry.io/vuln/alpine:3.18.2 is scanned and a vulnerability report is generated. A reference artifact is generated:

  1. Use Trivy to scan myregistry.io/vuln/alpine:3.18.2 and output a report in SARIF format
    trivy image -q -f sarif myregistry.io/vuln/alpine:3.18.2 > trivy-sarif.json
  2. A tool such as oras is used to package, attach, and then push the report to registry
    • artifact-type MUST be application/sarif+json
    • org.opencontainers.image.created annotation with RFC3339 formatted current timestamp
    oras attach \
    --artifact-type application/sarif+json \
    --annotation "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
    myregistry.io/vuln/alpine:3.18.2 \
    trivy-sarif.json

The resulting image will have a single SARIF vulnerability report artifact attached:

> oras discover myregistry.io/vuln/alpine:3.18.2 -o tree
myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70
└── application/sarif+json
└── sha256:6170ef41e5a7c7088f86e3bc6b9c370cf97e613f7c7e359628c0119ec7d3d5f4

Finally we will attempt to deploy our test image myregistry.io/vuln/alpine:3.18.2. We expect this to FAIL since our vulnerability report indicates multiple HIGH and CRITICAL level severities:

> kubectl run vuln-alpine-image -n default --image=myregistry.io/vuln/alpine:3.18.2
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [vulnerability-report-validation-constraint] Subject failed verification: myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70

Taking a look at the Ratify logs reveals the failing report:

> kubectl logs deploy/ratify -n gatekeeper-system
time=2023-11-20T12:00:00Z level=info msg=verify result for subject myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70: {
"verifierReports": [
{
"subject": "myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70",
"isSuccess": false,
"name": "vulnerabilityreport",
"message": "vulnerability report validation failed",
"extensions": {
"scanner": "trivy",
"severityViolations": [
{
"defaultConfiguration": {
"level": "error"
},
"fullDescription": {
"text": "There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution."
},
"help": {
"markdown": "**Vulnerability CVE-2022-48174**\n| Severity | Package | Fixed Version | Link |\n| --- | --- | --- | --- |\n|CRITICAL|ssl_client|1.36.1-r1|[CVE-2022-48174](https://avd.aquasec.com/nvd/cve-2022-48174)|\n\nThere is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.",
"text": "Vulnerability CVE-2022-48174\nSeverity: CRITICAL\nPackage: ssl_client\nFixed Version: 1.36.1-r1\nLink: [CVE-2022-48174](https://avd.aquasec.com/nvd/cve-2022-48174)\nThere is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution."
},
"helpUri": "https://avd.aquasec.com/nvd/cve-2022-48174",
"id": "CVE-2022-48174",
"name": "OsPackageVulnerability",
"properties": {
"precision": "very-high",
"security-severity": "9.8",
"tags": [
"vulnerability",
"security",
"CRITICAL"
]
},
"shortDescription": {
"text": "stack overflow vulnerability in ash.c leads to arbitrary code execution"
}
},
...
]
}
}
]
}

Configuration

Kubernetes

Sample YAML

apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier
metadata:
name: verifier-vulnerabilityreport
spec:
name: vulnerabilityreport
artifactTypes: application/sarif+json
parameters:
maximumAge: 24h
disallowedSeverities:
- high
- critical
denylistCVEs:
- CVE-2021-44228 # Log4Shell
NameRequiredPathDescriptionDefault Value
maximumAgeNospec.parameters.maximumAgeThe string formatted max age of report""
disallowedSeveritiesNospec.parameters.disallowedSeveritiesString array of disallowed severities. Verification fails if ANY specified severity found. Most common severities: low, medium, high, critical, unknown[]
denylistCVEsNospec.parameters.denylistCVEsString array of CVE IDs. Verification fails if ANY specified CVE ID found. Find CVEs here[]
passthroughNospec.parameters.passthroughBypasses all verification except for maximumAge. Report content in extension section's report field of verifier report. Refer to Passthrough Mode section for more details.false
createdAnnotationNameNospec.parameters.createdAnnotationNameThe annotation name which specifies the artifact's creation timestamp. Note: Timestamp must use RFC3339org.opencontainers.image.created
schemaURLNospec.parameters.schemaURLURL for a JSON schema to validate report against. Default SARIF version 2.1.0 is used.""

CLI

Sample JSON

{
"store": {
"version": "1.0.0",
"plugins": [
{
"name": "oras",
}
]
},
"policy": {
"version": "1.0.0",
"plugin": {
"name": "configPolicy"
}
},
"verifier": {
"version": "1.0.0",
"plugins": [
{
"name": "vulnerabilityreport",
"artifactTypes": "application/sarif+json",
"maximumAge": "24h",
"disallowedSeverities": ["high", "critical"],
"denylistCVEs": ["CVE-2021-44228"]
}
]
}
}

Vulnerability Report with Signature Validation

Alice has a Kubernetes cluster. She wants to enable vulnerability report verification. In particular, she wants to make sure all container images used in K8s resources have the most recent vulnerability report attached to the image be valid. The report must:

  • be in SARIF format
  • generated from Trivy or Grype
  • created less than 24 hours ago
  • has no HIGH or CRITICAL severity level vulnerabilities in the report
  • does not contain the log4shell CVE

Furthermore, the most recent report being validated must have a verified Notary Project signature attached to it.

First, follow the first step of the manual quickstart to install Gatekeeper.

Second, install Ratify with the vulnerability report verifier enabled and configured. The notation verifier must also be configued and cert provided. Here, we will assume the report is signed using the quickstart image's signing key.

helm repo add ratify https://ratify-project.github.io/ratify
# download the notary verification certificate
curl -sSLO https://raw.githubusercontent.com/deislabs/ratify/main/test/testdata/notation.crt
helm install ratify \
ratify/ratify --atomic \
--namespace gatekeeper-system \
--set featureFlags.RATIFY_CERT_ROTATION=true \
--set-file notationCerts={./notation.crt} \
--set vulnerabilityreport.enabled=true \
--set vulnerabilityreport.maximumAge="24h" \
--set vulnerabilityreport.notaryProjectSignatureRequired=true \
--set vulnerabilityreport.disallowedSeverities="{"high","critical"}" \
--set vulnerabilityreport.denylistCVEs={"CVE-2021-44228"}

Next, install the vulnerability report constraint template and constraint. The Constraint Template defines the policy "all container images used in K8s resources have at least one valid most recent vulnerability report attached to the image and the report has a valid Notary Project signature"

curl https://raw.githubusercontent.com/deislabs/ratify/23b143d07a53fd61557703c9836e486353959530/library/vulnerability-report-validation/template.yaml | sed 's/require_signature := false/require_signature := true/' | kubectl apply -f -

kubectl apply -f https://raw.githubusercontent.com/deislabs/ratify/v1.1.0/library/vulnerability-report-validation/samples/constraint.yaml

An image myregistry.io/vuln/alpine:3.18.2 is scanned and a vulnerability report is generated. A reference artifact is generated:

  1. Use Trivy to scan myregistry.io/vuln/alpine:3.18.2 and output a report in SARIF format
    trivy image -q -f sarif myregistry.io/vuln/alpine:3.18.2 > trivy-sarif.json
  2. A tool such as oras is used to package, attach, and then push the report to registry
    • artifact-type MUST be application/sarif+json
    • org.opencontainers.image.created annotation with RFC3339 formatted current timestamp
    oras attach \
    --artifact-type application/sarif+json \
    --annotation "org.opencontainers.image.created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
    myregistry.io/vuln/alpine:3.18.2 \
    trivy-sarif.json
  3. Use notation to sign the report
    report_digest=$(oras discover myregistry.io/vuln/alpine:3.18.2 -o json | jq .manifests[0].digest | tr -d \")
    notation sign myregistry.io/vuln/alpine@$report_digest

The resulting image will have a single SARIF vulnerability report artifact attached with Notary Project signature attached to the report:

> oras discover myregistry.io/vuln/alpine:3.18.2 -o tree
myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70
└── application/sarif+json
└── sha256:6170ef41e5a7c7088f86e3bc6b9c370cf97e613f7c7e359628c0119ec7d3d5f4
└── application/vnd.cncf.notary.signature
└── sha256:1f89580da5b08d943ed0a403f1629928a21aec3f51bce7c38b0bea8b187b83a7

Finally we will attempt to deploy our test image myregistry.io/vuln/alpine:3.18.2. We expect this to FAIL since our vulnerability report indicates multiple HIGH and CRITICAL level severities:

> kubectl run vuln-alpine-image -n default --image=myregistry.io/vuln/alpine:3.18.2
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [vulnerability-report-validation-constraint] Subject failed verification: myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70

Taking a look at the Ratify logs reveals the failing report. Note how the report includes a successful Notary Project signature verification.

> kubectl logs deploy/ratify -n gatekeeper-system
time=2023-11-20T12:00:00Z level=info msg=verify result for subject myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70: {
"verifierReports": [
{
"subject": "myregistry.io/vuln/alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70",
"isSuccess": false,
"name": "vulnerabilityreport",
"message": "vulnerability report validation failed",
"extensions": {
"scanner": "trivy",
"severityViolations": [
{
"defaultConfiguration": {
"level": "error"
},
"fullDescription": {
"text": "There is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution."
},
"help": {
"markdown": "**Vulnerability CVE-2022-48174**\n| Severity | Package | Fixed Version | Link |\n| --- | --- | --- | --- |\n|CRITICAL|ssl_client|1.36.1-r1|[CVE-2022-48174](https://avd.aquasec.com/nvd/cve-2022-48174)|\n\nThere is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution.",
"text": "Vulnerability CVE-2022-48174\nSeverity: CRITICAL\nPackage: ssl_client\nFixed Version: 1.36.1-r1\nLink: [CVE-2022-48174](https://avd.aquasec.com/nvd/cve-2022-48174)\nThere is a stack overflow vulnerability in ash.c:6030 in busybox before 1.35. In the environment of Internet of Vehicles, this vulnerability can be executed from command to arbitrary code execution."
},
"helpUri": "https://avd.aquasec.com/nvd/cve-2022-48174",
"id": "CVE-2022-48174",
"name": "OsPackageVulnerability",
"properties": {
"precision": "very-high",
"security-severity": "9.8",
"tags": [
"vulnerability",
"security",
"CRITICAL"
]
},
"shortDescription": {
"text": "stack overflow vulnerability in ash.c leads to arbitrary code execution"
}
},
...
]
},
"nestedResults": [
{
"subject": "myregistry.io/vuln/alpine@sha256:6170ef41e5a7c7088f86e3bc6b9c370cf97e613f7c7e359628c0119ec7d3d5f4",
"isSuccess": true,
"name": "notation",
"message": "signature verification success",
"extensions": {
"Issuer": "CN=wabbit-networks.io,O=Notary,L=Seattle,ST=WA,C=US",
"SN": "CN=wabbit-networks.io,O=Notary,L=Seattle,ST=WA,C=US"
},
"artifactType": "application/vnd.cncf.notary.signature"
}
],
}
]
}

Passthrough Mode

There may be scenarios where the current vulnerability report rules enforced are not sufficient. Passthrough mode allows for the entire contents of the vulnerability report to be outputted in the resulting verifier report. Users can then apply custom Rego parsing the report contents either in the built-in Ratify Rego Policy provider or Gatekeeper's ConstraintTemplate.

To enable, set the passthrough parameter to true in the verifier configuration (JSON config or CRD).

Note: ALL other configuration parameters for the vulnerabilityreport verifier are NOT considered, EXCEPT for the maximumAge parameter.