Проверка репозитория исходного кода
В Tekton Chains можно собирать определённые входные и выходные данные из PipelineRun и записывать их в SLSA Provenance.
Мы можем использовать эту функцию для включения информации о репозитории кода в данные SLSA Provenance. Затем мы можем проверить репозиторий кода с помощью kyverno.
Обзор функции
Этот метод использует Chains для автоматической генерации SLSA Provenance для собранного образа, а затем использует Kyverno для проверки происхождения:
- Настроить Tekton Chains для автоматической генерации SLSA Provenance для собранного образа.
- Использовать задачу
git Tekton для получения репозитория исходного кода.
- Использовать задачу
buildah Tekton для сборки образа.
- Объявить результаты задач
git и buildah в результатах Pipeline. Это облегчает запись информации о репозитории исходного кода и коммите для образа.
- Настроить правила Kyverno для проверки репозитория исходного кода.
- Использовать образ для создания Pod и проверки репозитория исходного кода.
Сценарии использования
Следующие сценарии требуют обращения к рекомендациям из этого документа:
- Реализация проверки репозитория исходного кода в кластерах Kubernetes с использованием Kyverno
- Применение политик безопасности, разрешающих развертывание только образов, собранных из определённых репозиториев исходного кода
- Настройка автоматической проверки репозитория исходного кода в CI/CD пайплайнах
- Обеспечение происхождения образов и подлинности исходного кода в продуктивных средах
- Внедрение мер безопасности цепочки поставок для контейнерных образов путём проверки их источников кода
Предварительные требования
- Кластер Kubernetes с установленными Tekton Pipelines, Tekton Chains и Kyverno
- Регистратор с разрешением на пуш образов
- Установленный и настроенный CLI
kubectl для доступа к кластеру
- Установленный CLI инструмент
cosign
- Установленный CLI инструмент
jq
Обзор процесса
Пошаговые инструкции
Шаги 1-3: Базовая настройка
Эти шаги идентичны руководству Quick Start: Signed Provenance. Пожалуйста, следуйте инструкциям в этом руководстве для:
-
Шаг 1: Генерация ключей подписи
-
Шаг 2: Настройка аутентификации
-
Шаг 3: Настройка Tekton Chains
Чтобы избежать генерации SLSA Provenance для TaskRun и PipelineRun одновременно, что повлияет на последующую проверку kyverno, сначала отключим SLSA Provenance для TaskRun.
TIP
Для выполнения этого процесса требуются права администратора платформы.
$ kubectl patch tektonconfigs.operator.tekton.dev config --type=merge -p='{
"spec": {
"chain": {
"artifacts.taskrun.storage": ""
}
}
}'
Шаг 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 непосредственно в пайплайне.
В продуктивных средах обычно:
- Используют задачу
git-clone для получения исходного кода из репозитория
- Строят образ с использованием Containerfile, находящегося в исходном коде
- Такой подход обеспечивает правильное управление версиями и разделение кода и конфигурации пайплайна
Объяснение полей 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: Скорректировать адрес базового образа.
image:
default: Адрес целевого образа для сборки.
Сохраните в файл с именем 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
Сохраните в файл с именем 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:
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: "Тип сборки должен быть tekton.dev/v1beta1/PipelineRun, а не {{ buildType }}"
- key: "{{ materials[?starts_with(uri, 'https://github.com/tektoncd/')] | length(@) }}"
operator: GreaterThan
value: 0
message: "В материалах должен быть хотя бы один элемент, начинающийся с https://github.com/tektoncd/, {{ materials }}"
Объяснение полей YAML
- Политика в целом совпадает с политикой из Проверка подписи образа
spec.rules[].verifyImages[].attestations[].conditions: Условия для проверки.
all: Все условия должны быть выполнены.
key: "{{ buildType }}": Тип сборки должен быть равен tekton.dev/v1beta1/PipelineRun.
key: "{{ materials[?starts_with(uri, 'https://github.com/tektoncd/')] | length(@) }}": В материалах должен быть хотя бы один элемент, начинающийся с https://github.com/tektoncd/.
Сохраните в файл с именем 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: "Тип сборки должен быть tekton.dev/v1beta1/PipelineRun, а не {{ buildType }}"
- key: "{{ materials[?starts_with(uri, 'https://gitlab.com/')] | length(@) }}"
operator: GreaterThan
value: 0
message: "В материалах должен быть хотя бы один элемент, начинающийся с 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 пайплайнах. В продуктивной среде рекомендуется:
- Настроить правильную изоляцию namespace и контроль доступа
- Реализовать безопасное управление ключами подписи
- Настроить мониторинг и оповещения о нарушениях политик
- Регулярно менять ключи подписи и обновлять политики безопасности
- Рассмотреть внедрение дополнительных мер безопасности, таких как сканирование уязвимостей
References