Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add vulnerability report verifier #1173

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/publish-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
--build-arg build_sbom=true \
--build-arg build_licensechecker=true \
--build-arg build_schemavalidator=true \
--build-arg build_vulnerabilityreport=true \
--build-arg LDFLAGS="-X github.com/deislabs/ratify/internal/version.Version=$(TAG)" \
--label org.opencontainers.image.revision=${{ github.sha }} \
-t ${{ steps.prepare.outputs.ref }} \
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Follow the steps below to build and deploy a Ratify image with your private chan
export REGISTRY=yourregistry
docker buildx create --use

docker buildx build -f httpserver/Dockerfile --platform linux/amd64 --build-arg build_cosign=true --build-arg build_sbom=true --build-arg build_licensechecker=true --build-arg build_schemavalidator=true -t ${REGISTRY}/deislabs/ratify:yourtag .
docker buildx build -f httpserver/Dockerfile --platform linux/amd64 --build-arg build_cosign=true --build-arg build_sbom=true --build-arg build_licensechecker=true --build-arg build_schemavalidator=true --build-arg build_vulnerabilityreport=true -t ${REGISTRY}/deislabs/ratify:yourtag .
docker build --progress=plain --build-arg KUBE_VERSION="1.27.7" --build-arg TARGETOS="linux" --build-arg TARGETARCH="amd64" -f crd.Dockerfile -t ${REGISTRY}/localbuildcrd:yourtag ./charts/ratify/crds
```

Expand Down
30 changes: 27 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ SYFT_VERSION ?= v0.76.0
YQ_VERSION ?= v4.34.1
YQ_BINARY ?= yq_linux_amd64
ALPINE_IMAGE ?= alpine@sha256:93d5a28ff72d288d69b5997b8ba47396d2cbb62a72b5d87cd3351094b5d578a0
ALPINE_IMAGE_VULNERABLE ?= alpine@sha256:25fad2a32ad1f6f510e528448ae1ec69a28ef81916a004d3629874104f8a7f70
REDIS_IMAGE_TAG ?= 7.0-debian-11
CERT_ROTATION_ENABLED ?= false
REGO_POLICY_ENABLED ?= false
SBOM_TOOL_VERSION ?=v1.2.0
TRIVY_VERSION ?= 0.47.0

# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.24.2
Expand Down Expand Up @@ -82,6 +84,7 @@ build-plugins:
go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/sample/... -o ./bin/plugins/ ./plugins/verifier/sample
go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/sbom/... -o ./bin/plugins/ ./plugins/verifier/sbom
go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/schemavalidator/... -o ./bin/plugins/ ./plugins/verifier/schemavalidator
go build -cover -coverpkg=github.com/deislabs/ratify/plugins/verifier/vulnerabilityreport/... -o ./bin/plugins/ ./plugins/verifier/vulnerabilityreport

.PHONY: install
install:
Expand Down Expand Up @@ -153,7 +156,7 @@ test-e2e: generate-rotation-certs
EXPIRING_CERT_DIR=.staging/rotation/expiring-certs CERT_DIR=.staging/rotation GATEKEEPER_VERSION=${GATEKEEPER_VERSION} bats -t ${BATS_PLUGIN_TESTS_FILE}

.PHONY: test-e2e-cli
test-e2e-cli: e2e-dependencies e2e-create-local-registry e2e-notation-setup e2e-notation-leaf-cert-setup e2e-cosign-setup e2e-licensechecker-setup e2e-sbom-setup e2e-schemavalidator-setup
test-e2e-cli: e2e-dependencies e2e-create-local-registry e2e-notation-setup e2e-notation-leaf-cert-setup e2e-cosign-setup e2e-licensechecker-setup e2e-sbom-setup e2e-schemavalidator-setup e2e-vulnerabilityreport-setup
rm ${GOCOVERDIR} -rf
mkdir ${GOCOVERDIR} -p
RATIFY_DIR=${INSTALL_DIR} TEST_REGISTRY=${TEST_REGISTRY} ${GITHUB_WORKSPACE}/bin/bats -t ${BATS_CLI_TESTS_FILE}
Expand Down Expand Up @@ -396,7 +399,7 @@ e2e-schemavalidator-setup:
mkdir -p .staging/schemavalidator

# Install Trivy
curl -L https://github.com/aquasecurity/trivy/releases/download/v0.35.0/trivy_0.35.0_Linux-64bit.tar.gz --output .staging/schemavalidator/trivy.tar.gz
curl -L https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz --output .staging/schemavalidator/trivy.tar.gz
tar -zxf .staging/schemavalidator/trivy.tar.gz -C .staging/schemavalidator

# Build/Push Images
Expand All @@ -415,6 +418,26 @@ e2e-schemavalidator-setup:
${TEST_REGISTRY}/all:v0 \
.staging/schemavalidator/trivy-scan.sarif:application/sarif+json

e2e-vulnerabilityreport-setup:
rm -rf .staging/vulnerabilityreport
mkdir -p .staging/vulnerabilityreport

# Install Trivy
curl -L https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz --output .staging/vulnerabilityreport/trivy.tar.gz
tar -zxf .staging/vulnerabilityreport/trivy.tar.gz -C .staging/vulnerabilityreport

# Build/Push Image
printf 'FROM ${ALPINE_IMAGE_VULNERABLE}\nCMD ["echo", "vulnerabilityreport image"]' > .staging/vulnerabilityreport/Dockerfile
docker build --no-cache -t ${TEST_REGISTRY}/vulnerabilityreport:v0 .staging/vulnerabilityreport
docker push ${TEST_REGISTRY}/vulnerabilityreport:v0

# Create/Attach Scan Result
.staging/vulnerabilityreport/trivy image --format sarif --output .staging/vulnerabilityreport/trivy-sarif.json ${TEST_REGISTRY}/vulnerabilityreport:v0
${GITHUB_WORKSPACE}/bin/oras attach \
--artifact-type application/sarif+json \
${TEST_REGISTRY}/vulnerabilityreport:v0 \
.staging/vulnerabilityreport/trivy-sarif.json:application/sarif+json

e2e-inlinecert-setup:
rm -rf .staging/inlinecert
mkdir -p .staging/inlinecert
Expand Down Expand Up @@ -468,14 +491,15 @@ e2e-deploy-base-ratify: e2e-notation-setup e2e-notation-leaf-cert-setup e2e-inli

rm mount_config.json

e2e-deploy-ratify: e2e-notation-setup e2e-notation-leaf-cert-setup e2e-cosign-setup e2e-cosign-setup e2e-licensechecker-setup e2e-sbom-setup e2e-schemavalidator-setup e2e-inlinecert-setup e2e-build-crd-image e2e-build-local-ratify-image e2e-helm-deploy-ratify
e2e-deploy-ratify: e2e-notation-setup e2e-notation-leaf-cert-setup e2e-cosign-setup e2e-cosign-setup e2e-licensechecker-setup e2e-sbom-setup e2e-schemavalidator-setup e2e-vulnerabilityreport-setup e2e-inlinecert-setup e2e-build-crd-image e2e-build-local-ratify-image e2e-helm-deploy-ratify

e2e-build-local-ratify-image:
docker build --progress=plain --no-cache \
--build-arg build_cosign=true \
--build-arg build_sbom=true \
--build-arg build_licensechecker=true \
--build-arg build_schemavalidator=true \
--build-arg build_vulnerabilityreport=true \
-f ./httpserver/Dockerfile \
-t localbuild:test .
kind load docker-image --name kind localbuild:test
Expand Down
14 changes: 14 additions & 0 deletions config/samples/config_v1beta1_verifier_vulnerabilityreport.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier
metadata:
name: verifier-vulnerabilityreport
spec:
name: vulnerabilityreport
artifactTypes: application/sarif+json
parameters:
maximumAge: 24h
disallowedSeverity:
- high
- critical
denylistCVEs:
- CVE-2021-44228 # Log4Shell
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier
metadata:
name: verifier-vulnerabilityreport
spec:
name: vulnerabilityreport
artifactTypes: application/sarif+json
parameters:
maximumAge: 24h
denylistCVEs:
- CVE-2021-44228 # Log4Shell
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
github.com/open-policy-agent/opa v0.58.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc5
github.com/owenrumney/go-sarif/v2 v2.3.0
github.com/pkg/errors v0.9.1
github.com/sigstore/cosign/v2 v2.2.1
github.com/sigstore/sigstore v1.7.5
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/x
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
Expand Down Expand Up @@ -640,6 +641,9 @@ github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U=
github.com/owenrumney/go-sarif/v2 v2.3.0 h1:wP5yEpI53zr0v5cBmagXzLbHZp9Oylyo3AJDpfLBITs=
github.com/owenrumney/go-sarif/v2 v2.3.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
Expand Down Expand Up @@ -769,6 +773,8 @@ github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinC
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/veraison/go-cose v1.2.0 h1:Ok0Hr3GMAf8K/1NB4sV65QGgCiukG1w1QD+H5tmt0Ow=
github.com/veraison/go-cose v1.2.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/go-gitlab v0.93.2 h1:kNNf3BYNYn/Zkig0B89fma12l36VLcYSGu7OnaRlRDg=
Expand Down Expand Up @@ -804,6 +810,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4=
github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
Expand Down
2 changes: 2 additions & 0 deletions httpserver/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ARG build_sbom
ARG build_cosign
ARG build_licensechecker
ARG build_schemavalidator
ARG build_vulnerabilityreport

ENV CGO_ENABLED=0 \
GOOS=${TARGETOS} \
Expand All @@ -43,6 +44,7 @@ RUN if [ "$build_sbom" = "true" ]; then go build -o /app/out/plugins/ /app/plugi
RUN if [ "$build_cosign" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/cosign; fi
RUN if [ "$build_licensechecker" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/licensechecker; fi
RUN if [ "$build_schemavalidator" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/schemavalidator; fi
RUN if [ "$build_vulnerabilityreport" = "true" ]; then go build -o /app/out/plugins/ /app/plugins/verifier/vulnerabilityreport; fi

FROM $BASEIMAGE
LABEL org.opencontainers.image.source https://github.com/deislabs/ratify
Expand Down
11 changes: 11 additions & 0 deletions library/notation-nested-validation/samples/constraint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: NotationNestedValidation
metadata:
name: notation-nested-constraint
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["default"]
80 changes: 80 additions & 0 deletions library/notation-nested-validation/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: notationnestedvalidation
spec:
crd:
spec:
names:
kind: NotationNestedValidation
validation:
openAPIV3Schema:
type: object
properties:
issuer:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package notationnestedvalidation
import future.keywords.if

remote_data := response {
images := [img | img = input.review.object.spec.containers[_].image]
images_init := [img | img = input.review.object.spec.initContainers[_].image]
images_ephemeral := [img | img = input.review.object.spec.ephemeralContainers[_].image]
other_images := array.concat(images_init, images_ephemeral)
all_images := array.concat(other_images, images)
response := external_data({"provider": "ratify-provider", "keys": all_images})
}

violation[{"msg": msg}] {
general_violation[{"result": msg}]
}

# Check if there are any system errors
general_violation[{"result": result}] {
err := remote_data.system_error
err != ""
result := sprintf("System error calling external data provider: %s", [err])
}
# Check if there are errors for any of the images
general_violation[{"result": result}] {
count(remote_data.errors) > 0
result := sprintf("Error validating one or more images: %s", remote_data.errors)
}

# Check if the success criteria is true
general_violation[{"result": result}] {
subject_validation := remote_data.responses[_]
subject_result := subject_validation[1]
failed_verify(subject_result)
result := sprintf("Subject failed verification: %s", [subject_validation[0]])
}

failed_verify(reports) if {
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
newReports := {"nestedResults": reports.verifierReports}
has_subject_failed_verify(newReports)
}

has_subject_failed_verify(nestedReports) if {
[path, value] := walk(nestedReports)
path[count(path) - 1] == "nestedResults"
not notary_signature_pass_verify(value)
}

notary_signature_pass_verify(nestedReports) if {
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
count_with_success := notary_signature_signature_count(nestedReports)
count_with_success > 0
}

notary_signature_signature_count(nestedReports) := number if {
sigs := [x |
some i
nestedReports[i].isSuccess == true
nestedReports[i].artifactType == "application/vnd.cncf.notary.signature"
x := nestedReports[i].subject
]
number := count(sigs)
}

11 changes: 11 additions & 0 deletions library/vulnerability-report-validation/samples/constraint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: VulnerabilityReportValidation
metadata:
name: vulnerability-report-validation-constraint
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["default"]
82 changes: 82 additions & 0 deletions library/vulnerability-report-validation/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: vulnerabilityreportvalidation
spec:
crd:
spec:
names:
kind: VulnerabilityReportValidation
validation:
openAPIV3Schema:
type: object
properties:
issuer:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package vulnerabilityreportvalidation

# TODO: add support for custom reason message propagating to user
import future.keywords.if
import future.keywords.in
import future.keywords.every

default require_signature := false # change to true to require notation signature on vulnerability report

# Get data from Ratify
remote_data := response {
images := [img | img = input.review.object.spec.containers[_].image]
images_init := [img | img = input.review.object.spec.initContainers[_].image]
images_ephemeral := [img | img = input.review.object.spec.ephemeralContainers[_].image]
other_images := array.concat(images_init, images_ephemeral)
all_images := array.concat(other_images, images)
response := external_data({"provider": "ratify-provider", "keys": all_images})
}

violation[{"msg": msg}] {
general_violation[{"result": msg}]
}

# Check if there are any system errors
general_violation[{"result": result}] {
err := remote_data.system_error
err != ""
result := sprintf("System error calling external data provider: %s", [err])
}

# Check if there are errors for any of the images
general_violation[{"result": result}] {
count(remote_data.errors) > 0
result := sprintf("Error validating one or more images: %s", remote_data.errors)
}

# Check if the success criteria is true
general_violation[{"result": result}] {
subject_validation := remote_data.responses[_]
subject_result := subject_validation[1]
vuln_results := [res | subject_result.verifierReports[i].name == "vulnerabilityreport"; res := subject_result.verifierReports[i]]
count(vuln_results) > 0
not process_vuln_reports(vuln_results)
result := sprintf("Subject failed verification: %s", [subject_validation[0]])
}

process_vuln_reports(reports) if {
# At least one report must be valid
some vuln_report in reports
vuln_report.isSuccess == true
valid_signatures(vuln_report)
}

valid_signatures(_) := true {
require_signature == false
}

valid_signatures(report) := true {
require_signature
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
count(report.nestedResults) > 0
some nestedResult in report.nestedResults
nestedResult.artifactType == "application/vnd.cncf.notary.signature"
nestedResult.isSuccess
}
Loading
Loading