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

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

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

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

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

    1. Используйте задачу Tekton trivy для сканирования образа на уязвимости.
    2. Используйте задачу Tekton cosign для загрузки результатов сканирования уязвимостей в образ.
    3. Настройте правила Kyverno для проверки результатов сканирования уязвимостей.
    4. Используйте образ для создания 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-хранилища и конфигурация подписи, отключение TaskRun SLSA Provenance
    4Создание примерного пайплайнаСоздание определения пайплайна с задачами сканера trivy и загрузки cosign
    5Запуск примерного пайплайнаСоздание и запуск PipelineRun с правильной конфигурацией
    6Ожидание подписиОжидание подписи PipelineRun Chains
    7Получение информации об образеИзвлечение URI и дайджеста образа из PipelineRun
    8(Опционально) Получение аттестации уязвимостейПолучение и проверка аттестации результатов сканирования уязвимостей
    9Проверка с помощью KyvernoСоздание и применение политики Kyverno для проверки результатов сканирования уязвимостей
    10(Опционально) Требование свежести сканированияДобавление условия, требующего результатов сканирования уязвимостей не старше 168 часов
    11ОчисткаУдаление тестовых ресурсов и политик

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

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

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

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

    Это ресурс 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 генерируются непосредственно внутри пайплайна. В продуктивных средах обычно:

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

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

    $ export NAMESPACE=<default>
    
    # создайте пайплайн в namespace
    $ kubectl create -n $NAMESPACE -f chains-demo-4.yaml
    
    pipeline.tekton.dev/chains-demo-4 created

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

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

    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"}

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

    $ export NAMESPACE=<default>
    
    # создайте запуск пайплайна в 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

    # Получить 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-4:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046

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

    Шаг 8: (Опционально) Получение аттестации уязвимостей cosign

    Если вас интересует содержимое аттестации уязвимостей cosign, вы можете продолжить чтение.

    Подробнее об аттестации уязвимостей cosign смотрите в cosign vuln attestation

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

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

    {
      "_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.scanner:
      • uri: URI сканера.
      • version: Версия сканера.
      • result: Результат сканирования уязвимостей.
        • CreatedAt: Время завершения сканирования уязвимостей.
        • Metadata:
          • OS:
            • Family: Семейство ОС.
            • Name: Название ОС.
        • Results: Результаты сканирования уязвимостей.
          • Class:
            • os-pkgs: Пакеты ОС.
            • lang-pkgs: Пакеты языка.
          • Packages: Пакеты образа.
          • Vulnerabilities: Уязвимости образа.
            • Severity: Уровень серьезности уязвимости.
            • PkgID: Идентификатор пакета уязвимости.
            • PkgName: Название пакета уязвимости.
            • CVSS: CVSS уязвимости.
              • nvd: NVD уязвимости.
              • redhat: Red Hat уязвимости. :::

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

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

    TIP

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

    Подробнее о 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:
                  # Учетные данные должны существовать в namespace, где развернут kyverno
                  - registry-credentials
    
              attestations:
                - type: https://cosign.sigstore.dev/attestation/vuln/v1
                  attestors:
                    - entries:
                        - attestor:
                          keys:
                            publicKeys: |- # <- Публичный ключ подписывающего
                              -----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
                          # поддерживаемые значения: UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL
                          value: ["HIGH", "CRITICAL"]
                          message: |
                            Образ содержит уязвимости высокого риска, пожалуйста, исправьте их перед продолжением.
                            Уровни серьезности: {{ scanner.result.Results[].Vulnerabilities[].Severity }}
    
                        - key: "{{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`][] | length(@) }}"
                          operator: Equals
                          value: 0
                          message: |
                            Образ содержит уязвимости высокого риска, пожалуйста, исправьте их перед продолжением.
                            Уязвимости высокого риска (CVSS > 1.0): {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].CVSS.redhat.V3Score[] }}.
                            Уровни серьезности: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].Severity[] }}.
                            PkgIDs: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].PkgID[] }}.

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

    • Политика в основном совпадает с той, что в Проверке подписи образа
    • spec.rules[0].verifyImages[].attestations[0].conditions
      • type: Тип аттестации уязвимостей cosign — 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 баллом выше 1.0 должно быть 0. :::

    Сохраните политику в файл с именем 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 баллом меньше 10.0.

    conditions:
      - all:
          - key: "{{ scanner.result.Results[].Vulnerabilities[].Severity }}"
            operator: AllNotIn
            value: ["CRITICAL"]
            message: |
              Образ содержит уязвимости высокого риска, пожалуйста, исправьте их перед продолжением.
              Уровни серьезности: {{ scanner.result.Results[].Vulnerabilities[].Severity }}
    
          - key: "{{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`][] | length(@) }}"
            operator: Equals
            value: 0
            message: |
              Образ содержит уязвимости высокого риска, пожалуйста, исправьте их перед продолжением.
              Уязвимости высокого риска (CVSS > 10.0): {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `10.0`].CVSS.redhat.V3Score[] }}.
              Уровни серьезности: {{ 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: "Результаты сканирования уязвимостей должны быть не старше 168 часов, а не {{ metadata.scanFinishedOn }}"

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

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

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

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

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

    $ kubectl delete clusterpolicy reject-high-risk-image

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

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

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

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

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

    Ссылки