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

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

    TIP

    Подробности смотрите в разделе Tekton Chains Type Hinting выше.

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

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

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

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

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

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

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

    Требования

    • Kubernetes кластер с установленными Tekton Pipelines, Tekton Chains и Kyverno
    • Регистратор с разрешением на пуш образов
    • Установленный и настроенный CLI kubectl для доступа к кластеру
    • Установленный CLI инструмент cosign
    • Установленный CLI инструмент jq

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

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

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

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

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

    Шаг 4: Создание примерного пайплайна

    В предыдущем пайплайне сборки образа добавьте задачу git clone и сохраните вывод задачи 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: Скрипт для имитации клонирования кода и записи URL репозитория и сообщения коммита в результаты.
          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: Скрипт для генерации Containerfile для сборки образа.
          name: generate-containerfile
          type: string
        - default: <registry>/test/chains/demo-3:latest
          description: Адрес целевого образа для сборки
          name: image
          type: string
      results:
        - description: первый артефакт образа
          name: first_image_ARTIFACT_OUTPUTS
          type: object
          value:
            digest: $(tasks.build-image.results.IMAGE_DIGEST)
            uri: $(tasks.build-image.results.IMAGE_URL)
        - description: первый артефакт репозитория исходного кода
          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: Рабочее пространство для исходного кода.
        - name: registryconfig
          description: Рабочее пространство для конфигурации регистра распространения.
    TIP

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

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

    Необходимо скорректировать конфигурацию

    • params:
      • generate-containerfile
        • default: Скорректировать адрес образа в from.
      • image:
        • default: Адрес целевого образа для сборки.

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

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

    Шаг 5: Запуск примерного пайплайна

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

    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>
    
    # создать ресурс pipeline run в 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

    # Получить URI образа
    $ export IMAGE_URI=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.uri}')
    
    # Получить дайджест образа
    $ export IMAGE_DIGEST=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.digest}')
    
    # Объединить URI и дайджест для полного указания образа
    $ export IMAGE=$IMAGE_URI@$IMAGE_DIGEST
    
    # Вывести ссылку на образ
    $ echo $IMAGE
    
    <registry>/test/chains/demo-3:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046

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

    Шаг 8: (Опционально) Получение аттестации SLSA Provenance

    TIP

    Если вас интересует содержимое аттестации SLSA Provenance, можете продолжить чтение.

    Подробности об аттестации SLSA Provenance смотрите в SLSA Provenance

    Получите публичный ключ подписи согласно разделу Get the signing public key.

    # Отключить загрузку tlog и включить приватную инфраструктуру
    $ 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

    Вывод будет похож на следующий, содержащий аттестацию SLSA Provenance.

    Аттестация SLSA Provenance
    {
      "_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:
      • buildConfig:
        • tasks: Задачи сборки.
      • buildType: Тип сборки, здесь tekton.dev/v1beta1/PipelineRun.
      • builder:
        • id: Идентификатор билдера, здесь 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

    Для этого шага требуются права администратора кластера.

    Подробности о 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:
                  # Учетные данные должны существовать в namespace, где развернут kyverno
                  - registry-credentials
    
              attestations:
                - type: https://slsa.dev/provenance/v0.2
                  attestors:
                    - entries:
                        - keys:
                            publicKeys: |- # <- Публичный ключ подписанта
                              -----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(@) }}": В материалах должен быть хотя бы один элемент, URI которого начинается с 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: Очистка ресурсов

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

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

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

    $ kubectl delete clusterpolicy verify-code-repository-material

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

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

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

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

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

    Ссылки