• Русский
  • Сканирование и верификация уязвимостей

    В ACP (Alauda Container Platform) можно использовать Tekton Pipeline для сборки и сканирования образа на наличие уязвимостей.

    В частности, используйте задачу trivy для генерации результатов сканирования уязвимостей, затем используйте cosign для загрузки аттестации результатов сканирования уязвимостей и, наконец, используйте kyverno для проверки аттестации результатов сканирования уязвимостей.

    Обзор функциональности

    Этот метод использует инструменты, подобные trivy, для сканирования образа на наличие уязвимостей, а затем использует Kyverno для проверки результатов сканирования уязвимостей:

    1. Используйте Tekton Task trivy для сканирования образа на наличие уязвимостей.
    2. Используйте Tekton Task cosign для загрузки результатов сканирования уязвимостей в образ.
    3. Настройте правила Kyverno для проверки результатов сканирования уязвимостей.
    4. Используйте образ для создания Pod, чтобы проверить результаты сканирования уязвимостей.

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

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

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

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

    • Кластер 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 с задачами trivy scanner и cosign upload
    5Запуск примера pipelineСоздайте и запустите PipelineRun с корректной конфигурацией
    6Ожидание подписиДождитесь, пока PipelineRun будет подписан Chains
    7Получение информации об образеИзвлеките URI и digest образа из PipelineRun
    8(Необязательно) Получение аттестации уязвимостейПолучите и проверьте аттестацию сканирования уязвимостей
    9Проверка с помощью KyvernoСоздайте и примените политику Kyverno для проверки результатов сканирования уязвимостей
    10(Необязательно) Требование свежего сканированияДобавьте условие, требующее, чтобы результаты сканирования уязвимостей были не старше 168 часов
    11ОчисткаУдалите тестовые ресурсы и политики

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

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

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

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

    Это ресурс Pipeline, который используется для сборки образа и генерации cosign-аттестации уязвимостей.

    apiVersion: tekton.dev/v1
    kind: Pipeline
    metadata:
      name: chains-demo-4
    spec:
      params:
        - 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-4: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)
      tasks:
        - name: generate-containerfile
          params:
            - name: script
              value: $(params.generate-containerfile)
          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
        - name: trivy-scanner
          params:
            - name: COMMAND
              value: |-
                set -x
    
                mkdir -p .git
    
                # support for insecure registry
                export TRIVY_INSECURE=true
    
                echo "generate cyclonedx sbom"
                trivy image --skip-db-update --skip-java-db-update --scanners vuln --format cyclonedx --output .git/sbom-cyclonedx.json $(tasks.build-image.results.IMAGE_URL)@$(tasks.build-image.results.IMAGE_DIGEST)
                cat .git/sbom-cyclonedx.json
    
                echo "trivy scan vulnerabilities based on cyclonedx sbom"
                trivy sbom --skip-db-update --skip-java-db-update --format cosign-vuln --output .git/trivy-scan-result.json .git/sbom-cyclonedx.json
                cat .git/trivy-scan-result.json
    
                echo "trivy scan vulnerabilities based on cyclonedx sbom and output in table format"
                trivy sbom --skip-db-update --skip-java-db-update --format table .git/sbom-cyclonedx.json
          runAfter:
            - build-image
          taskRef:
            params:
              - name: kind
                value: task
              - name: catalog
                value: catalog
              - name: name
                value: trivy-scanner
              - name: version
                value: "0.4"
            resolver: hub
          timeout: 30m0s
          workspaces:
            - name: source
              workspace: source
            - name: registryconfig
              workspace: registryconfig
        - name: cosign-uploads
          params:
            - name: COMMAND
              value: |-
                set -x
    
                export COSIGN_ALLOW_INSECURE_REGISTRY=true
                export COSIGN_TLOG_UPLOAD=false
                export COSIGN_KEY=$(workspaces.signkey.path)/cosign.key
    
                echo "Signing image vuln"
                cosign attest --type vuln --predicate .git/trivy-scan-result.json $(tasks.build-image.results.IMAGE_URL)@$(tasks.build-image.results.IMAGE_DIGEST)
    
                echo "Signing image sbom"
                cosign attest --type cyclonedx --predicate .git/sbom-cyclonedx.json $(tasks.build-image.results.IMAGE_URL)@$(tasks.build-image.results.IMAGE_DIGEST)
          runAfter:
            - trivy-scanner
          taskRef:
            params:
              - name: kind
                value: task
              - name: catalog
                value: catalog
              - name: name
                value: cosign
              - name: version
                value: "0.1"
            resolver: hub
          timeout: 30m0s
          workspaces:
            - name: source
              workspace: source
            - name: registryconfig
              workspace: registryconfig
            - name: signkey
              workspace: signkey
      workspaces:
        - name: source
          description: The workspace for source code.
        - name: registryconfig
          description: The workspace for distribution registry configuration.
        - name: signkey
          description: The workspace for private keys and passwords used for image signatures.
    TIP

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

    1. Используют задачу git-clone для получения исходного кода из вашего repository
    2. Собирают образ с использованием Containerfile, который уже находится в исходном коде
    3. Такой подход обеспечивает корректное управление версиями и сохраняет разделение между кодом и конфигурацией pipeline
    Пояснение полей YAML
    • То же самое, что и в Шаг 4: Создание примера Pipeline, но добавляется следующий контент:
      • workspaces:
        • signkey: workspace для private keys и паролей, используемых для подписей образов.
      • tasks:
        • trivy-scanner: задача для сканирования образа на наличие уязвимостей.
        • cosign-uploads: задача для загрузки аттестации результатов сканирования уязвимостей. :::

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

    $ export NAMESPACE=<default>
    
    # create the pipeline in the namespace
    $ kubectl create -n $NAMESPACE -f chains-demo-4.yaml
    
    pipeline.tekton.dev/chains-demo-4 created

    Шаг 5: Запуск примера Pipeline

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

    apiVersion: tekton.dev/v1
    kind: PipelineRun
    metadata:
      generateName: chains-demo-4-
    spec:
      pipelineRef:
        name: chains-demo-4
      taskRunTemplate:
        serviceAccountName: <default>
      workspaces:
        - name: registryconfig
          secret:
            secretName: <registry-credentials>
        - name: signkey
          secret:
            secretName: <signing-secrets>
        - name: source
          volumeClaimTemplate:
            spec:
              accessModes:
                - ReadWriteOnce
              resources:
                requests:
                  storage: 1Gi
              storageClassName: <nfs>

    :::details {title="Пояснение полей YAML"}

    • То же самое, что и в Шаг 5: Запуск примера Pipeline. Ниже приведены только различия.
    • workspaces
      • signkey: имя secret для signing key.
        • secret.secretName: signing secret, подготовленный на предыдущем шаге Получение signing secret. Но вам нужно создать новый secret в том же namespace, что и PipelineRun. :::

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

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

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

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

    Шаг 6: Дождитесь, пока PipelineRun будет подписан

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

    $ export NAMESPACE=<default>
    $ export PIPELINERUN_NAME=<chains-demo-4-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-4:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046

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

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

    Если вам интересно содержимое cosign vuln attestation, вы можете продолжить чтение следующего материала.

    Подробнее о cosign vuln attestation см. cosign vuln attestation

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

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

    Вывод будет похож на следующий, он содержит результаты сканирования уязвимостей.

    :::details {title="cosign vuln attestation"}

    {
      "_type": "https://in-toto.io/Statement/v0.1",
      "predicateType": "https://cosign.sigstore.dev/attestation/vuln/v1",
      "predicate": {
        "scanner": {
          "uri": "pkg:github/aquasecurity/trivy@dev",
          "version": "dev",
          "result": {
            "CreatedAt": "2025-06-07T07:05:30.098889688Z",
            "Metadata": {
              "OS": {
                "Family": "ubuntu",
                "Name": "24.04"
              }
            },
            "Results": [
              {
                "Class": "os-pkgs",
                "Packages": [
                  {
                    "Arch": "amd64",
                    "ID": "coreutils@9.4-3ubuntu6",
                    "Identifier": {
                      "BOMRef": "pkg:deb/ubuntu/coreutils@9.4-3ubuntu6?arch=amd64&distro=ubuntu-24.04",
                      "PURL": "pkg:deb/ubuntu/coreutils@9.4-3ubuntu6?arch=amd64&distro=ubuntu-24.04",
                      "UID": "82bb3c93286700bc"
                    },
                    "Licenses": [
                      "GPL-3.0-or-later",
                      "BSD-4-Clause-UC",
                      "GPL-3.0-only",
                      "ISC",
                      "FSFULLR",
                      "GFDL-1.3-no-invariants-only",
                      "GFDL-1.3-only"
                    ],
                    "Name": "coreutils"
                  }
                ],
                "Vulnerabilities": [
                  {
                    "CVSS": {
                      "nvd": {
                        "V2Score": 2.1,
                        "V2Vector": "AV:L/AC:L/Au:N/C:N/I:P/A:N",
                        "V3Score": 6.5,
                        "V3Vector": "CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N"
                      },
                      "redhat": {
                        "V2Score": 6.2,
                        "V2Vector": "AV:L/AC:H/Au:N/C:C/I:C/A:C",
                        "V3Score": 8.6,
                        "V3Vector": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H"
                      }
                    },
                    "InstalledVersion": "9.4-3ubuntu6",
                    "LastModifiedDate": "2025-04-20T01:37:25.86Z",
                    "PkgID": "coreutils@9.4-3ubuntu6",
                    "PkgName": "coreutils",
                    "PublishedDate": "2017-02-07T15:59:00.333Z",
                    "References": [
                      "http://seclists.org/oss-sec/2016/q1/452",
                      "http://www.openwall.com/lists/oss-security/2016/02/28/2",
                      "http://www.openwall.com/lists/oss-security/2016/02/28/3",
                      "https://access.redhat.com/security/cve/CVE-2016-2781",
                      "https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772%40%3Cdev.mina.apache.org%3E",
                      "https://lore.kernel.org/patchwork/patch/793178/",
                      "https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/v2.28/v2.28-ReleaseNotes",
                      "https://nvd.nist.gov/vuln/detail/CVE-2016-2781",
                      "https://www.cve.org/CVERecord?id=CVE-2016-2781"
                    ],
                    "Severity": "LOW",
                    "SeveritySource": "ubuntu",
                    "Status": "affected",
                    "VendorSeverity": {
                      "azure": 2,
                      "cbl-mariner": 2,
                      "nvd": 2,
                      "redhat": 2,
                      "ubuntu": 1
                    },
                    "VulnerabilityID": "CVE-2016-2781"
                  }
                ]
              }
            ],
            "SchemaVersion": 2
          }
        },
        "metadata": {
          "scanStartedOn": "2025-06-07T07:05:30.104726629Z",
          "scanFinishedOn": "2025-06-07T07:05:30.104726629Z"
        }
      }
    }

    :::details {title="Описание полей"}

    • predicateType: тип predicate.
    • predicate.scanner:
      • uri: URI сканера.
      • version: версия сканера.
      • result: результат сканирования уязвимостей.
        • CreatedAt: время завершения сканирования уязвимостей.
        • Metadata:
          • OS:
            • Family: семейство OS.
            • Name: название OS.
        • Results: результаты сканирования уязвимостей.
          • Class:
            • os-pkgs: пакеты OS.
            • lang-pkgs: языковые пакеты.
          • Packages: пакеты образа.
          • Vulnerabilities: уязвимости образа.
            • Severity: уровень серьезности уязвимости.
            • PkgID: идентификатор пакета уязвимости.
            • PkgName: имя пакета уязвимости.
            • CVSS: CVSS уязвимости.
              • nvd: NVD уязвимости.
              • redhat: Red Hat уязвимости. :::

    Шаг 9: Проверка результатов сканирования уязвимостей с помощью Kyverno

    Шаг 9.1: Создание политики Kyverno для отклонения образов с уязвимостями высокого риска

    TIP

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

    Подробнее о Kyverno ClusterPolicy см. Kyverno ClusterPolicy

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

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: reject-high-risk-image
    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://cosign.sigstore.dev/attestation/vuln/v1
                  attestors:
                    - entries:
                        - attestor:
                          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: "{{ scanner.result.Results[].Vulnerabilities[].Severity }}"
                          operator: AllNotIn
                          # supported values: UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL
                          value: ["HIGH", "CRITICAL"]
                          message: |
                            The image contains high-risk vulnerabilities, please fix them before proceeding.
                            Severity levels: {{ scanner.result.Results[].Vulnerabilities[].Severity }}
    
                        - key: "{{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`][] | length(@) }}"
                          operator: Equals
                          value: 0
                          message: |
                            The image contains high-risk vulnerabilities, please fix them before proceeding.
                            High-risk vulnerabilities (CVSS > 1.0): {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].CVSS.redhat.V3Score[] }}.
                            Severity levels: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].Severity[] }}.
                            PkgIDs: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].PkgID[] }}.

    :::details {title="Пояснение полей YAML"}

    • Политика в основном совпадает с политикой в Image Signature Verification
    • spec.rules[0].verifyImages[].attestations[0].conditions
      • type: тип cosign vuln attestation — https://cosign.sigstore.dev/attestation/vuln/v1
      • attestors: то же самое, что и выше.
      • conditions: условия, которые нужно проверить.
        • all: должны быть выполнены все условия.
          • key: "{{ scanner.result.Results[].Vulnerabilities[].Severity }}": уровень серьезности уязвимостей не должен быть HIGH или CRITICAL.
          • key: "{{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > 1.0][] | length(@) }}": количество уязвимостей с CVSS score выше 1.0 должно быть равно 0. :::

    Сохраните политику в yaml-файл с именем kyverno.reject-high-risk-image.yaml и примените его:

    $ kubectl apply -f kyverno.reject-high-risk-image.yaml
    
    clusterpolicy.kyverno.io/reject-high-risk-image configured

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

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

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

    $ export NAMESPACE=<policy>
    $ export IMAGE=<<registry>/test/chains/demo-4:latest@sha256:0f123204c44969876ed12f40066ccccbfd68361f68c91eb313ac764d59428bef>
    
    $ kubectl run -n $NAMESPACE vuln-image --image=${IMAGE} -- sleep 3600

    Если в вашем образе есть уязвимости высокого риска, Pod будет заблокирован политикой. Вы получите такой вывод:

    Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:
    
    resource Pod/policy/high-risk was blocked due to the following policies
    
    reject-high-risk-image:
      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-4:latest and predicate https://cosign.sigstore.dev/attestation/vuln/v1: The image contains high-risk vulnerabilities, please fix them before proceeding.
        High-risk vulnerabilities (CVSS > 1.0): [8.6,2.7,6.2,5.9,7.5,4.7,7.4,4.7,7.4,4.7,7.4,4.7,7.4,5.9,3.6,3.6,7.3,4.4,6.5,5.4].
        Severity levels: ["LOW","MEDIUM","LOW","LOW","MEDIUM","MEDIUM","MEDIUM","MEDIUM","MEDIUM","MEDIUM","MEDIUM","MEDIUM","MEDIUM","LOW","LOW","LOW","MEDIUM","MEDIUM","MEDIUM","MEDIUM"].
        PkgIDs: ["coreutils@9.4-3ubuntu6","gpgv@2.4.4-2ubuntu17","gpgv@2.4.4-2ubuntu17","libgcrypt20@1.10.3-2build1","liblzma5@5.6.1+really5.4.5-1build0.1","libpam-modules@1.5.3-5ubuntu5.1","libpam-modules@1.5.3-5ubuntu5.1","libpam-modules-bin@1.5.3-5ubuntu5.1","libpam-modules-bin@1.5.3-5ubuntu5.1","libpam-runtime@1.5.3-5ubuntu5.1","libpam-runtime@1.5.3-5ubuntu5.1","libpam0g@1.5.3-5ubuntu5.1","libpam0g@1.5.3-5ubuntu5.1","libssl3t64@3.0.13-0ubuntu3.5","login@1:4.13+dfsg1-4ubuntu3.2","passwd@1:4.13+dfsg1-4ubuntu3.2","perl-base@5.38.2-3.2build2.1","golang.org/x/net@v0.23.0","golang.org/x/net@v0.23.0","stdlib@v1.22.12"].

    Измените условия в ClusterPolicy, чтобы разрешить образы с уязвимостями высокого риска, но с CVSS score меньше 10.0.

    conditions:
      - all:
          - key: "{{ scanner.result.Results[].Vulnerabilities[].Severity }}"
            operator: AllNotIn
            value: ["CRITICAL"]
            message: |
              The image contains high-risk vulnerabilities, please fix them before proceeding.
              Severity levels: {{ scanner.result.Results[].Vulnerabilities[].Severity }}
    
          - key: "{{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`][] | length(@) }}"
            operator: Equals
            value: 0
            message: |
              The image contains high-risk vulnerabilities, please fix them before proceeding.
              High-risk vulnerabilities (CVSS > 10.0): {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`].CVSS.redhat.V3Score[] }}.
              Severity levels: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`].Severity[] }}.
              PkgIDs: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`].PkgID[] }}.

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

    $ kubectl run -n $NAMESPACE vuln-image --image=${IMAGE} -- sleep 3600

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

    Шаг 10: (Необязательно) Требование, чтобы результаты сканирования уязвимостей были не старше 168 часов

    TIP

    Если вы хотите добавить в политику дополнительные условия, можете продолжить чтение следующего материала.

    Поскольку Cosign Vulnerability Scan Record Attestation включает поле scanFinishedOn, а trivy соответствует этой спецификации, мы можем использовать это поле, чтобы определить, находятся ли результаты сканирования уязвимостей в пределах 168 часов.

    Нам нужно только добавить условие в ClusterPolicy, чтобы проверить, находится ли поле scanFinishedOn в пределах 168 часов.

    conditions:
      - all:
          - key: "{{ time_since('','{{metadata.scanFinishedOn}}','') }}"
            operator: LessThanOrEquals
            value: "168h"
            message: "The vulnerability scan results must be within 168 hours, not {{ metadata.scanFinishedOn }}"

    Здесь это не демонстрируется; заинтересованные читатели могут попробовать сами.

    Шаг 11: Очистка ресурсов

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

    $ export NAMESPACE=<policy>
    $ kubectl delete pod -n $NAMESPACE vuln-image

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

    $ kubectl delete clusterpolicy reject-high-risk-image

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

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

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

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

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

    Ссылки