From c7ea42e8efbcd3b153d883822639c1ffd27d6b8a Mon Sep 17 00:00:00 2001 From: leigh capili Date: Tue, 26 May 2026 17:03:59 +0100 Subject: [PATCH 1/2] hack: sigstore e2e test harness Scripts and testdata for running cosign verification tests against a local sigstore stack on kind. Uses zot (referrers API) and registry:2 (tag fallback) with the scaffold Helm chart. Covers v2/v3 key-pair, v2/v3 keyless with trustedRootSecretRef, tlog, combined refs, wrong key/identity/rekor material, and registry auth. Signed-off-by: leigh capili --- hack/sigstore-test/Makefile | 33 ++++ hack/sigstore-test/build-and-load.sh | 26 +++ hack/sigstore-test/fetch-cosign.sh | 43 +++++ hack/sigstore-test/kind-down.sh | 22 +++ hack/sigstore-test/kind-up.sh | 85 ++++++++ hack/sigstore-test/port-forward.sh | 29 +++ hack/sigstore-test/setup-sigstore.sh | 52 +++++ hack/sigstore-test/test-signing.sh | 181 ++++++++++++++++++ .../combined-secretref-trustedroot.yaml | 17 ++ .../sigstore-test/testdata/registry-auth.yaml | 17 ++ .../testdata/registry2-fallback.yaml | 48 +++++ hack/sigstore-test/testdata/v2-key.yaml | 15 ++ .../testdata/v2-keyless-trustedroot.yaml | 18 ++ hack/sigstore-test/testdata/v3-key-tlog.yaml | 15 ++ hack/sigstore-test/testdata/v3-key.yaml | 15 ++ .../testdata/v3-keyless-trustedroot.yaml | 18 ++ .../testdata/wrong-identity.yaml | 18 ++ hack/sigstore-test/testdata/wrong-key.yaml | 15 ++ .../testdata/wrong-rekor-key.yaml | 18 ++ 19 files changed, 685 insertions(+) create mode 100644 hack/sigstore-test/Makefile create mode 100755 hack/sigstore-test/build-and-load.sh create mode 100755 hack/sigstore-test/fetch-cosign.sh create mode 100755 hack/sigstore-test/kind-down.sh create mode 100755 hack/sigstore-test/kind-up.sh create mode 100755 hack/sigstore-test/port-forward.sh create mode 100755 hack/sigstore-test/setup-sigstore.sh create mode 100755 hack/sigstore-test/test-signing.sh create mode 100644 hack/sigstore-test/testdata/combined-secretref-trustedroot.yaml create mode 100644 hack/sigstore-test/testdata/registry-auth.yaml create mode 100644 hack/sigstore-test/testdata/registry2-fallback.yaml create mode 100644 hack/sigstore-test/testdata/v2-key.yaml create mode 100644 hack/sigstore-test/testdata/v2-keyless-trustedroot.yaml create mode 100644 hack/sigstore-test/testdata/v3-key-tlog.yaml create mode 100644 hack/sigstore-test/testdata/v3-key.yaml create mode 100644 hack/sigstore-test/testdata/v3-keyless-trustedroot.yaml create mode 100644 hack/sigstore-test/testdata/wrong-identity.yaml create mode 100644 hack/sigstore-test/testdata/wrong-key.yaml create mode 100644 hack/sigstore-test/testdata/wrong-rekor-key.yaml diff --git a/hack/sigstore-test/Makefile b/hack/sigstore-test/Makefile new file mode 100644 index 000000000..7ab2f47e3 --- /dev/null +++ b/hack/sigstore-test/Makefile @@ -0,0 +1,33 @@ +# Sigstore test harness for PR #2003 validation +# Usage: +# make -f hack/sigstore-test/Makefile up # create cluster + registry +# make -f hack/sigstore-test/Makefile sigstore # install sigstore stack +# make -f hack/sigstore-test/Makefile build # build and load source-controller +# make -f hack/sigstore-test/Makefile cosign # fetch cosign v2 and v3 binaries +# make -f hack/sigstore-test/Makefile test # run signing/verification tests +# make -f hack/sigstore-test/Makefile all # do everything +# make -f hack/sigstore-test/Makefile down # tear down + +SCRIPT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +.PHONY: all up sigstore down build cosign test + +all: up sigstore cosign build test + +up: + bash $(SCRIPT_DIR)/kind-up.sh + +sigstore: + bash $(SCRIPT_DIR)/setup-sigstore.sh + +down: + bash $(SCRIPT_DIR)/kind-down.sh + +build: + bash $(SCRIPT_DIR)/build-and-load.sh + +cosign: + bash $(SCRIPT_DIR)/fetch-cosign.sh + +test: + bash $(SCRIPT_DIR)/test-signing.sh diff --git a/hack/sigstore-test/build-and-load.sh b/hack/sigstore-test/build-and-load.sh new file mode 100755 index 000000000..338ffda43 --- /dev/null +++ b/hack/sigstore-test/build-and-load.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Build source-controller and load it into the kind cluster. +set -euo pipefail + +CLUSTER_NAME="${CLUSTER_NAME:-sigstore-test}" +IMG="${IMG:-test/source-controller}" +TAG="${TAG:-latest}" +BUILD_PLATFORM="${BUILD_PLATFORM:-linux/arm64}" + +REPO_ROOT="$(git rev-parse --show-toplevel)" + +echo ">>> building source-controller image" +cd "${REPO_ROOT}" +make docker-build IMG="${IMG}" TAG="${TAG}" BUILD_PLATFORMS="${BUILD_PLATFORM}" BUILD_ARGS=--load + +echo ">>> loading image into kind cluster ${CLUSTER_NAME}" +kind load docker-image --name "${CLUSTER_NAME}" "${IMG}:${TAG}" + +echo ">>> deploying source-controller" +make dev-deploy IMG="${IMG}" TAG="${TAG}" + +echo ">>> waiting for source-controller rollout" +kubectl -n source-system rollout status deploy/source-controller --timeout=2m + +echo ">>> source-controller deployed" +kubectl -n source-system get pods diff --git a/hack/sigstore-test/fetch-cosign.sh b/hack/sigstore-test/fetch-cosign.sh new file mode 100755 index 000000000..f8fcc2440 --- /dev/null +++ b/hack/sigstore-test/fetch-cosign.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Fetch cosign v2 and v3 binaries for testing. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BIN_DIR="${SCRIPT_DIR}/bin" +mkdir -p "${BIN_DIR}" + +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" +case "${ARCH}" in + x86_64|amd64) ARCH="amd64" ;; + aarch64|arm64) ARCH="arm64" ;; + *) echo "unsupported arch: ${ARCH}"; exit 1 ;; +esac + +# cosign v3 (latest) +COSIGN_V3_VERSION="${COSIGN_V3_VERSION:-v3.0.6}" +COSIGN_V3_URL="https://github.com/sigstore/cosign/releases/download/${COSIGN_V3_VERSION}/cosign-${OS}-${ARCH}" + +# cosign v2 (last v2 release) +COSIGN_V2_VERSION="${COSIGN_V2_VERSION:-v2.4.3}" +COSIGN_V2_URL="https://github.com/sigstore/cosign/releases/download/${COSIGN_V2_VERSION}/cosign-${OS}-${ARCH}" + +fetch_binary() { + local name="$1" url="$2" dest="$3" + if [ -f "${dest}" ]; then + echo ">>> ${name} already exists at ${dest}" + else + echo ">>> downloading ${name} from ${url}" + curl -fSL -o "${dest}" "${url}" + chmod +x "${dest}" + fi + "${dest}" version 2>&1 | head -3 + echo "" +} + +fetch_binary "cosign-v3" "${COSIGN_V3_URL}" "${BIN_DIR}/cosign-v3" +fetch_binary "cosign-v2" "${COSIGN_V2_URL}" "${BIN_DIR}/cosign-v2" + +echo "=== Cosign binaries ready ===" +echo " v2: ${BIN_DIR}/cosign-v2" +echo " v3: ${BIN_DIR}/cosign-v3" diff --git a/hack/sigstore-test/kind-down.sh b/hack/sigstore-test/kind-down.sh new file mode 100755 index 000000000..38710eb5b --- /dev/null +++ b/hack/sigstore-test/kind-down.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +CLUSTER_NAME="${CLUSTER_NAME:-sigstore-test}" +REG_NAME="${CLUSTER_NAME}-registry" + +echo ">>> tearing down sigstore test environment" + +echo ">>> killing port-forwards" +pkill -f "kubectl.*port-forward.*sigstore" 2>/dev/null || true + +echo ">>> uninstalling scaffold Helm release" +helm uninstall scaffold -n sigstore 2>/dev/null || true + +echo ">>> deleting kind cluster ${CLUSTER_NAME}" +kind delete cluster --name "${CLUSTER_NAME}" 2>/dev/null || true + +echo ">>> removing registries" +docker rm -f "${REG_NAME}" 2>/dev/null || true +docker rm -f "${CLUSTER_NAME}-registry2" 2>/dev/null || true + +echo ">>> done" diff --git a/hack/sigstore-test/kind-up.sh b/hack/sigstore-test/kind-up.sh new file mode 100755 index 000000000..f228e2ea9 --- /dev/null +++ b/hack/sigstore-test/kind-up.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# Spin up a kind cluster with a local OCI registry on the same Docker network. +# Sigstore stack installation is a separate step (see setup-sigstore.sh). +set -euo pipefail + +CLUSTER_NAME="${CLUSTER_NAME:-sigstore-test}" +REG_NAME="${CLUSTER_NAME}-registry" +REG_LOCALHOST_PORT="${REG_LOCALHOST_PORT:-5555}" +REG_CLUSTER_PORT=5000 +NODE_IMAGE="${KIND_NODE_IMAGE:-kindest/node:v1.32.2}" + +echo "=== Phase 1: Local OCI Registries ===" +# Primary registry: zot (supports OCI 1.1 referrers API natively) +if [ "$(docker inspect -f '{{.State.Running}}' "${REG_NAME}" 2>/dev/null || true)" != 'true' ]; then + echo ">>> starting zot ${REG_NAME} on localhost:${REG_LOCALHOST_PORT}" + docker run -d --restart=always \ + -p "127.0.0.1:${REG_LOCALHOST_PORT}:5000" \ + --name "${REG_NAME}" \ + ghcr.io/project-zot/zot-linux-$(uname -m | sed 's/aarch64/arm64/;s/x86_64/amd64/'):latest +else + echo ">>> registry ${REG_NAME} already running" +fi + +# Fallback registry: registry:2 (tag-based referrers only, no referrers API) +REG2_NAME="${CLUSTER_NAME}-registry2" +REG2_LOCALHOST_PORT="${REG2_LOCALHOST_PORT:-5557}" +if [ "$(docker inspect -f '{{.State.Running}}' "${REG2_NAME}" 2>/dev/null || true)" != 'true' ]; then + echo ">>> starting registry:2 ${REG2_NAME} on localhost:${REG2_LOCALHOST_PORT}" + docker run -d --restart=always \ + -p "127.0.0.1:${REG2_LOCALHOST_PORT}:${REG_CLUSTER_PORT}" \ + --name "${REG2_NAME}" \ + registry:2 +else + echo ">>> registry:2 ${REG2_NAME} already running" +fi + +echo "=== Phase 2: Kind Cluster ===" +if ! kind get clusters 2>/dev/null | grep -q "^${CLUSTER_NAME}$"; then + echo ">>> creating kind cluster ${CLUSTER_NAME}" + cat <>> cluster ${CLUSTER_NAME} already exists" +fi + +# Connect registries to kind network +if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${REG_NAME}" 2>/dev/null)" = 'null' ]; then + echo ">>> connecting registry:3 to kind network" + docker network connect "kind" "${REG_NAME}" +fi +if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${REG2_NAME}" 2>/dev/null)" = 'null' ]; then + echo ">>> connecting registry:2 to kind network" + docker network connect "kind" "${REG2_NAME}" +fi + +echo ">>> waiting for cluster readiness" +kubectl wait node "${CLUSTER_NAME}-control-plane" --for=condition=ready --timeout=2m +kubectl wait --for=condition=ready -n kube-system -l k8s-app=kube-dns pod --timeout=2m + +# Allow unauthenticated OIDC discovery (needed for Fulcio to validate SA tokens) +kubectl create clusterrolebinding oidc-reviewer \ + --clusterrole=system:service-account-issuer-discovery \ + --group=system:unauthenticated 2>/dev/null || true + +echo "" +echo "=== Cluster Ready ===" +echo " cluster: ${CLUSTER_NAME}" +echo " registry3: localhost:${REG_LOCALHOST_PORT} (in-cluster: ${REG_NAME}:${REG_CLUSTER_PORT})" +echo " registry2: localhost:${REG2_LOCALHOST_PORT} (in-cluster: ${REG2_NAME}:${REG_CLUSTER_PORT})" +echo "" +echo "Next: run setup-sigstore.sh to install the sigstore stack" diff --git a/hack/sigstore-test/port-forward.sh b/hack/sigstore-test/port-forward.sh new file mode 100755 index 000000000..5542f97c9 --- /dev/null +++ b/hack/sigstore-test/port-forward.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Set up port-forwarding to sigstore services and export env vars. +# Source this file: source hack/sigstore-test/port-forward.sh +set -euo pipefail + +echo ">>> setting up port-forwarding to sigstore services" + +# Kill any existing port-forwards +pkill -f "kubectl.*port-forward.*sigstore" 2>/dev/null || true +sleep 1 + +# Rekor +kubectl -n rekor-system port-forward svc/rekor-server 3000:80 &>/dev/null & +# Fulcio +kubectl -n fulcio-system port-forward svc/fulcio-server 5555:80 &>/dev/null & +# TUF +kubectl -n tuf-system port-forward svc/tuf 8081:80 &>/dev/null & + +sleep 2 + +export REKOR_URL="http://localhost:3000" +export FULCIO_URL="http://localhost:5555" +export TUF_MIRROR="http://localhost:8081" + +echo " REKOR_URL=${REKOR_URL}" +echo " FULCIO_URL=${FULCIO_URL}" +echo " TUF_MIRROR=${TUF_MIRROR}" +echo "" +echo "Port-forwarding active. Use 'kill %1 %2 %3' to stop." diff --git a/hack/sigstore-test/setup-sigstore.sh b/hack/sigstore-test/setup-sigstore.sh new file mode 100755 index 000000000..3052eacfa --- /dev/null +++ b/hack/sigstore-test/setup-sigstore.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# Install sigstore stack into the kind cluster using the scaffold Helm chart. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo "=== Installing Sigstore Stack ===" +helm repo add sigstore https://sigstore.github.io/helm-charts 2>/dev/null || true +helm repo update sigstore + +echo ">>> installing sigstore/scaffold (this takes a few minutes)..." +helm upgrade --install scaffold sigstore/scaffold \ + --namespace sigstore --create-namespace \ + --timeout 10m \ + --wait + +echo ">>> waiting for sigstore namespaces" +for ns in trillian-system rekor-system fulcio-system ctlog-system tuf-system; do + if kubectl get ns "${ns}" &>/dev/null; then + echo " ${ns}: waiting for deployments..." + for deploy in $(kubectl get deploy -n "${ns}" -o name 2>/dev/null); do + kubectl rollout status --timeout=5m -n "${ns}" "${deploy}" 2>/dev/null || true + done + kubectl wait --timeout=5m -n "${ns}" --for=condition=Complete jobs --all 2>/dev/null || true + fi +done + +echo "=== Extracting PKI Material ===" +mkdir -p "${SCRIPT_DIR}/pki" + +kubectl -n fulcio-system get secrets fulcio-pub-key -ojsonpath='{.data.cert}' 2>/dev/null \ + | base64 -d > "${SCRIPT_DIR}/pki/fulcio.crt.pem" && echo " extracted fulcio.crt.pem" || echo " WARN: fulcio cert not found" + +kubectl -n ctlog-system get secret ctlog-public-key -ojsonpath='{.data.public}' 2>/dev/null \ + | base64 -d > "${SCRIPT_DIR}/pki/ctfe.pub" && echo " extracted ctfe.pub" || echo " WARN: ctlog pub key not found" + +# Rekor public key is fetched via API since the scaffold chart uses an in-memory signer +echo " fetching rekor public key via API..." +kubectl -n rekor-system port-forward svc/rekor-server 3000:80 &>/dev/null & +PF_PID=$! +sleep 2 +if curl -sf http://localhost:3000/api/v1/log/publicKey > "${SCRIPT_DIR}/pki/rekor.pub" 2>/dev/null; then + echo " extracted rekor.pub" +else + echo " WARN: could not fetch rekor public key" +fi +kill $PF_PID 2>/dev/null || true + +echo "" +echo "=== Sigstore Stack Ready ===" +echo " pki: ${SCRIPT_DIR}/pki/" +ls -la "${SCRIPT_DIR}/pki/" 2>/dev/null diff --git a/hack/sigstore-test/test-signing.sh b/hack/sigstore-test/test-signing.sh new file mode 100755 index 000000000..367e5b265 --- /dev/null +++ b/hack/sigstore-test/test-signing.sh @@ -0,0 +1,181 @@ +#!/usr/bin/env bash +# test-signing.sh: Validate cosign v2/v3 x key-pair/keyless verification flows. +# +# Prerequisites: +# - kind cluster running (hack/sigstore-test/kind-up.sh) +# - sigstore stack installed (hack/sigstore-test/setup-sigstore.sh) +# - source-controller deployed (hack/sigstore-test/build-and-load.sh) +# - fulcio config patched for cluster.local issuer +# - rekor-np and fulcio-np NodePort services created +set -eoux pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PKI_DIR="${SCRIPT_DIR}/pki" +KEYS_DIR="${SCRIPT_DIR}/keys" +TESTDATA="${SCRIPT_DIR}/testdata" + +REG="localhost:5555" +REG2="localhost:5557" +NS="source-system" + +NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}') +REKOR_NP=$(kubectl -n rekor-system get svc rekor-np -o jsonpath='{.spec.ports[0].nodePort}') +FULCIO_NP=$(kubectl -n fulcio-system get svc fulcio-np -o jsonpath='{.spec.ports[0].nodePort}') +REKOR_URL="http://${NODE_IP}:${REKOR_NP}" +FULCIO_URL="http://${NODE_IP}:${FULCIO_NP}" + +# --- Setup keys and secrets --- + +mkdir -p "$KEYS_DIR" "$PKI_DIR" + +if [ ! -f "$KEYS_DIR/test.key" ]; then + COSIGN_PASSWORD="" cosign generate-key-pair --output-key-prefix="$KEYS_DIR/test" +fi +if [ ! -f "$KEYS_DIR/wrong.key" ]; then + COSIGN_PASSWORD="" cosign generate-key-pair --output-key-prefix="$KEYS_DIR/wrong" +fi +if [ ! -f "$KEYS_DIR/signing-config-notlog.json" ]; then + cosign signing-config create --out "$KEYS_DIR/signing-config-notlog.json" +fi + +if [ ! -s "$PKI_DIR/trusted_root.json" ]; then + cosign trusted-root create \ + --fulcio="url=http://fulcio-server.fulcio-system.svc,certificate-chain=$PKI_DIR/fulcio.crt.pem" \ + --rekor="url=http://rekor-server.rekor-system.svc,public-key=$PKI_DIR/rekor.pub,start-time=2024-01-01T00:00:00Z" \ + --ctfe="url=http://ctlog.ctlog-system.svc,public-key=$PKI_DIR/ctfe.pub,start-time=2024-01-01T00:00:00Z" \ + --out "$PKI_DIR/trusted_root.json" +fi + +# Wrong trusted root (uses wrong.pub as rekor key) +cosign trusted-root create \ + --fulcio="url=http://fulcio-server.fulcio-system.svc,certificate-chain=$PKI_DIR/fulcio.crt.pem" \ + --rekor="url=http://rekor-server.rekor-system.svc,public-key=$KEYS_DIR/wrong.pub,start-time=2024-01-01T00:00:00Z" \ + --ctfe="url=http://ctlog.ctlog-system.svc,public-key=$PKI_DIR/ctfe.pub,start-time=2024-01-01T00:00:00Z" \ + --out "$PKI_DIR/wrong_trusted_root.json" + +kubectl -n "$NS" create secret generic cosign-test-key \ + --from-file=cosign.pub="$KEYS_DIR/test.pub" --dry-run=client -o yaml | kubectl apply -f - +kubectl -n "$NS" create secret generic cosign-wrong-key \ + --from-file=cosign.pub="$KEYS_DIR/wrong.pub" --dry-run=client -o yaml | kubectl apply -f - +kubectl -n "$NS" create secret generic sigstore-trusted-root \ + --from-file=trusted_root.json="$PKI_DIR/trusted_root.json" --dry-run=client -o yaml | kubectl apply -f - +kubectl -n "$NS" create secret generic sigstore-wrong-root \ + --from-file=trusted_root.json="$PKI_DIR/wrong_trusted_root.json" --dry-run=client -o yaml | kubectl apply -f - +kubectl -n "$NS" create secret docker-registry registry-creds \ + --docker-server="sigstore-test-registry:5000" \ + --docker-username=user --docker-password=pass \ + --dry-run=client -o yaml | kubectl apply -f - + +# --- Helper --- + +push_artifact() { + local ref="$1" + local tmp + tmp=$(mktemp -d) + echo "{\"test\":\"$(basename "$ref")\"}" > "$tmp/data.yaml" + flux push artifact "oci://$ref" --path="$tmp" --source=test --revision=v1 + rm -rf "$tmp" +} + +# --- Sign artifacts --- + +echo "Run cosign v2-style key-pair tests" +push_artifact "$REG/test/v2-key:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --tlog-upload=false --use-signing-config=false --new-bundle-format=false \ + --allow-insecure-registry "$REG/test/v2-key:v1" + +echo "Run cosign v3 bundle key-pair tests" +push_artifact "$REG/test/v3-key:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --signing-config="$KEYS_DIR/signing-config-notlog.json" \ + --allow-insecure-registry "$REG/test/v3-key:v1" + +echo "Run cosign v2-style keyless tests" +push_artifact "$REG/test/v2-keyless:v1" +cosign sign \ + --trusted-root="$PKI_DIR/trusted_root.json" \ + --fulcio-url="$FULCIO_URL" --rekor-url="$REKOR_URL" \ + --allow-insecure-registry --use-signing-config=false --new-bundle-format=false \ + --identity-token="$(kubectl create token default -n default --audience=sigstore)" \ + --yes "$REG/test/v2-keyless:v1" + +echo "Run cosign v3 bundle keyless tests" +push_artifact "$REG/test/v3-keyless:v1" +cosign sign \ + --trusted-root="$PKI_DIR/trusted_root.json" \ + --fulcio-url="$FULCIO_URL" --rekor-url="$REKOR_URL" \ + --allow-insecure-registry --use-signing-config=false \ + --identity-token="$(kubectl create token default -n default --audience=sigstore)" \ + --yes "$REG/test/v3-keyless:v1" + +echo "Run cosign v3 key-pair with tlog tests" +push_artifact "$REG/test/v3-key-tlog:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --trusted-root="$PKI_DIR/trusted_root.json" \ + --rekor-url="$REKOR_URL" \ + --allow-insecure-registry --use-signing-config=false \ + --yes "$REG/test/v3-key-tlog:v1" + +echo "Run registry auth test" +push_artifact "$REG/test/authed:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --tlog-upload=false --use-signing-config=false --new-bundle-format=false \ + --allow-insecure-registry "$REG/test/authed:v1" + +echo "Run registry:2 fallback tests" +push_artifact "$REG2/test/v3-key-fallback:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --signing-config="$KEYS_DIR/signing-config-notlog.json" \ + --allow-insecure-registry "$REG2/test/v3-key-fallback:v1" + +push_artifact "$REG2/test/v3-keyless-fallback:v1" +cosign sign \ + --trusted-root="$PKI_DIR/trusted_root.json" \ + --fulcio-url="$FULCIO_URL" --rekor-url="$REKOR_URL" \ + --allow-insecure-registry --use-signing-config=false \ + --identity-token="$(kubectl create token default -n default --audience=sigstore)" \ + --yes "$REG2/test/v3-keyless-fallback:v1" + +push_artifact "$REG2/test/v3-key-tlog-fallback:v1" +COSIGN_PASSWORD="" cosign sign --key="$KEYS_DIR/test.key" \ + --trusted-root="$PKI_DIR/trusted_root.json" \ + --rekor-url="$REKOR_URL" \ + --allow-insecure-registry --use-signing-config=false \ + --yes "$REG2/test/v3-key-tlog-fallback:v1" + +# --- Apply and verify --- + +echo "Run OCIRepository verify tests" +kubectl -n "$NS" apply -f "${TESTDATA}/v2-key.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/v3-key.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/v2-keyless-trustedroot.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/v3-keyless-trustedroot.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/v3-key-tlog.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/combined-secretref-trustedroot.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/registry-auth.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/registry2-fallback.yaml" + +kubectl -n "$NS" wait ocirepository/test-v2-key --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-key --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v2-keyless --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-keyless --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-key-tlog --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-combined --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-authed --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-key-fallback --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-keyless-fallback --for=condition=ready --timeout=1m +kubectl -n "$NS" wait ocirepository/test-v3-key-tlog-fallback --for=condition=ready --timeout=1m + +echo "Run negative verification tests" +kubectl -n "$NS" apply -f "${TESTDATA}/wrong-key.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/wrong-identity.yaml" +kubectl -n "$NS" apply -f "${TESTDATA}/wrong-rekor-key.yaml" + +# Negative tests: wait for VerificationError condition +sleep 30 +kubectl -n "$NS" get ocirepository test-wrong-key -o jsonpath='{.status.conditions[?(@.type=="Ready")].reason}' | grep -q "VerificationError" +kubectl -n "$NS" get ocirepository test-wrong-identity -o jsonpath='{.status.conditions[?(@.type=="Ready")].reason}' | grep -q "VerificationError" +kubectl -n "$NS" get ocirepository test-wrong-rekor -o jsonpath='{.status.conditions[?(@.type=="Ready")].reason}' | grep -q "VerificationError" + +echo "All sigstore verification tests passed!" diff --git a/hack/sigstore-test/testdata/combined-secretref-trustedroot.yaml b/hack/sigstore-test/testdata/combined-secretref-trustedroot.yaml new file mode 100644 index 000000000..af45c4c0f --- /dev/null +++ b/hack/sigstore-test/testdata/combined-secretref-trustedroot.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-combined +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-key + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key + trustedRootSecretRef: + name: sigstore-trusted-root diff --git a/hack/sigstore-test/testdata/registry-auth.yaml b/hack/sigstore-test/testdata/registry-auth.yaml new file mode 100644 index 000000000..dd27bce12 --- /dev/null +++ b/hack/sigstore-test/testdata/registry-auth.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-authed +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/authed + ref: + tag: v1 + insecure: true + secretRef: + name: registry-creds + verify: + provider: cosign + secretRef: + name: cosign-test-key diff --git a/hack/sigstore-test/testdata/registry2-fallback.yaml b/hack/sigstore-test/testdata/registry2-fallback.yaml new file mode 100644 index 000000000..aa0437ce8 --- /dev/null +++ b/hack/sigstore-test/testdata/registry2-fallback.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-key-fallback +spec: + interval: 5m + url: oci://sigstore-test-registry2:5000/test/v3-key-fallback + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-keyless-fallback +spec: + interval: 5m + url: oci://sigstore-test-registry2:5000/test/v3-keyless-fallback + ref: + tag: v1 + insecure: true + verify: + provider: cosign + trustedRootSecretRef: + name: sigstore-trusted-root + matchOIDCIdentity: + - issuer: "^https://kubernetes\\.default\\.svc" + subject: "^https://kubernetes\\.io/namespaces/default/serviceaccounts/default$" +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-key-tlog-fallback +spec: + interval: 5m + url: oci://sigstore-test-registry2:5000/test/v3-key-tlog-fallback + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key diff --git a/hack/sigstore-test/testdata/v2-key.yaml b/hack/sigstore-test/testdata/v2-key.yaml new file mode 100644 index 000000000..bc3bfaa49 --- /dev/null +++ b/hack/sigstore-test/testdata/v2-key.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v2-key +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-key + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key diff --git a/hack/sigstore-test/testdata/v2-keyless-trustedroot.yaml b/hack/sigstore-test/testdata/v2-keyless-trustedroot.yaml new file mode 100644 index 000000000..be437b5f9 --- /dev/null +++ b/hack/sigstore-test/testdata/v2-keyless-trustedroot.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v2-keyless +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-keyless + ref: + tag: v1 + insecure: true + verify: + provider: cosign + trustedRootSecretRef: + name: sigstore-trusted-root + matchOIDCIdentity: + - issuer: "^https://kubernetes\\.default\\.svc" + subject: "^https://kubernetes\\.io/namespaces/default/serviceaccounts/default$" diff --git a/hack/sigstore-test/testdata/v3-key-tlog.yaml b/hack/sigstore-test/testdata/v3-key-tlog.yaml new file mode 100644 index 000000000..1e4b21915 --- /dev/null +++ b/hack/sigstore-test/testdata/v3-key-tlog.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-key-tlog +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v3-key-tlog + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key diff --git a/hack/sigstore-test/testdata/v3-key.yaml b/hack/sigstore-test/testdata/v3-key.yaml new file mode 100644 index 000000000..97b5e46e2 --- /dev/null +++ b/hack/sigstore-test/testdata/v3-key.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-key +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v3-key + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-test-key diff --git a/hack/sigstore-test/testdata/v3-keyless-trustedroot.yaml b/hack/sigstore-test/testdata/v3-keyless-trustedroot.yaml new file mode 100644 index 000000000..e81324884 --- /dev/null +++ b/hack/sigstore-test/testdata/v3-keyless-trustedroot.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-v3-keyless +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v3-keyless + ref: + tag: v1 + insecure: true + verify: + provider: cosign + trustedRootSecretRef: + name: sigstore-trusted-root + matchOIDCIdentity: + - issuer: "^https://kubernetes\\.default\\.svc" + subject: "^https://kubernetes\\.io/namespaces/default/serviceaccounts/default$" diff --git a/hack/sigstore-test/testdata/wrong-identity.yaml b/hack/sigstore-test/testdata/wrong-identity.yaml new file mode 100644 index 000000000..3c6b67ec2 --- /dev/null +++ b/hack/sigstore-test/testdata/wrong-identity.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-wrong-identity +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-keyless + ref: + tag: v1 + insecure: true + verify: + provider: cosign + trustedRootSecretRef: + name: sigstore-trusted-root + matchOIDCIdentity: + - issuer: "^https://wrong-issuer\\.example\\.com$" + subject: "^wrong-subject@example\\.com$" diff --git a/hack/sigstore-test/testdata/wrong-key.yaml b/hack/sigstore-test/testdata/wrong-key.yaml new file mode 100644 index 000000000..42ba87c39 --- /dev/null +++ b/hack/sigstore-test/testdata/wrong-key.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-wrong-key +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-key + ref: + tag: v1 + insecure: true + verify: + provider: cosign + secretRef: + name: cosign-wrong-key diff --git a/hack/sigstore-test/testdata/wrong-rekor-key.yaml b/hack/sigstore-test/testdata/wrong-rekor-key.yaml new file mode 100644 index 000000000..bdcf5482a --- /dev/null +++ b/hack/sigstore-test/testdata/wrong-rekor-key.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: test-wrong-rekor +spec: + interval: 5m + url: oci://sigstore-test-registry:5000/test/v2-keyless + ref: + tag: v1 + insecure: true + verify: + provider: cosign + trustedRootSecretRef: + name: sigstore-wrong-root + matchOIDCIdentity: + - issuer: "^https://kubernetes\\.default\\.svc" + subject: "^https://kubernetes\\.io/namespaces/default/serviceaccounts/default$" From 106bf504621d01936f8f2e80ba2e14fd9899e859 Mon Sep 17 00:00:00 2001 From: leigh capili Date: Tue, 26 May 2026 17:04:03 +0100 Subject: [PATCH 2/2] ci: run sigstore e2e in parallel with existing e2e New sigstore-linux-amd64 job deploys a local sigstore stack and runs the verification test suite concurrently with kind-linux-amd64. Signed-off-by: leigh capili --- .github/workflows/e2e.yaml | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 570d4edd5..86d12e80d 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -33,3 +33,73 @@ jobs: continue-on-error: true run: | kubectl -n source-system logs -l app=source-controller + + sigstore-linux-amd64: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Test suite setup + uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.9.0 + with: + go-version: 1.26.x + - name: Setup cosign + env: + COSIGN_VERSION: v3.0.6 + COSIGN_SHA256: c956e5dfcac53d52bcf058360d579472f0c1d2d9b69f55209e256fe7783f4c74 + run: | + curl -fsSL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" -o /usr/local/bin/cosign + echo "${COSIGN_SHA256} /usr/local/bin/cosign" | sha256sum -c - + chmod +x /usr/local/bin/cosign + - name: Setup Flux CLI + run: curl -fsSL https://fluxcd.io/install.sh | bash + - name: Create cluster and registries + run: hack/sigstore-test/kind-up.sh + - name: Install sigstore stack + run: hack/sigstore-test/setup-sigstore.sh + - name: Patch Fulcio config + run: | + kubectl -n fulcio-system get cm fulcio-server-config -o json | \ + python3 -c " + import json, sys + cm = json.load(sys.stdin) + config = json.loads(cm['data']['config.json']) + config['OIDCIssuers']['https://kubernetes.default.svc.cluster.local'] = { + 'IssuerURL': 'https://kubernetes.default.svc.cluster.local', + 'ClientID': 'sigstore', + 'Type': 'kubernetes' + } + cm['data']['config.json'] = json.dumps(config, indent=2) + json.dump(cm, sys.stdout) + " | kubectl apply -f - + kubectl -n fulcio-system rollout restart deploy/fulcio-server + kubectl -n fulcio-system rollout status deploy/fulcio-server --timeout=2m + - name: Expose sigstore services + run: | + kubectl -n rekor-system expose deploy rekor-server --name=rekor-np --type=NodePort --port=80 --target-port=3000 + cat <