Skip to content

Commit

Permalink
feat: add vulnerability report verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
akashsinghal committed Nov 20, 2023
1 parent 4ff7854 commit 9275cb5
Show file tree
Hide file tree
Showing 25 changed files with 7,700 additions and 14 deletions.
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_vulnerabilityreport -t ${REGISTRY}/deislabs/ratify:yourtag .
docker build --progress=plain --build-arg KUBE_VERSION="1.25.0" --build-arg TARGETOS="linux" --build-arg TARGETARCH="amd64" -f crd.Dockerfile -t ${REGISTRY}/localbuildcrd:yourtag ./charts/ratify/crds
```

Expand Down
27 changes: 25 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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
Expand Down Expand Up @@ -68,6 +69,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 @@ -139,7 +141,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 @@ -401,6 +403,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/v0.47.0/trivy_0.47.0_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 @@ -460,14 +482,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
11 changes: 11 additions & 0 deletions config/samples/config_v1beta1_verifier_vulnerabilityreport2.yaml
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 @@ -13,6 +13,7 @@ ARG build_sbom
ARG build_cosign
ARG build_licensechecker
ARG build_schemavalidator
ARG build_vulnerabilityreport

ENV CGO_ENABLED=0 \
GOOS=${TARGETOS} \
Expand All @@ -30,6 +31,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 {
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 {
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
count(report.nestedResults) > 0
some nestedResult in report.nestedResults
nestedResult.artifactType == "application/vnd.cncf.notary.signature"
nestedResult.isSuccess
}
Loading

0 comments on commit 9275cb5

Please sign in to comment.