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 15, 2023
1 parent 29a38b6 commit 1630608
Show file tree
Hide file tree
Showing 20 changed files with 7,498 additions and 3 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
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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 @@ -468,6 +469,7 @@ e2e-build-local-ratify-image:
--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
15 changes: 15 additions & 0 deletions config/samples/config_v1beta1_verifier_vulnerabilityreport.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier
metadata:
name: verifier-vulnerabilityreport
spec:
name: vulnerabilityreport
artifactTypes: application/sarif+json
parameters:
nestedReferences: application/vnd.cncf.notary.signature
maximumAge: 24h
disallowedSeverity:
- high
- critical
denylistCVEs:
- CVE-2021-44228 # Log4Shell
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/deislabs/ratify

go 1.20
go 1.21

toolchain go1.21.3

// Accidentally published prior to 1.0.0 release
retract (
Expand Down Expand Up @@ -34,6 +36,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
89 changes: 89 additions & 0 deletions go.sum

Large diffs are not rendered by default.

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 1630608

Please sign in to comment.