• Русский
  • Проверка репозитория исходного кода

    В Tekton Chains он может собирать определённые входные и выходные данные из PipelineRun и записывать их в SLSA Provenance.

    TIP

    См. раздел Tekton Chains Type Hinting выше для получения дополнительных сведений.

    Мы можем использовать эту функцию, чтобы включить сведения о репозитории кода в информацию SLSA Provenance. Затем мы можем проверить репозиторий кода в Kyverno.

    Обзор функции

    Этот метод использует Chains для автоматического создания SLSA Provenance для собранного образа, а затем использует Kyverno для проверки provenance:

    1. Настроить Tekton Chains для автоматического создания SLSA Provenance для собранного образа.
    2. Использовать git Tekton Task для получения репозитория исходного кода.
    3. Использовать buildah Tekton Task для сборки образа.
    4. Объявить сведения о результатах git и buildah в результатах Pipeline. Это позволяет записывать репозиторий исходного кода и сведения о коммите для образа.
    5. Настроить правила Kyverno для проверки репозитория исходного кода.
    6. Использовать образ для создания Pod и проверки репозитория исходного кода.

    Сценарии использования

    Следующие сценарии требуют обращения к рекомендациям в этом документе:

    • Реализация проверки репозитория исходного кода в кластерах Kubernetes с использованием Kyverno
    • Принудительное применение политик безопасности, разрешающих развёртывание только образов, собранных из определённых репозиториев исходного кода
    • Настройка автоматической проверки репозитория исходного кода в CI/CD pipeline
    • Обеспечение provenance образов и подлинности исходного кода в production-средах
    • Реализация средств контроля безопасности цепочки поставок для container images путём проверки их происхождения из исходного кода

    Предварительные требования

    • Кластер Kubernetes с установленными Tekton Pipelines, Tekton Chains и Kyverno
    • Registry с включённой возможностью push образов
    • Установленный и настроенный CLI kubectl для доступа к кластеру
    • Установленный CLI-инструмент cosign
    • Установленный CLI-инструмент jq

    Обзор процесса

    ШагОперацияОписание
    1Сгенерировать ключи подписиСоздать пару ключей для подписывания артефактов с помощью cosign
    2Настроить аутентификациюНастроить учётные данные registry для push образов
    3Настроить Tekton ChainsНастроить Chains на использование OCI storage и конфигурацию подписи, отключить TaskRun SLSA Provenance
    4Создать пример pipelineСоздать определение pipeline с задачами git clone и buildah
    5Запустить пример pipelineСоздать и запустить PipelineRun с корректной конфигурацией
    6Дождаться подписанияДождаться, пока PipelineRun будет подписан Chains
    7Получить сведения об образеИзвлечь URI и digest образа из PipelineRun
    8(Необязательно) Получить SLSA ProvenanceПолучить и проверить attestation SLSA Provenance
    9Проверить с помощью KyvernoСоздать и применить политику Kyverno для проверки репозитория-источника образа
    10ОчиститьУдалить тестовые ресурсы и политики

    Пошаговые инструкции

    Шаги 1-3: Базовая настройка

    Эти шаги идентичны руководству Quick Start: Signed Provenance. Пожалуйста, следуйте инструкциям в этом руководстве для:

    Шаг 4: Создать пример pipeline

    В предыдущем image build pipeline добавьте задачу клонирования git и сохраните вывод задачи git в results PipelineRun.

    apiVersion: tekton.dev/v1
    kind: Pipeline
    metadata:
      name: chains-demo-3
    spec:
      params:
        - default: |-
            echo "Simulate cloning the code and write the repository URL and commit message into the results."
    
            # This commit sha must be a valid commit sha [0-9a-f]{40}.
            cat << 'EOF' > $(results.array-result.path)
            [
              "https://github.com/tektoncd/pipeline",
              "cccccaaaa0000000000000000000000000000000"
            ]
            EOF
    
            echo -e "\nResults:"
            echo "-------------------"
            cat $(results.array-result.path)
            echo "-------------------"
            echo -e "\nClone successfully!"
          description: A script to simulate cloning the code and write the repository URL and commit message into the results.
          name: generate-git-clone-results
          type: string
        - default: |-
            echo "Generate a Containerfile for building an image."
    
            cat << 'EOF' > Containerfile
            FROM ubuntu:latest
            ENV TIME=1
            EOF
    
            echo -e "\nContainerfile contents:"
            echo "-------------------"
            cat Containerfile
            echo "-------------------"
            echo -e "\nContainerfile generated successfully!"
          description: A script to generate a Containerfile for building an image.
          name: generate-containerfile
          type: string
        - default: <registry>/test/chains/demo-3:latest
          description: The target image address built
          name: image
          type: string
      results:
        - description: first image artifact output
          name: first_image_ARTIFACT_OUTPUTS
          type: object
          value:
            digest: $(tasks.build-image.results.IMAGE_DIGEST)
            uri: $(tasks.build-image.results.IMAGE_URL)
        - description: first repo artifact input
          name: source_repo_ARTIFACT_INPUTS
          type: object
          value:
            digest: sha1:$(tasks.git-clone.results.array-result[1])
            uri: $(tasks.git-clone.results.array-result[0])
      tasks:
        - name: git-clone
          params:
            - name: script
              value: $(params.generate-git-clone-results)
          taskRef:
            params:
              - name: kind
                value: task
              - name: catalog
                value: catalog
              - name: name
                value: run-script
              - name: version
                value: "0.1"
            resolver: hub
          timeout: 30m0s
          workspaces:
            - name: source
              workspace: source
        - name: generate-containerfile
          params:
            - name: script
              value: $(params.generate-containerfile)
          runAfter:
            - git-clone
          taskRef:
            params:
              - name: kind
                value: task
              - name: catalog
                value: catalog
              - name: name
                value: run-script
              - name: version
                value: "0.1"
            resolver: hub
          timeout: 30m0s
          workspaces:
            - name: source
              workspace: source
        - name: build-image
          params:
            - name: IMAGES
              value:
                - $(params.image)
            - name: TLS_VERIFY
              value: "false"
          runAfter:
            - generate-containerfile
          taskRef:
            params:
              - name: kind
                value: task
              - name: catalog
                value: catalog
              - name: name
                value: buildah
              - name: version
                value: "0.9"
            resolver: hub
          timeout: 30m0s
          workspaces:
            - name: source
              workspace: source
            - name: registryconfig
              workspace: registryconfig
      workspaces:
        - name: source
          description: The workspace for source code.
        - name: registryconfig
          description: The workspace for distribution registry configuration.
    TIP

    В этом руководстве демонстрируется упрощённый workflow: Containerfile и выходные данные задачи git-clone генерируются непосредственно внутри pipeline. В production-средах обычно:

    1. Используют задачу git-clone для получения исходного кода из репозитория
    2. Собирают образ с помощью Containerfile, который уже находится в исходном коде
    3. Такой подход обеспечивает корректное управление версиями и сохраняет разделение между кодом и конфигурацией pipeline
    Пояснение полей YAML
    • Большинство полей совпадает с Шаг 4: Создать пример pipeline. Ниже описаны только отличия.
    • params
      • generate-git-clone-results: Скрипт для имитации клонирования кода и записи URL репозитория и сообщения коммита в results.
    • results
      • source_repo_ARTIFACT_INPUTS: URL репозитория исходного кода и сообщение коммита.
        • digest: Commit SHA репозитория исходного кода.
      • Этот формат совместим с Tekton Chains, см. Tekton Chains Type Hinting в разделе выше для получения дополнительных сведений.

    Требуется скорректировать конфигурацию

    • params:
      • generate-containerfile
        • default: Настройте адрес исходного from image.
      • image:
        • default: Адрес целевого собираемого образа.

    Сохраните это в файл YAML с именем chains.demo-3.pipeline.yaml и примените его:

    $ export NAMESPACE=<default>
    $ kubectl apply -n $NAMESPACE -f chains.demo-3.pipeline.yaml

    Шаг 5: Запустить пример pipeline

    Это ресурс PipelineRun, который используется для запуска pipeline.

    apiVersion: tekton.dev/v1
    kind: PipelineRun
    metadata:
      generateName: chains-demo-3-
    spec:
      pipelineRef:
        name: chains-demo-3
      taskRunTemplate:
        serviceAccountName: <default>
      workspaces:
        - name: registryconfig
          secret:
            secretName: <registry-credentials>
        - name: source
          volumeClaimTemplate:
            spec:
              accessModes:
                - ReadWriteOnce
              resources:
                requests:
                  storage: 1Gi
              storageClassName: <nfs>
    Пояснение полей YAML

    Сохраните это в YAML-файл с именем chains.demo-3.pipelinerun.yaml и примените его:

    $ export NAMESPACE=<default>
    
    # create the pipeline run resource in the namespace
    $ kubectl create -n $NAMESPACE -f chains.demo-3.pipelinerun.yaml

    Дождитесь завершения PipelineRun.

    $ kubectl get pipelinerun -n $NAMESPACE -w
    
    chains-demo-3-<xxxxx>   True        Succeeded   2m         2m

    Шаг 6: Дождаться подписания PipelineRun

    Дождитесь, пока у PipelineRun появится аннотация chains.tekton.dev/signed: "true".

    $ export NAMESPACE=<default>
    $ export PIPELINERUN_NAME=<chains-demo-3-xxxxx>
    
    $ kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o yaml | grep "chains.tekton.dev/signed"
    
        chains.tekton.dev/signed: "true"

    Когда у PipelineRun появляется аннотация chains.tekton.dev/signed: "true", это означает, что образ подписан.

    Шаг 7: Получить образ из PipelineRun

    # Get the image URI
    $ export IMAGE_URI=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.uri}')
    
    # Get the image digest
    $ export IMAGE_DIGEST=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.digest}')
    
    # Combine the image URI and digest to form the full image reference
    $ export IMAGE=$IMAGE_URI@$IMAGE_DIGEST
    
    # Print the image reference
    $ echo $IMAGE
    
    <registry>/test/chains/demo-3:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046

    Этот образ будет использоваться для проверки репозитория исходного кода.

    Шаг 8: (Необязательно) Получить attestation SLSA Provenance

    TIP

    Если вам интересен содержимое attestation SLSA Provenance, можете продолжить читать следующий раздел.

    Более подробную информацию об attestation SLSA Provenance см. в SLSA Provenance

    Получите открытый ключ подписи согласно разделу Получить открытый ключ подписи.

    # Disable tlog upload and enable private infrastructure
    $ export COSIGN_TLOG_UPLOAD=false
    $ export COSIGN_PRIVATE_INFRASTRUCTURE=true
    
    $ export IMAGE=<<registry>/test/chains/demo-3:latest@sha256:db2607375049e8defa75a8317a53fd71fd3b448aec3c507de7179ded0d4b0f20>
    
    $ cosign verify-attestation --key cosign.pub --type slsaprovenance $IMAGE | jq -r '.payload | @base64d' | jq -s

    Результат будет похож на следующий и содержит attestation SLSA Provenance.

    SLSA Provenance attestation
    {
      "_type": "https://in-toto.io/Statement/v0.1",
      "subject": [
        {
          "name": "<registry>/test/chains/demo-3:latest",
          "digest": {
            "sha256": "db2607375049e8defa75a8317a53fd71fd3b448aec3c507de7179ded0d4b0f20"
          }
        }
      ],
      "predicateType": "https://slsa.dev/provenance/v0.2",
      "predicate": {
        "buildConfig": {
          "tasks": null
        },
        "buildType": "tekton.dev/v1beta1/PipelineRun",
        "builder": {
          "id": "https://alauda.io/builders/tekton/v1"
        },
        "invocation": {
          "parameters": {
            "image": "<registry>/test/chains/demo-3:latest"
          }
        },
        "materials": [
          {
            "digest": {
              "sha256": "bad5d84ded24307d12cacc9ef37fc38bce90ea5d00501f43b27d0c926be26f19"
            },
            "uri": "oci://<registry>/devops/tektoncd/hub/run-script"
          },
          {
            "digest": {
              "sha1": "cccccaaaa0000000000000000000000000000000"
            },
            "uri": "https://github.com/tektoncd/pipeline"
          }
        ],
        "metadata": {
          "buildFinishedOn": "2025-06-06T10:28:21Z",
          "buildStartedOn": "2025-06-06T10:27:34Z"
        }
      }
    }
    Описание полей
    • predicateType: Тип predicate.
    • predicate:
      • buildConfig:
        • tasks: Задачи сборки.
      • buildType: Тип сборки, это tekton.dev/v1beta1/PipelineRun.
      • builder:
        • id: Идентификатор builder, это https://alauda.io/builders/tekton/v1.
      • invocation:
        • parameters: Параметры сборки.
      • materials: Материалы сборки.
        • uri:
          • oci://<registry>/devops/tektoncd/hub/run-script: Используемый образ задачи.
          • https://github.com/tektoncd/pipeline: Репозиторий исходного кода задачи.
      • metadata: Метаданные сборки.
        • buildFinishedOn: Время завершения сборки.
        • buildStartedOn: Время начала сборки.

    Шаг 9: Проверить ограничение репозитория-источника образа с помощью Kyverno

    Шаг 9.1: Создать политику Kyverno, разрешающую к развёртыванию только образы, собранные из определённых репозиториев исходного кода

    TIP

    Для выполнения этого шага требуются права cluster administrator.

    Более подробную информацию о Kyverno ClusterPolicy см. в Kyverno ClusterPolicy

    Политика выглядит следующим образом:

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: verify-code-repository-material
    spec:
      webhookConfiguration:
        failurePolicy: Fail
        timeoutSeconds: 30
      background: false
      rules:
        - name: check-image
          match:
            any:
              - resources:
                  kinds:
                    - Pod
                  namespaces:
                    - policy
          verifyImages:
            - imageReferences:
                - "*"
                # - "<registry>/test/*"
              skipImageReferences:
                - "ghcr.io/trusted/*"
              failureAction: Enforce
              verifyDigest: false
              required: false
              useCache: false
              imageRegistryCredentials:
                allowInsecureRegistry: true
                secrets:
                  # The credential needs to exist in the namespace where kyverno is deployed
                  - registry-credentials
    
              attestations:
                - type: https://slsa.dev/provenance/v0.2
                  attestors:
                    - entries:
                        - keys:
                            publicKeys: |- # <- The public key of the signer
                              -----BEGIN PUBLIC KEY-----
                              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZNGfYwn7+b4uSdEYLKjxWi3xtP3
                              UkR8hQvGrG25r0Ikoq0hI3/tr0m7ecvfM75TKh5jGAlLKSZUJpmCGaTToQ==
                              -----END PUBLIC KEY-----
    
                            ctlog:
                              ignoreSCT: true
    
                            rekor:
                              ignoreTlog: true
                  conditions:
                    - all:
                        - key: "{{ buildType }}"
                          operator: Equals
                          value: "tekton.dev/v1beta1/PipelineRun"
                          message: "The buildType must be equal to tekton.dev/v1beta1/PipelineRun, not {{ buildType }}"
    
                        - key: "{{ materials[?starts_with(uri, 'https://github.com/tektoncd/')] | length(@) }}"
                          operator: GreaterThan
                          value: 0
                          message: "The materials must have at least one entry starts with https://github.com/tektoncd/, {{ materials }}"
    Пояснение полей YAML
    • Эта политика в целом соответствует политике из Image Signature Verification
    • spec.rules[].verifyImages[].attestations[].conditions: Условия для проверки.
      • all: Все условия должны быть выполнены.
        • key: "{{ buildType }}": Тип сборки должен быть равен tekton.dev/v1beta1/PipelineRun.
        • key: "{{ materials[?starts_with(uri, 'https://github.com/tektoncd/')] | length(@) }}": В materials должна быть как минимум одна запись, начинающаяся с https://github.com/tektoncd/.

    Сохраните это в YAML-файл с именем verify-code-repository-material.yaml и примените его:

    $ kubectl create -f verify-code-repository-material.yaml
    
    clusterpolicy.kyverno.io/verify-code-repository-material created

    Шаг 9.2: Проверить политику

    В namespace policy, где определена политика, создайте Pod для проверки политики.

    Используйте собранный образ для создания Pod.

    $ export NAMESPACE=<policy>
    $ export IMAGE=<<registry>/test/chains/demo-3:latest@sha256:db2607375049e8defa75a8317a53fd71fd3b448aec3c507de7179ded0d4b0f20>
    
    $ kubectl run -n $NAMESPACE built-from-specific-repo --image=${IMAGE} -- sleep 3600
    
    pod/built-from-specific-repo created

    Pod будет создан успешно.

    $ kubectl get pod -n $NAMESPACE built-from-specific-repo
    
    NAME                      READY   STATUS    RESTARTS   AGE
    built-from-specific-repo   1/1     Running   0          10s

    Измените репозиторий кода в ClusterPolicy на другое значение https://gitlab.com/ и выполните проверку снова.

    conditions:
      - all:
          - key: "{{ buildType }}"
            operator: Equals
            value: "tekton.dev/v1beta1/PipelineRun"
            message: "The buildType must be equal to tekton.dev/v1beta1/PipelineRun, not {{ buildType }}"
    
          - key: "{{ materials[?starts_with(uri, 'https://gitlab.com/')] | length(@) }}"
            operator: GreaterThan
            value: 0
            message: "The materials must have at least one entry starts with https://gitlab.com/, {{ materials }}"
    $ kubectl run -n $NAMESPACE unbuilt-from-specific-repo --image=${IMAGE} -- sleep 3600

    Если вы получаете такой вывод, это означает, что Pod заблокирован политикой.

    Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:
    
    resource Pod/policy/unbuilt-from-specific-repo was blocked due to the following policies
    
    verify-code-repository-material:
      check-image: 'image attestations verification failed, verifiedCount: 0, requiredCount:
        1, error: .attestations[0].attestors[0].entries[0].keys: attestation checks failed
        for <registry>/test/chains/demo-3:latest and predicate https://slsa.dev/provenance/v0.2:
        The materials must have at least one entry starts with https://gitlab.com/,
        [{"digest":{"sha256":"bad5d84ded24307d12cacc9ef37fc38bce90ea5d00501f43b27d0c926be26f19"},"uri":"oci://<registry>/devops/tektoncd/hub/run-script"},{"digest":{"sha256":"7a63e6c2d1b4c118e9a974e7850dd3e9321e07feec8302bcbcd16653c512ac59"},"uri":"http://tekton-hub-api.tekton-pipelines:8000/v1/resource/catalog/task/run-script/0.1/yaml"},{"digest":{"sha256":"8d5ea9ecd9b531e798fecd87ca3b64ee1c95e4f2621d09e893c58ed593bfd4c4"},"uri":"oci://<registry>/devops/tektoncd/hub/buildah"},{"digest":{"sha256":"3225653d04c223be85d173747372290058a738427768c5668ddc784bf24de976"},"uri":"http://tekton-hub-api.tekton-pipelines:8000/v1/resource/catalog/task/buildah/0.9/yaml"},{"digest":{"sha1":"cccccaaaa0000000000000000000000000000000"},"uri":"https://github.com/tektoncd/pipeline"}]'

    Шаг 10: Очистить ресурсы

    Удалите Pod, созданный на предыдущих шагах.

    $ export NAMESPACE=<policy>
    $ kubectl delete pod -n $NAMESPACE built-from-specific-repo

    Удалите политику.

    $ kubectl delete clusterpolicy verify-code-repository-material

    Ожидаемые результаты

    После завершения этого руководства:

    • У вас будет рабочая конфигурация Tekton Chains для генерации SLSA Provenance и Kyverno для проверки репозитория исходного кода
    • В ваши container images будет автоматически включаться информация о репозитории исходного кода в SLSA Provenance
    • Только образы, собранные из разрешённых репозиториев исходного кода, смогут быть развёрнуты в указанном namespace
    • Образы, собранные из неавторизованных репозиториев исходного кода, будут автоматически блокироваться политиками Kyverno
    • Вы реализуете базовый контроль безопасности цепочки поставок путём проверки происхождения исходного кода ваших container images

    Это руководство даёт основу для реализации безопасности цепочки поставок в ваших CI/CD pipeline. В production-среде следует:

    1. Настроить надлежащую изоляцию namespace и управление доступом
    2. Реализовать безопасное управление ключами для ключей подписи
    3. Настроить мониторинг и оповещения о нарушениях политик
    4. Регулярно ротировать ключи подписи и обновлять политики безопасности
    5. Рассмотреть возможность внедрения дополнительных мер безопасности, таких как сканирование уязвимостей

    Ссылки