diff --git a/.github/workflows/conformance.yaml b/.github/workflows/conformance.yaml index 907fd7bd95..f121a5300d 100644 --- a/.github/workflows/conformance.yaml +++ b/.github/workflows/conformance.yaml @@ -499,6 +499,89 @@ jobs: if: failure() uses: ./.github/actions/kyverno-logs + # runs conformance test suites with configuration: + custom-sigstore: + runs-on: ubuntu-latest + permissions: + packages: read + strategy: + fail-fast: false + matrix: + config: + - name: custom-sigstore + values: + - standard + - custom-sigstore + k8s-version: + - name: v1.25 + version: v1.25.11 + - name: v1.26 + version: v1.26.6 + - name: v1.27 + version: v1.27.3 + - name: v1.28 + version: v1.28.0 + tests: + - custom-sigstore + needs: prepare-images + name: ${{ matrix.k8s-version.name }} - ${{ matrix.config.name }} - ${{ matrix.tests }} + steps: + - name: Checkout + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - name: Setup build env + uses: ./.github/actions/setup-build-env + timeout-minutes: 10 + with: + build-cache-key: run-conformance + - name: Create kind cluster and setup Sigstore Scaffolding + uses: sigstore/scaffolding/actions/setup@9fb4937ae18ed8456d725e99cb2871d309673022 + - name: Create TUF values config map + run: | + kubectl create namespace kyverno + kubectl -n kyverno create configmap tufvalues --from-literal=TUF_MIRROR=$TUF_MIRROR --from-literal=FULCIO_URL=$FULCIO_URL --from-literal=REKOR_URL=$REKOR_URL --from-literal=CTLOG_URL=$CTLOG_URL --from-literal=ISSUER_URL=$ISSUER_URL + kubectl -n tuf-system get secrets tuf-root -oyaml | sed 's/namespace: .*/namespace: kyverno/' | kubectl create -f - + - name: Download kyverno images archive + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + name: kyverno.tar + - name: Load kyverno images archive in kind cluster + shell: bash + run: | + set -e + make kind-load-image-archive + - name: Install kyverno + shell: bash + run: | + set -e + export USE_CONFIG=${{ join(matrix.config.values, ',') }} + make kind-deploy-kyverno + - name: Install crane + uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c + - name: Install Cosign + uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 + - name: Create test image + shell: bash + run: | + DIGEST=$(crane digest cgr.dev/chainguard/static) + IMAGE_NAME=$(uuidgen | tr "[:upper:]" "[:lower:]") + TEST_IMAGE_URL=ttl.sh/${IMAGE_NAME}:1h + crane copy cgr.dev/chainguard/static@$DIGEST $TEST_IMAGE_URL + cosign initialize --mirror $TUF_MIRROR --root $TUF_MIRROR/root.json + COSIGN_EXPERIMENTAL=1 cosign sign --rekor-url $REKOR_URL --fulcio-url $FULCIO_URL $TEST_IMAGE_URL --identity-token `curl -s $ISSUER_URL` -y + echo "TEST_IMAGE_URL=$TEST_IMAGE_URL" >> $GITHUB_ENV + - name: Wait for kyverno ready + uses: ./.github/actions/kyverno-wait-ready + - name: Test with kuttl + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + ./.tools/kubectl-kuttl test ./test/conformance/kuttl/${{ matrix.tests }} --config ./test/conformance/kuttl/_config/common.yaml + - name: Debug failure + if: failure() + uses: ./.github/actions/kyverno-logs + # runs conformance test suites with configuration: default: runs-on: ubuntu-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index 23dc08fbe7..d9218b02ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Note +- Added `--tufRoot` and `--tufMirror` flags to configure tuf for custom sigstore deployments. - Remove description from deprecated fields in CRDs - Remove CLI `kyverno test manifest ...` commands (replaced by `kyverno create ...`). - Added `--caSecretName` and `--tlsSecretName` flags to control names of certificate related secrets. diff --git a/charts/kyverno/Chart.yaml b/charts/kyverno/Chart.yaml index c77123e581..1d91a13090 100644 --- a/charts/kyverno/Chart.yaml +++ b/charts/kyverno/Chart.yaml @@ -68,3 +68,5 @@ annotations: description: match conditions support in webhooks - kind: fixed description: missing image pull policy missing in a couple of deployments + - kind: added + description: added TUF flags for custom sigstore deployments diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index 53dd2222d5..6932d7f1b7 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -315,6 +315,8 @@ The chart values are organised per component. | features.registryClient.credentialHelpers | list | `["default","google","amazon","azure","github"]` | Enable registry client helpers | | features.reports.chunkSize | int | `1000` | Reports chunk size | | features.ttlController.reconciliationInterval | string | `"1m"` | Reconciliation interval for the label based cleanup manager | +| features.tuf.root | string | `nil` | Tuf root | +| features.tuf.mirror | string | `nil` | Tuf mirror | ### Admission controller diff --git a/charts/kyverno/templates/_helpers.tpl b/charts/kyverno/templates/_helpers.tpl index 72dc424bda..fbf51f993a 100644 --- a/charts/kyverno/templates/_helpers.tpl +++ b/charts/kyverno/templates/_helpers.tpl @@ -74,6 +74,14 @@ {{- with .ttlController -}} {{- $flags = append $flags (print "--ttlReconciliationInterval=" .reconciliationInterval) -}} {{- end -}} +{{- with .tuf -}} + {{- with .mirror -}} + {{- $flags = append $flags (print "--tufMirror=" .) -}} + {{- end -}} + {{- with .root -}} + {{- $flags = append $flags (print "--tufRoot=" .) -}} + {{- end -}} +{{- end -}} {{- with $flags -}} {{- toYaml . -}} {{- end -}} diff --git a/charts/kyverno/templates/admission-controller/deployment.yaml b/charts/kyverno/templates/admission-controller/deployment.yaml index 02761eef05..ae9c398fe4 100644 --- a/charts/kyverno/templates/admission-controller/deployment.yaml +++ b/charts/kyverno/templates/admission-controller/deployment.yaml @@ -166,6 +166,7 @@ spec: "policyExceptions" "protectManagedResources" "registryClient" + "tuf" ) | nindent 12 }} {{- range $key, $value := .Values.admissionController.container.extraArgs }} {{- if $value }} diff --git a/charts/kyverno/templates/reports-controller/deployment.yaml b/charts/kyverno/templates/reports-controller/deployment.yaml index ae71c0d602..b5ddd77480 100644 --- a/charts/kyverno/templates/reports-controller/deployment.yaml +++ b/charts/kyverno/templates/reports-controller/deployment.yaml @@ -121,6 +121,7 @@ spec: "policyExceptions" "reports" "registryClient" + "tuf" ) | nindent 12 }} {{- range $key, $value := .Values.reportsController.extraArgs }} {{- if $value }} diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index f8ce06e127..bd6ca2719c 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -447,6 +447,11 @@ features: ttlController: # -- Reconciliation interval for the label based cleanup manager reconciliationInterval: 1m + tuf: + # -- Tuf root + root: + # -- Tuf mirror + mirror: # Cleanup cronjobs to prevent internal resources from stacking up in the cluster cleanupJobs: diff --git a/cmd/internal/flag.go b/cmd/internal/flag.go index e4ab8681ff..824d13f09e 100644 --- a/cmd/internal/flag.go +++ b/cmd/internal/flag.go @@ -8,6 +8,7 @@ import ( "github.com/kyverno/kyverno/pkg/leaderelection" "github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/toggle" + "github.com/sigstore/sigstore/pkg/tuf" ) var ( @@ -38,6 +39,8 @@ var ( enableConfigMapCaching bool // cosign imageSignatureRepository string + tufMirror string + tufRoot string // registry client imagePullSecrets string allowInsecureRegistry bool @@ -98,6 +101,8 @@ func initDeferredLoadingFlags() { func initCosignFlags() { flag.StringVar(&imageSignatureRepository, "imageSignatureRepository", "", "(DEPRECATED, will be removed in 1.12) Alternate repository for image signatures. Can be overridden per rule via `verifyImages.Repository`.") + flag.StringVar(&tufMirror, "tufMirror", tuf.DefaultRemoteRoot, "Alternate TUF mirror for sigstore. If left blank, public sigstore one is used for cosign verification..") + flag.StringVar(&tufRoot, "tufRoot", "", "Alternate TUF root.json for sigstore. If left blank, public sigstore one is used for cosign verification.") } func initRegistryClientFlags() { diff --git a/cmd/internal/setup.go b/cmd/internal/setup.go index f9f89cb9bf..fa67d64bed 100644 --- a/cmd/internal/setup.go +++ b/cmd/internal/setup.go @@ -72,6 +72,9 @@ func Setup(config Configuration, name string, skipResourceFilters bool) (context if config.UsesImageVerifyCache() { imageVerifyCache = setupImageVerifyCache(ctx, logger) } + if config.UsesCosign() { + setupSigstoreTUF(ctx, logger) + } var leaderElectionClient kubeclient.UpstreamInterface if config.UsesLeaderElection() { leaderElectionClient = createKubernetesClient(logger, kubeclient.WithMetrics(metricsManager, metrics.KubeClient), kubeclient.WithTracing()) diff --git a/cmd/internal/tuf.go b/cmd/internal/tuf.go new file mode 100644 index 0000000000..fee327f1d6 --- /dev/null +++ b/cmd/internal/tuf.go @@ -0,0 +1,27 @@ +package internal + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/sigstore/cosign/v2/pkg/blob" + "github.com/sigstore/sigstore/pkg/tuf" +) + +func setupSigstoreTUF(ctx context.Context, logger logr.Logger) { + logger = logger.WithName("sigstore-tuf").WithValues("tufroot", tufRoot, "tufmirror", tufMirror) + logger.Info("setup tuf client for sigstore...") + var tufRootBytes []byte + var err error + if tufRoot != "" { + tufRootBytes, err = blob.LoadFileOrURL(tufRoot) + if err != nil { + checkError(logger, err, fmt.Sprintf("Failed to read alternate TUF root file %s : %v", tufRoot, err)) + } + } + logger.Info("Initializing TUF root") + if err := tuf.Initialize(ctx, tufMirror, tufRootBytes); err != nil { + checkError(logger, err, fmt.Sprintf("Failed to initialize TUF client from %s : %v", tufRoot, err)) + } +} diff --git a/scripts/config/custom-sigstore/kyverno.yaml b/scripts/config/custom-sigstore/kyverno.yaml new file mode 100644 index 0000000000..0bef1d2275 --- /dev/null +++ b/scripts/config/custom-sigstore/kyverno.yaml @@ -0,0 +1,61 @@ +features: + tuf: + root: "$(TUF_MIRROR)/root.json" + mirror: "$(TUF_MIRROR)" + +admissionController: + container: + extraEnvVars: + - name: TUF_MIRROR + valueFrom: + configMapKeyRef: + name: tufvalues + key: TUF_MIRROR + - name: FULCIO_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: FULCIO_URL + - name: REKOR_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: REKOR_URL + - name: CTLOG_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: CTLOG_URL + - name: ISSUER_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: ISSUER_URL + +reportsController: + extraEnvVars: + - name: TUF_MIRROR + valueFrom: + configMapKeyRef: + name: tufvalues + key: TUF_MIRROR + - name: FULCIO_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: FULCIO_URL + - name: REKOR_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: REKOR_URL + - name: CTLOG_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: CTLOG_URL + - name: ISSUER_URL + valueFrom: + configMapKeyRef: + name: tufvalues + key: ISSUER_URL diff --git a/test/conformance/kuttl/custom-sigstore/standard/basic/01-manifest.yaml b/test/conformance/kuttl/custom-sigstore/standard/basic/01-manifest.yaml new file mode 100644 index 0000000000..1cde6b52f7 --- /dev/null +++ b/test/conformance/kuttl/custom-sigstore/standard/basic/01-manifest.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: test-custom-sigstore +--- +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: basic-sigstore-test-policy +spec: + validationFailureAction: Enforce + background: false + webhookTimeoutSeconds: 30 + failurePolicy: Fail + rules: + - name: keyed-basic-rule + match: + any: + - resources: + kinds: + - Pod + context: + - name: tufvalues + configMap: + name: tufvalues + namespace: kyverno + verifyImages: + - imageReferences: + - "ttl.sh/*" + attestors: + - count: 1 + entries: + - keyless: + issuer: "https://kubernetes.default.svc.cluster.local" + subject: "*" + rekor: + url: "{{ tufvalues.data.REKOR_URL }}" + required: true \ No newline at end of file diff --git a/test/conformance/kuttl/custom-sigstore/standard/basic/02-assert.yaml b/test/conformance/kuttl/custom-sigstore/standard/basic/02-assert.yaml new file mode 100644 index 0000000000..d622499100 --- /dev/null +++ b/test/conformance/kuttl/custom-sigstore/standard/basic/02-assert.yaml @@ -0,0 +1,9 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: basic-sigstore-test-policy +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready diff --git a/test/conformance/kuttl/custom-sigstore/standard/basic/03-goodpod.yaml b/test/conformance/kuttl/custom-sigstore/standard/basic/03-goodpod.yaml new file mode 100644 index 0000000000..e1a3365404 --- /dev/null +++ b/test/conformance/kuttl/custom-sigstore/standard/basic/03-goodpod.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl -n test-custom-sigstore run test-sigstore --image=$TEST_IMAGE_URL \ No newline at end of file diff --git a/test/conformance/kuttl/custom-sigstore/standard/basic/04-assert.yaml b/test/conformance/kuttl/custom-sigstore/standard/basic/04-assert.yaml new file mode 100644 index 0000000000..07d111e808 --- /dev/null +++ b/test/conformance/kuttl/custom-sigstore/standard/basic/04-assert.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-sigstore + namespace: test-custom-sigstore \ No newline at end of file