From b9ed384c3e4dd46d01b305d83c2ec020b0830932 Mon Sep 17 00:00:00 2001 From: Min Zhang Date: Wed, 20 May 2026 18:54:59 -0400 Subject: [PATCH] feat: support protected repositories with Tekton Chains provenance Add support for cloning source code from protected (private) Git repositories in the Tekton supply-chain pipeline. Credentials are stored in Vault and delivered to the pipeline via an ExternalSecret that generates .git-credentials and .gitconfig files for the git-clone task's basic-auth workspace. Supply-chain chart changes: - Add init task (skopeo pre-flight image check, skip rebuild) - Add optional git-auth workspace and Chains provenance results (CHAINS-GIT_URL, CHAINS-GIT_COMMIT, IMAGE_URL, IMAGE_DIGEST) - Add ExternalSecret for git credentials (Opaque with .git-credentials) - Conditionally attach git-credentials secret to pipeline SA - Add skopeo image to tasks.images for the init task - Migrate all Tekton resources from v1beta1 to v1 API Generator and feature fragments: - Add protected-repos feature fragment with git.credentials overrides and qtodo.repository placeholder - Add --git-repo CLI argument to gen-feature-variants.py (required when protected-repos feature is enabled) - Add ignoreDifferences for Tekton Task/Pipeline defaulted fields to the supply-chain feature fragment Default values-hub.yaml: - Extend hub-supply-chain-jwt-secret Vault policy to cover secret/data/hub/supply-chain/* - Add commented-out Tekton ignoreDifferences, git.credentials overrides, and qtodo.repository override Documentation: - Update docs/supply-chain.md with protected repos setup, generator --git-repo usage, and git-auth workspace selection - Update scripts/gen-feature-variants.md with --git-repo examples - Add git-credentials entry to values-secret.yaml.template Signed-off-by: Min Zhang --- .../templates/pipeline-qtodo.yaml | 51 +++++++++++- .../templates/pipelinerun-qtodo.yaml | 2 +- .../templates/rbac/pipeline-sa.yaml | 5 +- .../secrets/qtodo-git-credentials.yaml | 38 +++++++++ .../templates/tasks/build-artifact.yaml | 2 +- .../templates/tasks/generate-sbom.yaml | 2 +- charts/supply-chain/templates/tasks/init.yaml | 55 +++++++++++++ .../templates/tasks/restart-qtodo.yaml | 2 +- .../templates/tasks/sbom-attest.yaml | 2 +- .../templates/tasks/sign-artifact.yaml | 2 +- .../templates/tasks/sign-image.yaml | 2 +- .../templates/tasks/upload-sbom.yaml | 2 +- .../templates/tasks/verify-artifact.yaml | 2 +- .../templates/tasks/verify-image.yaml | 2 +- charts/supply-chain/values.yaml | 10 +++ docs/supply-chain.md | 82 ++++++++++++++++++- scripts/features/features.yaml | 5 ++ scripts/features/protected-repos.yaml | 18 ++++ scripts/features/supply-chain.yaml | 11 +++ scripts/gen-feature-variants.md | 30 +++++++ scripts/gen-feature-variants.py | 60 +++++++++++++- values-hub.yaml | 20 +++++ values-secret.yaml.template | 17 ++++ 23 files changed, 404 insertions(+), 18 deletions(-) create mode 100644 charts/supply-chain/templates/secrets/qtodo-git-credentials.yaml create mode 100644 charts/supply-chain/templates/tasks/init.yaml create mode 100644 scripts/features/protected-repos.yaml diff --git a/charts/supply-chain/templates/pipeline-qtodo.yaml b/charts/supply-chain/templates/pipeline-qtodo.yaml index 1db94743..0116d440 100644 --- a/charts/supply-chain/templates/pipeline-qtodo.yaml +++ b/charts/supply-chain/templates/pipeline-qtodo.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Pipeline metadata: name: qtodo-supply-chain @@ -8,12 +8,20 @@ spec: params: - name: git-url type: string - description: The URL of the public Github qtodo repository + description: The URL of the qtodo repository (public or protected) default: {{ .Values.qtodo.repository | quote }} - name: git-revision type: string - description: The revision of the public Github qtodo repository + description: The revision of the qtodo repository default: {{ .Values.qtodo.revision }} + - name: rebuild + type: string + description: Force rebuild the image even if it already exists + default: "false" + - name: skip-checks + type: string + description: Skip pre-build checks against existing image + default: "false" - name: qtodo-build-cmd type: string description: The command to build the qtodo artifact @@ -94,9 +102,44 @@ spec: workspaces: - name: qtodo-source - name: registry-auth-config + - name: git-auth + optional: true + + results: + - name: CHAINS-GIT_URL + description: The git URL used for the build (Tekton Chains provenance) + value: $(tasks.qtodo-clone-repository.results.URL) + - name: CHAINS-GIT_COMMIT + description: The git commit SHA used for the build (Tekton Chains provenance) + value: $(tasks.qtodo-clone-repository.results.COMMIT) + - name: IMAGE_URL + description: The image URL built by the pipeline (Tekton Chains provenance) + value: $(tasks.qtodo-build-image.results.IMAGE_URL) + - name: IMAGE_DIGEST + description: The image digest built by the pipeline (Tekton Chains provenance) + value: $(tasks.qtodo-build-image.results.IMAGE_DIGEST) tasks: + - name: init + taskRef: + name: init + kind: Task + params: + - name: image-url + value: $(params.image-target) + - name: rebuild + value: $(params.rebuild) + - name: skip-checks + value: $(params.skip-checks) + - name: qtodo-clone-repository + runAfter: + - init + when: + - input: $(tasks.init.results.build) + operator: in + values: + - "true" taskRef: resolver: cluster params: @@ -114,6 +157,8 @@ spec: workspaces: - name: output workspace: qtodo-source + - name: basic-auth + workspace: git-auth - name: qtodo-build-artifact runAfter: diff --git a/charts/supply-chain/templates/pipelinerun-qtodo.yaml b/charts/supply-chain/templates/pipelinerun-qtodo.yaml index 36b52d4b..e87cdaf4 100644 --- a/charts/supply-chain/templates/pipelinerun-qtodo.yaml +++ b/charts/supply-chain/templates/pipelinerun-qtodo.yaml @@ -92,7 +92,7 @@ spec: fi cat <<'MANIFEST' | oc create -f - - apiVersion: tekton.dev/v1beta1 + apiVersion: tekton.dev/v1 kind: PipelineRun metadata: generateName: qtodo-supply-chain- diff --git a/charts/supply-chain/templates/rbac/pipeline-sa.yaml b/charts/supply-chain/templates/rbac/pipeline-sa.yaml index 06dff21e..ba97e2d4 100644 --- a/charts/supply-chain/templates/rbac/pipeline-sa.yaml +++ b/charts/supply-chain/templates/rbac/pipeline-sa.yaml @@ -10,4 +10,7 @@ metadata: argocd.argoproj.io/compare-options: IgnoreExtraneous argocd.argoproj.io/syncOptions: ServerSideApply=true secrets: - - name: qtodo-registry-auth \ No newline at end of file + - name: qtodo-registry-auth +{{- if .Values.git.credentials.enabled }} + - name: qtodo-git-credentials +{{- end }} \ No newline at end of file diff --git a/charts/supply-chain/templates/secrets/qtodo-git-credentials.yaml b/charts/supply-chain/templates/secrets/qtodo-git-credentials.yaml new file mode 100644 index 00000000..e844b456 --- /dev/null +++ b/charts/supply-chain/templates/secrets/qtodo-git-credentials.yaml @@ -0,0 +1,38 @@ +{{- if .Values.git.credentials.enabled }} +{{- $host := .Values.git.credentials.host }} +{{- $hostBare := $host | trimPrefix "https://" | trimPrefix "http://" }} +{{- $userKey := .Values.git.credentials.usernameKey }} +{{- $passKey := .Values.git.credentials.passwordKey }} +--- +apiVersion: "external-secrets.io/v1beta1" +kind: ExternalSecret +metadata: + name: qtodo-git-credentials + namespace: {{ .Release.Namespace | default .Values.global.namespace }} +spec: + refreshInterval: 15s + secretStoreRef: + name: {{ .Values.global.secretStore.name }} + kind: {{ .Values.global.secretStore.kind }} + target: + name: qtodo-git-credentials + template: + type: Opaque + metadata: + annotations: + tekton.dev/git-0: {{ $host | quote }} + data: + .gitconfig: | + [credential "{{ $host }}"] + helper = store + .git-credentials: {{ printf "https://{{ .%s }}:{{ .%s }}@%s" $userKey $passKey $hostBare | quote }} + data: + - secretKey: {{ $userKey }} + remoteRef: + key: {{ .Values.git.credentials.vaultPath }} + property: {{ $userKey }} + - secretKey: {{ $passKey }} + remoteRef: + key: {{ .Values.git.credentials.vaultPath }} + property: {{ $passKey }} +{{- end }} diff --git a/charts/supply-chain/templates/tasks/build-artifact.yaml b/charts/supply-chain/templates/tasks/build-artifact.yaml index 57ab161d..3d49194d 100644 --- a/charts/supply-chain/templates/tasks/build-artifact.yaml +++ b/charts/supply-chain/templates/tasks/build-artifact.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-build-artifact diff --git a/charts/supply-chain/templates/tasks/generate-sbom.yaml b/charts/supply-chain/templates/tasks/generate-sbom.yaml index aab2457f..a2c7e546 100644 --- a/charts/supply-chain/templates/tasks/generate-sbom.yaml +++ b/charts/supply-chain/templates/tasks/generate-sbom.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-generate-sbom diff --git a/charts/supply-chain/templates/tasks/init.yaml b/charts/supply-chain/templates/tasks/init.yaml new file mode 100644 index 00000000..ed5b8046 --- /dev/null +++ b/charts/supply-chain/templates/tasks/init.yaml @@ -0,0 +1,55 @@ +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + labels: + app.kubernetes.io/version: "0.1" + annotations: + tekton.dev/pipelines.minVersion: "0.12.1" + tekton.dev/tags: "supply-chain" + name: init + namespace: {{ .Values.global.namespace }} +spec: + description: >- + Initialize Pipeline Task. Determines whether the image should be built + by checking if the target image already exists in the registry. + Supports rebuild and skip-checks flags. + params: + - name: image-url + description: Image URL for build by PipelineRun + - name: rebuild + description: Rebuild the image even if it exists + default: "false" + - name: skip-checks + description: Skip checks against built image + default: "false" + results: + - name: build + description: Defines if the image in param image-url should be built + steps: + - name: init + image: {{ .Values.tasks.images.skopeo }} + computeResources: + limits: + memory: 256Mi + requests: + memory: 256Mi + cpu: 100m + env: + - name: IMAGE_URL + value: $(params.image-url) + - name: REBUILD + value: $(params.rebuild) + - name: SKIP_CHECKS + value: $(params.skip-checks) + script: | + #!/bin/bash + echo "Build Initialize: $IMAGE_URL" + echo + + echo "Determine if Image Already Exists" + if [ "$REBUILD" == "true" ] || [ "$SKIP_CHECKS" == "false" ] || ! skopeo inspect --no-tags --raw "docker://$IMAGE_URL" &>/dev/null; then + echo -n "true" > $(results.build.path) + else + echo -n "false" > $(results.build.path) + fi diff --git a/charts/supply-chain/templates/tasks/restart-qtodo.yaml b/charts/supply-chain/templates/tasks/restart-qtodo.yaml index 9fe23387..8eef1981 100644 --- a/charts/supply-chain/templates/tasks/restart-qtodo.yaml +++ b/charts/supply-chain/templates/tasks/restart-qtodo.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: restart-qtodo diff --git a/charts/supply-chain/templates/tasks/sbom-attest.yaml b/charts/supply-chain/templates/tasks/sbom-attest.yaml index 850f5374..ffd81fbb 100644 --- a/charts/supply-chain/templates/tasks/sbom-attest.yaml +++ b/charts/supply-chain/templates/tasks/sbom-attest.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-sbom-attestation diff --git a/charts/supply-chain/templates/tasks/sign-artifact.yaml b/charts/supply-chain/templates/tasks/sign-artifact.yaml index 3bfbaedd..03eb52bc 100644 --- a/charts/supply-chain/templates/tasks/sign-artifact.yaml +++ b/charts/supply-chain/templates/tasks/sign-artifact.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-sign-artifact diff --git a/charts/supply-chain/templates/tasks/sign-image.yaml b/charts/supply-chain/templates/tasks/sign-image.yaml index dc820538..9e8300af 100644 --- a/charts/supply-chain/templates/tasks/sign-image.yaml +++ b/charts/supply-chain/templates/tasks/sign-image.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-sign-image diff --git a/charts/supply-chain/templates/tasks/upload-sbom.yaml b/charts/supply-chain/templates/tasks/upload-sbom.yaml index d660c48d..9bac0960 100644 --- a/charts/supply-chain/templates/tasks/upload-sbom.yaml +++ b/charts/supply-chain/templates/tasks/upload-sbom.yaml @@ -1,6 +1,6 @@ {{- if eq .Values.rhtpa.enabled true }} --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-upload-sbom diff --git a/charts/supply-chain/templates/tasks/verify-artifact.yaml b/charts/supply-chain/templates/tasks/verify-artifact.yaml index 68e7186f..b36bd66e 100644 --- a/charts/supply-chain/templates/tasks/verify-artifact.yaml +++ b/charts/supply-chain/templates/tasks/verify-artifact.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-verify-artifact diff --git a/charts/supply-chain/templates/tasks/verify-image.yaml b/charts/supply-chain/templates/tasks/verify-image.yaml index c19d22e0..baaf4ef9 100644 --- a/charts/supply-chain/templates/tasks/verify-image.yaml +++ b/charts/supply-chain/templates/tasks/verify-image.yaml @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Task metadata: name: qtodo-verify-image diff --git a/charts/supply-chain/values.yaml b/charts/supply-chain/values.yaml index 129e29a0..62249085 100644 --- a/charts/supply-chain/values.yaml +++ b/charts/supply-chain/values.yaml @@ -39,6 +39,15 @@ rhtpa: clientSecretVaultPath: "secret/data/hub/infra/rhtpa/rhtpa-oidc-cli" clientSecretVaultKey: "client-secret" +# git credentials for protected repositories +git: + credentials: + enabled: false + host: "https://github.com" + vaultPath: "secret/data/hub/supply-chain/git-credentials" + usernameKey: "username" + passwordKey: "password" + # qtodo repository configuration qtodo: repository: "https://github.com/validatedpatterns-demos/qtodo.git" @@ -144,3 +153,4 @@ tasks: mandrel: "registry.redhat.io/quarkus/mandrel-for-jdk-21-rhel8:23.1-36" syft: "registry.redhat.io/rh-syft-tech-preview/syft-rhel9:1.29.0" rhtpa: "registry.access.redhat.com/ubi9/ubi:9.7-1764794285" + skopeo: "registry.access.redhat.com/ubi9/skopeo:9.5-1745865345@sha256:d91eb0dac7308ddfb12193368a42009509925edba80da9ffd3b82d03427dc3ed" diff --git a/docs/supply-chain.md b/docs/supply-chain.md index bb86dc18..cd03b224 100644 --- a/docs/supply-chain.md +++ b/docs/supply-chain.md @@ -46,6 +46,12 @@ For the Secure Supply Chain use case, the command would be: python3 scripts/gen-feature-variants.py --base values-hub.yaml --features supply-chain --registry-option ``` +If the source repository is **private** (protected), add the `protected-repos` feature: + +```shell +python3 scripts/gen-feature-variants.py --base values-hub.yaml --features supply-chain,protected-repos --registry-option +``` + Where `` is one of the options available in _Bring Your Own (BYO) Container Registry_: 1. Embedded Quay Registry @@ -251,6 +257,7 @@ Once the supply-chain application has synced in ArgoCD, start the pipeline using At the bottom we have the **workspaces**. These must be configured manually. * For **qtodo-source**, select `PersistentVolumeClaim` and the PVC name is `qtodo-workspace-source`. * For **registry-auth-config**, select `Secret` and the name of the secret is `qtodo-registry-auth`. + * For **git-auth** (optional, only when using protected repositories), select `Secret` and the name of the secret is `qtodo-git-credentials`. 5. Press **Start** to finish and run the pipeline. @@ -278,9 +285,13 @@ spec: - name: registry-auth-config secret: secretName: qtodo-registry-auth + # Uncomment for protected (private) repositories: + # - name: git-auth + # secret: + # secretName: qtodo-git-credentials ``` -As was described previously, verify the values associated with the PVC storage and registry configuration. +As was described previously, verify the values associated with the PVC storage and registry configuration. If you are using a protected repository, uncomment the `git-auth` workspace. Using the previously created definition, start a new execution of the pipeline using `oc` CLI: @@ -307,10 +318,79 @@ oc get taskruns -n layered-zero-trust-hub -l tekton.dev/pipelineRun=,tekton.dev/pipelineTask= ``` +### Protected Repositories + +By default the pipeline clones the qtodo source from a **public** GitHub repository. If your source code lives in a private (protected) repository, enable the Git credentials feature so the `git-clone` task can authenticate. + +#### 1. Store Git credentials in Vault + +Uncomment the `git-credentials` secret in your local `~/values-secret.yaml` (copied from `values-secret.yaml.template`) and fill in your Git username and Personal Access Token (PAT): + +```yaml +- name: git-credentials + vaultPrefixes: + - hub/supply-chain + fields: + - name: username + value: "your-git-username" + onMissingValue: error + - name: password + value: "your-personal-access-token" + onMissingValue: error +``` + +Then load the secret into Vault: `make load-secrets`. + +#### 2. Enable Git credentials in the supply-chain overrides + +Add the following overrides to the `supply-chain` application in `values-hub.yaml`: + +```yaml +- name: git.credentials.enabled + value: "true" +- name: git.credentials.host + value: "https://github.com" +- name: git.credentials.vaultPath + value: "secret/data/hub/supply-chain/git-credentials" +``` + +Alternatively, if you use the `gen-feature-variants.py` script, add `protected-repos` to the features list and provide your private repository URL with `--git-repo`. The Git credential overrides and the `qtodo.repository` override are included automatically: + +```shell +python3 scripts/gen-feature-variants.py \ + --features supply-chain,protected-repos \ + --registry-option \ + --git-repo https://github.com/your-org/qtodo.git +``` + +#### 3. Point the pipeline at your private repository + +When using the generator with `--git-repo`, the `qtodo.repository` override is set automatically in the generated `values-hub.yaml`. If you are configuring manually, add this override to the `supply-chain` application: + +```yaml +- name: qtodo.repository + value: "https://github.com/your-org/qtodo.git" +``` + +#### How it works + +When `git.credentials.enabled` is `true`: + +* An `ExternalSecret` (`qtodo-git-credentials`) pulls the username and PAT from Vault and creates an `Opaque` secret containing `.git-credentials` and `.gitconfig` files, annotated with `tekton.dev/git-0` pointing to the configured host. +* The pipeline ServiceAccount mounts this secret, and the `git-clone` task receives it via the optional `git-auth` / `basic-auth` workspace. When starting a PipelineRun, select `Secret` / `qtodo-git-credentials` for the **git-auth** workspace. +* The Vault policy `hub-supply-chain-jwt-secret` grants read access to `secret/data/hub/supply-chain/*` for the pipeline's SPIFFE identity. + +### Init task (pre-flight image check) + +The pipeline includes an `init` task that runs before `git-clone`. It uses `skopeo inspect` to check whether the target image already exists in the registry. If the image exists (and `rebuild` is not set to `"true"`), the pipeline skips the build. This avoids unnecessary rebuilds and is modeled after the [RHTAP sample pipelines](https://github.com/konflux-ci/build-definitions). + +The pipeline also emits Tekton Chains provenance results (`CHAINS-GIT_URL`, `CHAINS-GIT_COMMIT`, `IMAGE_URL`, `IMAGE_DIGEST`) so that Tekton Chains can automatically generate and sign provenance attestations. + ### Pipeline tasks The pipeline we have prepared has the following steps: +* **init**. Checks whether the target image already exists in the registry. Gates the build with a `when` condition. * **qtodo-clone-repository**. Clones the `qtodo` repository. * **qtodo-build-artifact**. Builds an _uber-jar_ of `qtodo` application. * **qtodo-sign-artifact**. Signs the JAR file generated during the build process. diff --git a/scripts/features/features.yaml b/scripts/features/features.yaml index 49ca349a..434d53a7 100644 --- a/scripts/features/features.yaml +++ b/scripts/features/features.yaml @@ -33,6 +33,11 @@ features: org: ztvp image_name: qtodo + protected-repos: + description: "Protected (private) Git repository support for supply-chain" + depends_on: [supply-chain] + git_repo_required: true + entra-id: description: "Azure Entra ID integration" depends_on: [supply-chain] diff --git a/scripts/features/protected-repos.yaml b/scripts/features/protected-repos.yaml new file mode 100644 index 00000000..2a84d972 --- /dev/null +++ b/scripts/features/protected-repos.yaml @@ -0,0 +1,18 @@ +# Protected repository support for the supply-chain pipeline. +# Enables git-clone to authenticate against private Git repositories +# using credentials stored in Vault (ExternalSecret -> basic-auth secret). +# +# Requires the git-credentials secret in values-secret.yaml.template +# to be uncommented and populated with your Git username and PAT. +clusterGroup: + merge_into_applications: + supply-chain: + overrides: + - name: git.credentials.enabled + value: "true" + - name: git.credentials.host + value: "https://github.com" + - name: git.credentials.vaultPath + value: "secret/data/hub/supply-chain/git-credentials" + - name: qtodo.repository + value: "REPLACE_WITH_GIT_REPO_URL" diff --git a/scripts/features/supply-chain.yaml b/scripts/features/supply-chain.yaml index f40d9bec..fdc7dbe8 100644 --- a/scripts/features/supply-chain.yaml +++ b/scripts/features/supply-chain.yaml @@ -1,6 +1,9 @@ # Secure Supply Chain application + vault role # Depends on: pipelines, rhtas, rhtpa, storage (all resolved automatically) # Requires --registry-option to select the registry backend. +# +# Optional: add --features protected-repos to enable private Git repository +# support (see scripts/features/protected-repos.yaml). clusterGroup: applications: supply-chain: @@ -13,6 +16,14 @@ clusterGroup: - kind: ServiceAccount jqPathExpressions: - ".imagePullSecrets[]|select(.name | contains(\"-dockercfg-\"))" + - group: tekton.dev + kind: Task + jsonPointers: + - /spec/steps + - group: tekton.dev + kind: Pipeline + jsonPointers: + - /spec/tasks merge_into_applications: vault: diff --git a/scripts/gen-feature-variants.md b/scripts/gen-feature-variants.md index fe9235e9..b3781a16 100644 --- a/scripts/gen-feature-variants.md +++ b/scripts/gen-feature-variants.md @@ -57,6 +57,12 @@ python3 scripts/gen-feature-variants.py --features supply-chain --registry-optio # Generate all three supply-chain registry variants at once python3 scripts/gen-feature-variants.py --features supply-chain --registry-option all +# Supply chain with protected (private) Git repository support +python3 scripts/gen-feature-variants.py \ + --features supply-chain,protected-repos \ + --registry-option 2 \ + --git-repo https://github.com/your-org/qtodo.git + # Custom base file and output directory python3 scripts/gen-feature-variants.py \ --features rhtpa --base values-hub.yaml --outdir /tmp @@ -81,6 +87,30 @@ The output directory is created automatically if it does not exist. > without these fields, replace the placeholders manually before applying > the generated file. +## Protected Repositories (`--git-repo`) + +When the `protected-repos` feature is enabled, the `--git-repo` argument is +**required**. It specifies the private Git repository URL that the Tekton +pipeline will clone. The generator substitutes the placeholder in the +`protected-repos` fragment with the supplied URL: + +```bash +python3 scripts/gen-feature-variants.py \ + --features supply-chain,protected-repos \ + --registry-option 1 \ + --git-repo https://github.com/your-org/qtodo.git +``` + +The generated `values-hub.yaml` will include: + +```yaml +- name: qtodo.repository + value: "https://github.com/your-org/qtodo.git" +``` + +See [Protected Repositories](../docs/supply-chain.md#protected-repositories) +for the full setup (Vault credentials, ExternalSecret, workspace selection). + ## How It Works 1. The script reads the base `values-hub.yaml`. diff --git a/scripts/gen-feature-variants.py b/scripts/gen-feature-variants.py index ca9bf4d3..26e1dcb8 100755 --- a/scripts/gen-feature-variants.py +++ b/scripts/gen-feature-variants.py @@ -267,6 +267,22 @@ def _substitute_repository_placeholders(base, org=None, image_name=None): base["global"]["registry"]["repository"] = repo +GIT_REPO_PLACEHOLDER = "REPLACE_WITH_GIT_REPO_URL" + + +def _substitute_git_repo_url(base, git_repo_url): + """Replace the git repo placeholder in supply-chain overrides.""" + apps = base.get("clusterGroup", {}).get("applications", {}) + sc = apps.get("supply-chain", {}) + for override in sc.get("overrides", []): + if ( + override.get("name") == "qtodo.repository" + and str(override.get("value")) == GIT_REPO_PLACEHOLDER + ): + override["value"] = git_repo_url + return + + def generate_variant( base_path, features_dir, @@ -275,6 +291,7 @@ def generate_variant( output_path, org=None, image_name=None, + git_repo_url=None, ): """Load base, merge all feature fragments + registry option, write output.""" yaml = YAML() @@ -306,6 +323,9 @@ def generate_variant( if org or image_name: _substitute_repository_placeholders(base, org=org, image_name=image_name) + if git_repo_url: + _substitute_git_repo_url(base, git_repo_url) + validate_output(base) cg = base.get("clusterGroup") if cg: @@ -323,7 +343,8 @@ def build_output_name(features, registry_option=None): """Construct the output filename from features and optional registry option.""" if "supply-chain" in features: label = REGISTRY_LABELS.get(registry_option, f"option-{registry_option}") - return f"values-hub-supply-chain-{label}.yaml" + suffix = "-protected-repos" if "protected-repos" in features else "" + return f"values-hub-supply-chain-{label}{suffix}.yaml" return f"values-hub-{'-'.join(features)}.yaml" @@ -358,6 +379,12 @@ def main(): default=None, help="Output directory (default: /tmp)", ) + parser.add_argument( + "--git-repo", + default=None, + help="Private Git repository URL for protected-repos feature " + "(e.g. https://github.com/your-org/qtodo.git)", + ) parser.add_argument( "--list-features", action="store_true", @@ -418,11 +445,24 @@ def main(): ) sys.exit(1) + needs_git_repo = any( + feature_defs.get(f, {}).get("git_repo_required") for f in resolved + ) + if needs_git_repo and not args.git_repo: + print( + "ERROR: --git-repo is required when protected-repos feature is enabled " + "(e.g. --git-repo https://github.com/your-org/qtodo.git)", + file=sys.stderr, + ) + sys.exit(1) + print(f"Base: {base}") print(f"Output: {outdir}") print(f"Features: {' -> '.join(resolved)}") if args.registry_option: print(f"Registry: option {args.registry_option}") + if args.git_repo: + print(f"Git repo: {args.git_repo}") if args.registry_option == "all": for opt_num in [1, 2, 3]: @@ -438,7 +478,14 @@ def main(): out_name = build_output_name(requested, opt_num) out_path = os.path.join(outdir, out_name) generate_variant( - base, FEATURES_DIR, resolved, reg_path, out_path, org, image_name + base, + FEATURES_DIR, + resolved, + reg_path, + out_path, + org, + image_name, + git_repo_url=args.git_repo, ) else: reg_path = None @@ -459,7 +506,14 @@ def main(): ) out_path = os.path.join(outdir, out_name) generate_variant( - base, FEATURES_DIR, resolved, reg_path, out_path, org, image_name + base, + FEATURES_DIR, + resolved, + reg_path, + out_path, + org, + image_name, + git_repo_url=args.git_repo, ) if args.registry_option and org and image_name: diff --git a/values-hub.yaml b/values-hub.yaml index a5dbd0e6..48b56491 100644 --- a/values-hub.yaml +++ b/values-hub.yaml @@ -361,6 +361,9 @@ clusterGroup: path "secret/data/hub/infra/rhtpa/rhtpa-oidc-cli" { capabilities = ["read"] } + path "secret/data/hub/supply-chain/*" { + capabilities = ["read"] + } jwt: enabled: true oidcDiscoveryUrl: https://spire-spiffe-oidc-discovery-provider.zero-trust-workload-identity-manager.svc.cluster.local @@ -575,6 +578,14 @@ clusterGroup: # - kind: ServiceAccount # jqPathExpressions: # - .imagePullSecrets[]|select(.name | contains("-dockercfg-")) + # - group: tekton.dev + # kind: Task + # jsonPointers: + # - /spec/steps + # - group: tekton.dev + # kind: Pipeline + # jsonPointers: + # - /spec/tasks # overrides: # # Registry credentials are inherited from global.registry. # # Only set app-specific overrides below. @@ -599,6 +610,15 @@ clusterGroup: # # Uncomment to auto-trigger a pipeline run on every ArgoCD sync # # - name: pipelinerun.enabled # # value: "true" + # # Protected repository credentials (Vault + ExternalSecret) + # # - name: git.credentials.enabled + # # value: "true" + # # - name: git.credentials.host + # # value: "https://github.com" + # # - name: git.credentials.vaultPath + # # value: "secret/data/hub/supply-chain/git-credentials" + # # - name: qtodo.repository + # # value: "https://github.com/your-org/qtodo.git" # # ACS Central Services acs-central: diff --git a/values-secret.yaml.template b/values-secret.yaml.template index f6afe138..48cbd4ea 100644 --- a/values-secret.yaml.template +++ b/values-secret.yaml.template @@ -219,6 +219,23 @@ secrets: # value: "REPLACE_WITH_REGISTRY_TOKEN" # onMissingValue: error + # =========================================================================== + # SUPPLY CHAIN GIT CREDENTIALS (hub/supply-chain/) + # Credentials for cloning protected Git repositories in Tekton pipelines + # Policy: hub-supply-chain-jwt-secret (read access to hub/supply-chain/*) + # =========================================================================== + # Uncomment when using protected (private) Git repositories + #- name: git-credentials + # vaultPrefixes: + # - hub/supply-chain + # fields: + # - name: username + # value: "your-git-username" # Replace with your Git username + # onMissingValue: error + # - name: password + # value: "your-personal-access-token" # Replace with your PAT/token + # onMissingValue: error + # =========================================================================== # COCO (CONFIDENTIAL CONTAINERS) SECRETS # Uncomment the secrets below when deploying with CoCo support.