Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
70 changes: 70 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.26.x
- name: Setup Kubernetes
uses: helm/kind-action@v1
with:
install_only: true
- name: Setup tools
run: |
curl -fsSL https://fluxcd.io/install.sh | bash
helm repo add sigstore https://sigstore.github.io/helm-charts
- 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 <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: fulcio-np
namespace: fulcio-system
spec:
type: NodePort
selector:
app.kubernetes.io/name: fulcio
app.kubernetes.io/instance: scaffold
ports:
- port: 80
targetPort: 5555
protocol: TCP
EOF
- name: Build and deploy source-controller
run: |
BUILD_PLATFORM=linux/amd64 hack/sigstore-test/build-and-load.sh
- name: Run sigstore verification tests
run: hack/sigstore-test/test-signing.sh
- name: Print controller logs
if: always()
continue-on-error: true
run: |
kubectl -n source-system logs deploy/source-controller
33 changes: 33 additions & 0 deletions hack/sigstore-test/Makefile
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions hack/sigstore-test/build-and-load.sh
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions hack/sigstore-test/fetch-cosign.sh
Original file line number Diff line number Diff line change
@@ -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"
22 changes: 22 additions & 0 deletions hack/sigstore-test/kind-down.sh
Original file line number Diff line number Diff line change
@@ -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"
85 changes: 85 additions & 0 deletions hack/sigstore-test/kind-up.sh
Original file line number Diff line number Diff line change
@@ -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 <<EOF | kind create cluster --name "${CLUSTER_NAME}" --image "${NODE_IMAGE}" --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${REG_LOCALHOST_PORT}"]
endpoint = ["http://${REG_NAME}:${REG_CLUSTER_PORT}"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${REG_NAME}:${REG_CLUSTER_PORT}"]
endpoint = ["http://${REG_NAME}:${REG_CLUSTER_PORT}"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${REG2_LOCALHOST_PORT}"]
endpoint = ["http://${REG2_NAME}:${REG_CLUSTER_PORT}"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${REG2_NAME}:${REG_CLUSTER_PORT}"]
endpoint = ["http://${REG2_NAME}:${REG_CLUSTER_PORT}"]
EOF
else
echo ">>> 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"
29 changes: 29 additions & 0 deletions hack/sigstore-test/port-forward.sh
Original file line number Diff line number Diff line change
@@ -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."
52 changes: 52 additions & 0 deletions hack/sigstore-test/setup-sigstore.sh
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading