cosign: подпись и проверка образов Docker перед запуском

Trivy отвечает на вопрос «есть ли в образе известные уязвимости». Отдельный вопрос — тот ли это вообще образ, который собрала ваша сборка, или кто-то подменил его в реестре или по дороге. cosign закрывает именно это: подписывает образ при публикации и проверяет подпись перед запуском. Ниже порядок и результаты проверки.
Статья опирается на логику направления «Управление уязвимостями» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Проверка выполнена с cosign 2.4.1 на тестовом реестре в контейнере, после неё всё удалено.
Что должно получиться
| Уровень | Что это значит на практике |
|---|---|
| 2. Гигиенический | Образы, собираемые конвейером, подписываются, а при развёртывании проверяется, что образ подписан известным ключом и не подменён |
Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.
Ключи и подпись
cosign generate-key-pair
# Private key written to cosign.key
# Public key written to cosign.pub
Приватный ключ (cosign.key) хранится у сборочного конвейера (в хранилище секретов, не в репозитории), публичный (cosign.pub) распространяется всем, кто проверяет образы — секретности он не требует. Подпись при публикации:
cosign sign --key cosign.key --yes РЕЕСТР/образ:тег
Команда требует доступа к реестру (та же авторизация, что для docker push) и пишет подпись туда же, рядом с образом, отдельным объектом. Для своего приватного реестра без публичного журнала прозрачности добавляйте --yes для автоматизации и, при необходимости, ключ хранения пароля через переменную окружения COSIGN_PASSWORD.
Проверка перед запуском
cosign verify --key cosign.pub РЕЕСТР/образ:тег
Команда возвращает успех только если найдена подпись, сделанная именно этим ключом. Встройте её как обязательный шаг перед развёртыванием: конвейер, который разворачивает неподписанный или подписанный не тем ключом образ, должен останавливаться.
Результат проверки
| Сценарий | Результат |
|---|---|
| Проверка подписанного образа верным ключом | Успех: «The signatures were verified against the specified public key» |
| Проверка тем же образом, подписанным другим ключом | Отказ: «no matching signatures: invalid signature when validating ASN.1 encoded signature» |
| Проверка образа, который не подписывали вовсе (другое содержимое под тем же именем) | Отказ: «no signatures found» |
| Публикация без изменения содержимого под новым тегом | Проверка нового тега проходит успешно без повторной подписи |
Последняя строка — не ошибка, а важная деталь устройства: cosign привязывает подпись к цифровому отпечатку (digest) содержимого образа, а не к тегу. Если под новым тегом оказался байт-в-байт тот же образ, у него тот же отпечаток, и ранее сделанная подпись к нему по-прежнему относится. А вот тег с действительно другим содержимым подписи не наследует: проверка на нём честно возвращает «no signatures found», даже если имя образа и тег выглядят знакомо.
Порядок внедрения
- Создайте ключевую пару для конвейера сборки, приватный ключ — в хранилище секретов CI, не в репозитории.
- Подписывайте образ последним шагом сборки, после прохождения проверок (статический анализ, сканирование уязвимостей) — подпись удостоверяет «этот образ прошёл наш конвейер», а не просто «мы его собрали».
- Распространите публичный ключ на все площадки, где образы разворачиваются.
- Добавьте обязательную проверку перед развёртыванием, с остановкой при отказе, а не предупреждением в журнале.
- Сверяйте по отпечатку, а не только по тегу, если нужна максимальная строгость: тег можно переставить на другой образ, отпечаток — нет.
Диагностика
| Симптом | Причина и что делать |
|---|---|
permission denied при записи ключей или подписи |
Образ cosign запускается от непривилегированного пользователя (контейнер без прав root). Дайте процессу право записи в рабочий каталог или запускайте с подходящим пользователем |
| Предупреждение про тег вместо отпечатка при подписи | Это ожидаемое предупреждение cosign: подписывать лучше по отпечатку (образ@sha256:…), чтобы точно знать, что именно подписано |
| Проверка проходит для образа, который вы не подписывали | Образ под новым тегом оказался байт-в-байт тем же, что уже подписанный (одинаковый отпечаток). Это не ошибка, а свойство адресации по содержимому |
Проверка без --insecure-ignore-tlog уходит в публичный журнал прозрачности |
По умолчанию cosign пишет запись в публичный Rekor. Для приватных образов без публичного журнала используйте отдельный приватный журнал или заведомо офлайн-режим — обычное использование сигнатур поверх своего реестра этого не требует |
Ограничения
Подпись подтверждает, что образ не изменился после подписания и подписан владельцем ключа — она не проверяет содержимое образа на уязвимости (для этого Trivy) и не защищает от того, что сам ключ или сборочный конвейер скомпрометированы. Если приватный ключ утёк, нужно немедленно считать скомпрометированными все подписи, сделанные им, и перейти на новую пару.
Где это в модели зрелости
Материал относится к направлению «Управление уязвимостями» (материалы направления, карта зрелости). Уровень 2 требует контроля над тем, что реально развёртывается: подпись образов закрывает конкретный риск — подмену образа между сборкой и запуском, — который сканирование уязвимостей само по себе не ловит.
Как проверить
- Каждый образ, выходящий из конвейера сборки, подписан ключом конвейера.
- Развёртывание проверяет подпись и останавливается при её отсутствии или несовпадении.
- Приватный ключ хранится в защищённом хранилище секретов, а не в репозитории и не в журналах сборки.
- Есть процедура на случай компрометации ключа: немедленная замена пары и пересборка затронутых образов.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
- Документация cosign (проект Sigstore): docs.sigstore.dev; исходный код: github.com/sigstore/cosign.


















