diff --git a/.gitignore b/.gitignore index c3cfbd7bb..342740f91 100644 --- a/.gitignore +++ b/.gitignore @@ -177,3 +177,7 @@ node_modules # kustomize expanded Helm charts hack/**/charts + +# Some other stuff we don't want to check in +hack/builtin-experiments/data +hack/builtin-experiments/policy diff --git a/acceptance/examples/sigstore.rego b/acceptance/examples/sigstore.rego index 559bb9c02..0ed946913 100644 --- a/acceptance/examples/sigstore.rego +++ b/acceptance/examples/sigstore.rego @@ -3,6 +3,8 @@ package sigstore import rego.v1 # METADATA +# title: Image validation +# description: Check image and attestation signatures # custom: # short_name: valid deny contains result if { @@ -16,50 +18,50 @@ _errors contains error if { } _errors contains error if { - not data.config.default_sigstore_opts + not _sigstore_opts error := "default sigstore options not set" } _errors contains error if { - info := ec.sigstore.verify_image(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_image(_image_ref, _sigstore_opts) some raw_error in info.errors error := sprintf("image signature verification failed: %s", [raw_error]) } _errors contains error if { - info := ec.sigstore.verify_image(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_image(_image_ref, _sigstore_opts) count(info.signatures) == 0 error := "verification successful, but no image signatures found" } _errors contains error if { - info := ec.sigstore.verify_image(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_image(_image_ref, _sigstore_opts) some sig in info.signatures not valid_signature(sig) error := sprintf("not a valid image signature: %s", [sig]) } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) some raw_error in info.errors error := sprintf("image attestation verification failed: %s", [raw_error]) } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) count(info.attestations) == 0 error := "verification successful, but no attestations found" } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) some att in info.attestations count(att.signatures) == 0 error := sprintf("attestation has no signatures: %s", [att]) } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) some att in info.attestations some sig in att.signatures not valid_signature(sig) @@ -67,7 +69,7 @@ _errors contains error if { } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) some att in info.attestations att.statement.predicateType != "https://slsa.dev/provenance/v0.2" @@ -75,7 +77,7 @@ _errors contains error if { } _errors contains error if { - info := ec.sigstore.verify_attestation(_image_ref, data.config.default_sigstore_opts) + info := ec.sigstore.verify_attestation(_image_ref, _sigstore_opts) some att in info.attestations builder_id := _builder_id(att) builder_id != "https://tekton.dev/chains/v2" @@ -84,6 +86,8 @@ _errors contains error if { _image_ref := input.image.ref +_sigstore_opts := data.config.default_sigstore_opts + valid_signature(sig) if { type_name(sig.keyid) == "string" type_name(sig.signature) == "string" diff --git a/hack/builtin-experiments/demo.sh b/hack/builtin-experiments/demo.sh new file mode 100755 index 000000000..e5629aab3 --- /dev/null +++ b/hack/builtin-experiments/demo.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +# Copyright The Enterprise Contract Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +GIT_ROOT=$(git rev-parse --show-toplevel) +EC=${EC:-"${GIT_ROOT}/dist/ec"} +LOCAL_DIR=${GIT_ROOT}/hack/builtin-experiments +DATA_DIR=${LOCAL_DIR}/data/data +POLICY_DIR=${LOCAL_DIR}/policy/policy + +mkdir -p ${DATA_DIR} ${POLICY_DIR} + +# This has no attestation currently... +#IMAGE=${IMAGE:-"quay.io/redhat-appstudio/ec-golden-image:latest"} + +# This is the ec build for TAS so it should be good 🐕🥣 +IMAGE=${IMAGE:-"quay.io/redhat-user-workloads/rhtap-contract-tenant/ec-v02/cli-v02:c862b0f77bb10082d1440e0d4b6a4e9645b83382"} + +# The image digest must be specified explictly so go look it up +IMAGE_DIGEST=$(skopeo inspect --no-tags docker://$IMAGE | jq -r .Digest) +FULL_IMAGE_REF="$IMAGE@$IMAGE_DIGEST" + +# Input looks like this +INPUT_JSON='{ + "image": { + "ref": "'$FULL_IMAGE_REF'" + } +}' + +# A minimal ECP using local files +# ec looks for specific subdirs under the source's root location +# so that's why we have policy/policy and data/data +POLICY_YAML=' +sources: + - policy: + - '$POLICY_DIR' + - github.com/simonbaird/ec-policies//policy/lib?ref=builtin-experiments + - github.com/simonbaird/ec-policies//policy/release?ref=builtin-experiments + + data: + - '$DATA_DIR' + - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest + - github.com/release-engineering/rhtap-ec-policy//data + + config: + include: + # Fixme: I dont think it is running the sigstore check sin sigstore.rego + # now, not sure why + - "sigstore" + - "@slsa3" +' + +# Public key for the signature of the image we're verifying +PUBLIC_KEY="-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZP/0htjhVt2y0ohjgtIIgICOtQtA +naYJRuLprwIv6FDhZ5yFjYUEtsmoNcW7rx2KM6FOXGsCX3BNc7qhHELT+g== +-----END PUBLIC KEY-----" + +# Hack hack... +echo '{ + "sigstore_opts": { + "ignore_rekor": true, + "public_key": "'${PUBLIC_KEY//$'\n'/\\n}'" + } +}' > ${DATA_DIR}/sigstore_opts.json + +# The acceptance test rego is pretty much prod-ready.. :) +# Tweak one line to make it work with the sigstore_opts data we just created above +sed \ + -e 's/^_sigstore_opts :=.*/_sigstore_opts := object.union(data.config.default_sigstore_opts, data.sigstore_opts)/' \ + -e 's/^package sigstore/package policy.release.sigstore/' \ + ${GIT_ROOT}/acceptance/examples/sigstore.rego \ + > ${POLICY_DIR}/sigstore.rego + +echo -e "\n* Input:\n" +echo "$INPUT_JSON" | yq -P + +echo -e "\n* EC results:\n" +$EC validate input \ + --file <(echo $INPUT_JSON) \ + --policy "$(echo "$POLICY_YAML" | yq -ojson)" \ + --show-successes \ + | yq -P + +# For debugging... +#echo "$INPUT_JSON" > i.json +#ec opa eval \ +# --input i.json \ +# 'data.lib._input_attestations' \ +# --data ${DATA_DIR} \ +# --data /home/sbaird/code/ec-policies/policy/lib \ +# --data /home/sbaird/code/ec-policies/policy/release | yq -P