Подписывание образов контейнеров при помощи Notation, Azure Key Vault и самоподписанного сертификата

Эта статья является частью серии по обеспечению целостности и подлинности образов контейнеров и других артефактов Open Container Initiative (OCI). Для полного рисунка начните с обзора, в котором объясняется, почему подписывание имеет значение и описывает различные сценарии.

Подписывание образов контейнеров — это процесс, который помогает обеспечить их подлинность и целостность. Цифровая подпись, которая добавляется в образ контейнера, проверяется во время развертывания. Сигнатура помогает убедиться, что изображение находится у доверенного издателя и не изменяется.

В этой статье рассматриваются следующие средства, связанные с процессом подписывания:

  • Нотация — это средство безопасности цепочки поставок с открытым исходным кодом, разработанное сообществом нотаричного проекта и поддерживаемым корпорацией Майкрософт. Он поддерживает подписывание и проверку образов контейнеров и других артефактов.

    Если вы хотите подписать образ контейнера с использованием Notation в конвейерах непрерывной интеграции и непрерывной доставки (CI/CD), следуйте рекомендациям по Azure Pipelines или GitHub Actions.

  • Azure Key Vault — это служба для хранения сертификатов с ключами подписывания. Нотация может использовать эти ключи с помощью плагина Key Vault (notation-azure-kv) для подписания и проверки образов контейнеров и других артефактов.

  • Реестр контейнеров Azure — это частный реестр, который можно использовать для присоединения подписей к образам контейнеров и другим артефактам, а также просмотра этих подписей.

В этой статье вы узнаете, как:

  • Установите командный интерфейс Notation (CLI) и подключаемый модуль для Key Vault.
  • Создайте самозаверяющий сертификат в Key Vault.
  • Создание и отправка образа контейнера с помощью задач реестра контейнеров.
  • Подпишите образ контейнера, используя Notation CLI и подключаемый модуль Key Vault.
  • Проверьте образ контейнера на основе подписи с помощью командной строки Notation.
  • Используйте метку времени.

Предпосылки

Установите CLI Notation и подключаемый модуль Key Vault

  1. Установите нотацию версии 1.3.2 в среде Linux AMD64. Чтобы скачать пакет для других сред, следуйте инструкциям по установке нотации.

    # Download, extract, and install
    curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v1.3.2/notation_1.3.2_linux_amd64.tar.gz
    tar xvzf notation.tar.gz
    
    # Copy the Notation binary to the desired bin directory in $PATH, for example
    cp ./notation /usr/local/bin
    
  2. Установите плагин Key Vault версии 1.2.1 (notation-azure-kv) в среде Linux AMD64.

    Замечание

    Вы можете найти URL-адрес и контрольную сумму SHA256 для подключаемого модуля на странице выпуска подключаемого модуля.

    notation plugin install --url https://github.com/Azure/notation-azure-kv/releases/download/v1.2.1/notation-azure-kv_1.2.1_linux_amd64.tar.gz --sha256sum 67c5ccaaf28dd44d2b6572684d84e344a02c2258af1d65ead3910b3156d3eaf5
    
  3. Выведите список доступных подключаемых модулей и убедитесь, что notation-azure-kv подключаемый модуль с версией 1.2.1 включен в список:

    notation plugin ls
    

Настройка переменных среды

Чтобы упростить выполнение команд в этой статье, укажите значения ресурсов Azure для сопоставления существующих ресурсов Реестра контейнеров и Key Vault.

  1. Настройка имен ресурсов Key Vault:

    AKV_SUB_ID=myAkvSubscriptionId
    AKV_RG=myAkvResourceGroup
    # Name of the existing key vault used to store the signing keys
    AKV_NAME=myakv
    # Name of the certificate created in the key vault
    CERT_NAME=wabbit-networks-io
    CERT_SUBJECT="CN=wabbit-networks.io,O=Notation,L=Seattle,ST=WA,C=US"
    CERT_PATH=./${CERT_NAME}.pem
    
  2. Настройте реестр контейнеров и имена ресурсов изображений:

    ACR_SUB_ID=myAcrSubscriptionId
    ACR_RG=myAcrResourceGroup
    # Name of the existing registry (example: myregistry.azurecr.io)
    ACR_NAME=myregistry
    # Existing full domain of the container registry
    REGISTRY=$ACR_NAME.azurecr.io
    # Container name inside the container registry where the image will be stored
    REPO=net-monitor
    TAG=v1
    IMAGE=$REGISTRY/${REPO}:$TAG
    # Source code directory that contains the Dockerfile to build
    IMAGE_SOURCE=https://github.com/wabbit-networks/net-monitor.git#main
    

Вход с помощью Azure CLI

az login

Дополнительные сведения см. в статье "Аутентификация в Azure" с помощью Azure CLI.

Предоставление разрешений на доступ к реестру контейнеров и Хранилищу ключей

При работе с реестром контейнеров и Key Vault необходимо предоставить соответствующие разрешения, чтобы обеспечить безопасный и контролируемый доступ. Вы можете авторизовать доступ для различных сущностей, таких как пользовательские субъекты, служебные субъекты или управляемые идентичности в зависимости от конкретных сценариев. В этой статье доступ авторизован для пользователя Azure, выполнившего вход.

Авторизация доступа к реестру контейнеров

Для реестров, поддерживающих управление доступом на основе атрибутов Microsoft Entra (ABAC), роли Container Registry Repository Reader и Container Registry Repository Writer необходимы для создания и подписания образов контейнеров в реестре контейнеров.

Для реестров, не включенных в ABAC, роли AcrPull и AcrPush необходимы.

Дополнительные сведения об ABAC см. в разделе "Управление доступом на основе атрибутов Microsoft Entra" для разрешений репозитория.

  1. Задайте подписку, содержащую ресурс реестра контейнеров:

    az account set --subscription $ACR_SUB_ID
    
  2. Назначьте роли. Правильная роль, используемая в назначении ролей, зависит от того, включён ли реестр ABAC или нет.

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    ROLE1="Container Registry Repository Reader" # For ABAC-enabled registries. Otherwise, use "AcrPull" for non-ABAC-enabled registries.
    ROLE2="Container Registry Repository Writer" # For ABAC-enabled registries. Otherwise, use "AcrPush" for non-ABAC-enabled registries.
    az role assignment create --role "$ROLE1" --role "$ROLE2" --assignee $USER_ID --scope "/subscriptions/$ACR_SUB_ID/resourceGroups/$ACR_RG/providers/Microsoft.ContainerRegistry/registries/$ACR_NAME"
    

Авторизация доступа к Key Vault

В этом разделе рассматриваются два варианта авторизации доступа к Key Vault.

Для подписывания с помощью самозаверяемых сертификатов требуются следующие роли:

  • Key Vault Certificates Officer для создания и чтения сертификатов
  • Key Vault Certificates User для чтения существующих сертификатов
  • Key Vault Crypto User для операций подписывания

Дополнительные сведения о доступе Key Vault с помощью управления доступом на основе ролей Azure (RBAC) см. в статье Предоставление доступа к ключам Key Vault, сертификатам и секретам с помощью управления доступом на основе ролей Azure.

  1. Задайте подписку, содержащую ресурс Key Vault:

    az account set --subscription $AKV_SUB_ID
    
  2. Назначьте роли:

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    az role assignment create --role "Key Vault Certificates Officer" --role "Key Vault Crypto User" --assignee $USER_ID --scope "/subscriptions/$AKV_SUB_ID/resourceGroups/$AKV_RG/providers/Microsoft.KeyVault/vaults/$AKV_NAME"
    

Назначение политики доступа в Key Vault (устаревшая версия)

Для удостоверения требуются следующие разрешения:

  • Create разрешения для создания сертификата
  • Get разрешения на чтение существующих сертификатов
  • Sign разрешения для операций подписывания

Чтобы узнать больше о назначении политики принципалу, см. статью "Назначение политики доступа к хранилищу ключей (версия для устаревших систем)."

  1. Задайте подписку, содержащую ресурс Key Vault:

    az account set --subscription $AKV_SUB_ID
    
  2. Задайте политику доступа в Key Vault:

    USER_ID=$(az ad signed-in-user show --query id -o tsv)
    az keyvault set-policy -n $AKV_NAME --certificate-permissions create get --key-permissions sign --object-id $USER_ID
    

Это важно

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

Создание самозаверяющего сертификата в Key Vault (Azure CLI)

Ниже показано, как создать самозаверяющий сертификат для тестирования.

  1. Создайте файл политики сертификата.

    После выполнения файла политики сертификата с помощью следующего кода он создает действительный сертификат, совместимый с требованиями сертификата нотаричного проекта в Key Vault. Значение для ekus предназначено для подписывания кода, однако для Нотации подписывание артефактов не обязательно. Субъект используется позже в качестве доверенной личности во время проверки.

    cat <<EOF > ./my_policy.json
    {
        "issuerParameters": {
        "certificateTransparency": null,
        "name": "Self"
        },
        "keyProperties": {
          "exportable": false,
          "keySize": 2048,
          "keyType": "RSA",
          "reuseKey": true
        },
        "secretProperties": {
          "contentType": "application/x-pem-file"
        },
        "x509CertificateProperties": {
        "ekus": [
            "1.3.6.1.5.5.7.3.3"
        ],
        "keyUsage": [
            "digitalSignature"
        ],
        "subject": "$CERT_SUBJECT",
        "validityInMonths": 12
        }
    }
    EOF
    
  2. Создайте сертификат:

    az keyvault certificate create -n $CERT_NAME --vault-name $AKV_NAME -p @my_policy.json
    

Подпишите образ контейнера, используя CLI Notation и модуль Key Vault.

  1. Аутентификация в реестре контейнеров с использованием вашего индивидуального удостоверения Azure.

    az acr login --name $ACR_NAME
    

    Это важно

    Если у вас установлен Docker в системе, и вы использовали az acr login или docker login для аутентификации в реестре контейнеров, ваши учетные данные уже хранятся и доступны для Notation. В этом случае вам не нужно снова запускать notation login, чтобы выполнить аутентификацию в вашем реестре контейнеров. Чтобы узнать больше о параметрах аутентификации для нотации, см. статью «Аутентификация с помощью реестров, совместимых с OCI».

  2. Создание и отправка нового образа с помощью задач реестра контейнеров Azure. Всегда используйте значение дайджеста для идентификации образа для цифровой подписи, так как теги изменяемы и могут быть перезаписаны.

    DIGEST=$(az acr build -r $ACR_NAME -t $REGISTRY/${REPO}:$TAG $IMAGE_SOURCE --no-logs --query "outputImages[0].digest" -o tsv)
    IMAGE=$REGISTRY/${REPO}@$DIGEST
    

    В этой статье, если образ уже создан и хранится в реестре, тег служит идентификатором этого образа для удобства:

    IMAGE=$REGISTRY/${REPO}:$TAG
    
  3. Получите идентификатор ключа подписывания. Сертификат в Key Vault может иметь несколько версий. Следующая команда получает идентификатор ключа последней версии:

    KEY_ID=$(az keyvault certificate show -n $CERT_NAME --vault-name $AKV_NAME --query 'kid' -o tsv)
    
  4. Подпишите образ контейнера в формате подписи и шифрования объектов CBOR (COSE) с использованием идентификатора ключа подписи. Чтобы войти с помощью самозаверяющего сертификата, необходимо задать значение self_signed=trueконфигурации подключаемого модуля.

    notation sign --signature-format cose --id $KEY_ID --plugin azure-kv --plugin-config self_signed=true $IMAGE
    

    Для аутентификации в Key Vault по умолчанию поочередно используются следующие типы учетных данных (если они включены).

    Если вы хотите указать тип учетных данных, используйте дополнительную конфигурацию credential_type подключаемого модуля. Например, вы можете явно установить credential_type как azurecli для использования учетных данных Azure CLI, как показано в этом примере:

    notation sign --signature-format cose --id $KEY_ID --plugin azure-kv --plugin-config self_signed=true --plugin-config credential_type=azurecli $IMAGE
    

    В следующей таблице показаны значения credential_type для различных типов учетных данных.

    Тип учетных данных Значение для credential_type
    Учетные данные для среды environment
    Идентификационные данные рабочей нагрузки workloadid
    Учетные данные для управляемого удостоверения managedid
    Учетные данные для входа с помощью Azure CLI azurecli

    Замечание

    С версии 1.2.0, Notation использует схему тега ссылок OCI для хранения подписи в реестре контейнеров по умолчанию. При необходимости можно включить API ссылок OCI с помощью флага --force-referrers-tag false. Функции реестра контейнеров поддерживают API ссылок OCI, за исключением реестра, зашифрованного с помощью ключей, управляемых клиентом (CMKs).

  5. Просмотрите график подписанных изображений и связанных подписей:

    notation ls $IMAGE
    

Проверьте образ контейнера с помощью Notation CLI

Чтобы проверить образ контейнера, добавьте корневой сертификат, который подписывает конечный сертификат, в хранилище доверенных сертификатов и создайте политики доверия для его проверки. Для самоподписанного сертификата, используемого в этой статье, корневой сертификат — это самоподписанный сертификат.

  1. Скачайте общедоступный сертификат:

    az keyvault certificate download --name $CERT_NAME --vault-name $AKV_NAME --file $CERT_PATH
    
  2. Добавьте скачанный общедоступный сертификат в именованное хранилище доверия для проверки подписи:

    STORE_TYPE="ca"
    STORE_NAME="wabbit-networks.io"
    notation cert add --type $STORE_TYPE --store $STORE_NAME $CERT_PATH
    
  3. Список сертификатов для подтверждения:

    notation cert ls
    
  4. Настройте политику доверия перед проверкой.

    Политики доверия позволяют пользователям указывать настраиваемые политики проверки подлинности. В следующем примере настраивается политика доверия с именем wabbit-networks-images. Эта политика применяется ко всем артефактам $REGISTRY/$REPO и использует именованное хранилище сертификатов $STORE_NAME типа $STORE_TYPE. Кроме того, предполагается, что пользователь доверяет определенному удостоверению, идентифицированному по субъекту X.509 $CERT_SUBJECT. Дополнительные сведения см. в спецификации политики доверия и хранилища доверия.

    cat <<EOF > ./trustpolicy.json
    {
        "version": "1.0",
        "trustPolicies": [
            {
                "name": "wabbit-networks-images",
                "registryScopes": [ "$REGISTRY/$REPO" ],
                "signatureVerification": {
                    "level" : "strict" 
                },
                "trustStores": [ "$STORE_TYPE:$STORE_NAME" ],
                "trustedIdentities": [
                    "x509.subject: $CERT_SUBJECT"
                ]
            }
        ]
    }
    EOF
    
  5. Используется notation policy для импорта конфигурации политики доверия из созданного ранее JSON-файла:

    notation policy import ./trustpolicy.json
    notation policy show
    
  6. Используйте notation verify для проверки того, что образ контейнера не был изменен после времени сборки:

    notation verify $IMAGE
    

    После успешной проверки изображения с помощью политики доверия хэш-дайджест SHA256 проверенного образа возвращается в успешном выходном сообщении.

Используйте метки времени

С момента выпуска версии 1.2.0, Нотация поддерживает совместимую с RFC 3161 метку времени. Это улучшение увеличивает доверие к подписям, созданным в течение срока действия сертификата, посредством доверия центру метки времени (TSA). Это доверие обеспечивает успешную проверку подписи даже после истечения срока действия сертификатов.

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

Технология меток времени снижает затраты, устраняя необходимость периодического переподписания образов из-за истечения сертификата. Эта возможность особенно важна при использовании коротких сертификатов. Подробные инструкции по подписи и проверке изображений с помощью метки времени см. в руководстве по метке времени нотаричного проекта.

Нотация предоставляет решения CI/CD в Azure Pipelines и GitHub Actions:

Чтобы обеспечить развертывание только доверенных образов контейнеров в службе Azure Kubernetes (AKS):