Supply chain: сканирование, SBOM и подпись образов

Рейтинг: 71.7% · 16 голосов
Практический курс по Docker: образы, контейнеры, тома, сети, Compose и продакшен. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
Marina_DevOps
Сообщения: 54
Зарегистрирован: 11 май 2026, 05:31

Supply chain: сканирование, SBOM и подпись образов

Сообщение Marina_DevOps »

Оглавление курса (46)
  1. Что такое Docker и какие задачи он решает
  2. Установка Docker и запуск первого контейнера
  3. Образы: слои, теги и реестр Docker Hub
  4. Пишем свой Dockerfile
  5. Тома и хранение данных: volumes и bind mounts
  6. Сети в Docker: связываем контейнеры между собой
  7. Переменные окружения и конфигурация контейнеров
  8. Docker Compose: поднимаем многоконтейнерное приложение
  9. Оптимизация образов: multi-stage сборка, размер и кэш слоёв
  10. Логи, отладка и мониторинг контейнеров
  11. Базовая безопасность контейнеров
  12. Подготовка к продакшену: что важно учесть
  13. Dockerfile глубже: ENTRYPOINT и CMD, HEALTHCHECK, .dockerignore, запуск не от root
  14. Реестры образов: приватные registry, push и pull, теги и digest, imagePullSecrets
  15. BuildKit и buildx: multi-arch сборки, секреты сборки, экспорт кэша
  16. Docker в CI/CD: автосборка, сканирование образов (Trivy, Docker Scout), публикация
  17. Итоговый проект и куда расти: от Dockerfile до прода, обзор оркестрации (Kubernetes, Podman, OCI)
  18. Архитектура Docker: dockerd, containerd, runc и OCI
  19. Как работает изоляция: namespaces и cgroups
  20. Образы изнутри: слои, OverlayFS и storage driver
  21. Жизненный цикл контейнера и политики перезапуска
  22. Отладка контейнеров: exec, attach, nsenter, debug
  23. Сети Docker глубже: bridge, overlay, macvlan и iptables
  24. Docker Compose глубже: profiles, healthcheck-зависимости, override
  25. Данные и тома глубже: драйверы, бэкап, права
  26. Секреты и конфигурация: как не светить пароли
  27. Ограничение ресурсов: CPU, память, OOM и cgroups
  28. Здоровье и автозапуск: HEALTHCHECK, init и сигналы
  29. Логирование контейнеров: драйверы, ротация, сбор
  30. Мониторинг контейнеров: stats, cAdvisor, Prometheus
  31. Безопасность глубже: rootless, user namespaces, seccomp, capabilities
  32. Supply chain: сканирование, SBOM и подпись образов (вы здесь)
  33. Глубокая оптимизация образов: distroless, scratch, кэш
  34. Multi-arch и сборка под ARM: buildx и эмуляция
  35. Реестры в продакшене: Harbor и облачные registry
  36. Docker без Docker: Podman, containerd, nerdctl, Buildah
  37. От Compose к оркестрации: когда контейнеров становится много
  38. Docker Swarm: встроенная оркестрация
  39. Сборка образов в CI без демона: kaniko, BuildKit, Buildah
  40. Траблшутинг Docker: типичные ошибки и как чинить
  41. Производительность и эксплуатация Docker-хоста
  42. Docker Desktop, WSL2 и альтернативы на Windows и Mac
  43. Контейнеризация приложений: бэкенд, фронтенд, БД
  44. Docker и Kubernetes: containerd, миграция, kompose
  45. Лучшие практики и антипаттерны Docker
  46. Сквозной проект: production-ready стек и путь дальше
Боль: ты не знаешь, что внутри твоего образа

Ты собрал образ из пяти строчек Dockerfile. Внутри - сотни системных пакетов из базового слоя, десятки прямых зависимостей приложения и сотни транзитивных, про которые ты вообще не в курсе. Любая из них может тащить дыру, бэкдор или просто протухшую версию OpenSSL с громким CVE. Когда в проде взрывается очередной Log4Shell или XZ-бэкдор, у тебя ровно два варианта: либо за минуты ответить "да, у нас это есть вот в этих 14 сервисах, патчим", либо сутки вручную перебирать образы и молиться.

Цепочка поставки ПО (software supply chain) - это весь путь от чужого кода до твоего контейнера в проде. И каждое звено - точка атаки: подменили базовый образ, протащили зловредный пакет через typosquatting, скомпрометировали CI и подсунули чужой слой. История 2024 года с бэкдором в xz-utils и серия атак на сам инструментарий (в марте 2026 дважды за две недели компрометировали цепочку поставки Trivy, и волна докатилась до зависимых проектов) - это не страшилки, а ровно то, от чего защищаемся в этом уроке. Защита держится на трёх китах: сканирование (что внутри и насколько дырявое), SBOM (опись содержимого) и подпись (доказательство, что образ тот самый и собран тем, кем надо).

Изображение

Сканирование уязвимостей: trivy docker, grype и docker scout

Сканер берёт образ, разбирает его на слои, вытаскивает список установленных пакетов (по метаданным менеджеров: apk, dpkg, rpm, а также lock-файлам npm/pip/go.sum) и сверяет версии с базами CVE (NVD, GHSA, базы дистрибутивов). Ключевое: сканер не запускает образ и не "нюхает" байты - он читает декларативные метаданные. Поэтому пакет, поставленный мимо менеджера (распакованный tar вручную), сканер не увидит. Это первый краевой случай, который ловит новичков.

Trivy (Aqua Security) - самый ходовой опенсорсный сканер. Запрос "trivy docker" в Яндексе ведёт именно к нему. Базовый прогон по образу:

Код: Выделить всё

trivy image --severity HIGH,CRITICAL nginx:1.27
Кусок вывода и разбор полей:

Код: Выделить всё

nginx:1.27 (debian 12.5)
Total: 2 (HIGH: 2, CRITICAL: 0)

Library   Vulnerability   Severity  Status      Installed  Fixed
libxml2   CVE-2024-25062  HIGH      fixed       2.9.14     2.9.14+dfsg-1.3+deb12u1
zlib1g    CVE-2023-45853  HIGH      affected    1.2.13     -
  • Library - уязвимый пакет. Severity - класс по CVSS. Самое важное поле - Status: fixed значит фикс есть, надо обновить базовый образ; affected или will_not_fix - мейнтейнер пока (или вообще) не выпустил патч, бесполезно дёргать. Fixed показывает, до какой версии обновляться.
  • Флаг --ignore-unfixed прячет то, что всё равно нельзя пофиксить - снижает шум в CI. --exit-code 1 валит пайплайн при находках. --vuln-type os,library разделяет дыры базового образа и зависимостей приложения.
Чтобы CI не плёлся в базы по сети каждый раз, держи кеш через --cache-dir и обновляй базы отдельным шагом (trivy image --download-db-only). И помни: severity по CVSS - это не приоритет. Критичный CVE в библиотеке, которая не достижима из твоего кода, опаснее на бумаге, чем реальная дыра в обработчике запросов. Поэтому современные сканеры тянут EPSS (вероятность эксплуатации) и контекст достижимости.

Grype (Anchore) - ближайшая альтернатива, особенно сильна в паре со своим генератором SBOM - syft. Умеет принимать на вход не только образ, но и готовый SBOM, что в разы быстрее в пайплайне:

Код: Выделить всё

grype registry.cyberlake.ru/app:1.4.0 -o table
docker scout - встроенный в Docker сканер, пришедший на смену старому "docker scan" (тот завязан на Snyk и снят с поддержки ещё в 2023, так что старый гайд по "docker scan" уже мимо). Подпись образа docker и анализ дыр теперь живут в одной экосистеме. Главная фишка docker scout - не просто список CVE, а рекомендации по базовому образу:

Код: Выделить всё

docker scout cves local://app:1.4.0
docker scout recommendations local://app:1.4.0
Команда recommendations подскажет: "перейди с node:20 на node:20-slim - минус 80% пакетов и минус 30 CVE". Это бьёт в корень: большинство дыр приезжает не из твоего кода, а из жирного базового слоя.

SBOM docker: опись содержимого образа (syft, docker sbom)

SBOM (Software Bill of Materials) - это машиночитаемая опись всего, что внутри образа: пакеты, версии, лицензии, хеши. Аналогия: состав на упаковке продукта. Сканер отвечает "дырявое ли это сейчас", SBOM отвечает "из чего это вообще состоит" - и хранится навсегда. Разница принципиальна: когда завтра выйдет новый CVE на пакет, который сегодня считался чистым, ты не пересобираешь образ - ты прогоняешь свежую базу по уже сохранённому SBOM и за секунды находишь все затронутые сервисы.

Два формата-стандарта: SPDX (под крылом Linux Foundation) и CycloneDX (OWASP). SPDX чаще требуют в комплаенсе и госзакупках, CycloneDX удобнее для security-конвейеров. Оба - валидный выбор, многие генерят сразу оба.

Инструмент де-факто - syft (Anchore):

Код: Выделить всё

syft registry.cyberlake.ru/app:1.4.0 -o spdx-json=app.spdx.json
syft app:1.4.0 -o cyclonedx-json=app.cdx.json
Запрос "sbom docker" приведёт и к встроенной команде docker sbom (под капотом тот же syft) - быстро глянуть состав без отдельной установки:

Код: Выделить всё

docker sbom app:1.4.0
Кусок вывода:

Код: Выделить всё

NAME            VERSION        TYPE
libssl3         3.0.13-1       deb
ca-certificates 20230311       deb
express         4.19.2         npm
lodash          4.17.21        npm
Дальше эту опись скармливаешь сканеру и сканируешь без образа: grype sbom:app.cdx.json или trivy sbom app.cdx.json. Так SBOM становится "точкой истины" в пайплайне. Дальше эту опись надо прикрепить к образу как аттестацию (attestation) - подписанное утверждение, привязанное к конкретному digest образа. Так SBOM не живёт отдельным файлом, который потерялся, а ездит вместе с образом в реестре.

Подпись образа docker: cosign, Sigstore и provenance

Сканирование говорит "образ чистый". Но как доказать, что в прод приедет именно этот образ, собранный твоим пайплайном, а не подменённый? Тег - не идентичность: nginx:1.27 завтра может указывать на другой digest. Цифровая подпись привязывает доверие к неизменяемому sha256.

cosign (проект Sigstore) - современный стандарт. Запрос "cosign" в поиске ведёт сюда. Классическая схема с ключами:

Код: Выделить всё

cosign generate-key-pair
cosign sign --key cosign.key registry.cyberlake.ru/app@sha256:abc123...
cosign verify --key cosign.pub registry.cyberlake.ru/app@sha256:abc123...
Подписывай всегда по digest, а не по тегу - иначе подпись повиснет в воздухе, когда тег переедет. Подпись сохраняется в том же реестре рядом с образом как отдельный OCI-артефакт (тег вида sha256-....sig).

Главный сдвиг 2026 года - keyless-подпись. Приватных ключей нет вообще (а значит, нечего украсть и нечего ротировать). Вместо ключа CI получает короткоживущий OIDC-токен (от GitHub Actions, GitLab CI и т.п.), Sigstore-сервис Fulcio выдаёт под него эфемерный сертификат на пару минут, а факт подписи навсегда фиксируется в публичном прозрачном логе Rekor. Личность подписавшего - это identity из токена, например конкретный workflow:

Код: Выделить всё

COSIGN_EXPERIMENTAL=1 cosign sign registry.cyberlake.ru/app@sha256:abc123...

cosign verify \
  --certificate-identity-regexp "https://github.com/cyberlake/app/.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  registry.cyberlake.ru/app@sha256:abc123...
Это уже не "доверяю владельцу ключа", а "доверяю сборке вот из этого репозитория вот этим пайплайном". Keyless-подпись через OIDC закрывает требования SLSA Build Level 3 по происхождению.

Provenance и attestations. BuildKit/buildx умеет на этапе сборки выпускать SLSA-provenance - подписанную справку "кто, из какого исходника, какой командой собрал":

Код: Выделить всё

docker buildx build \
  --provenance=mode=max \
  --sbom=true \
  --push -t registry.cyberlake.ru/app:1.4.0 .
Флаг --provenance=mode=max кладёт максимум деталей (команды, аргументы, материалы сборки), --sbom=true генерит и прикрепляет SBOM как аттестацию. Всё это ложится в OCI-индекс образа и проверяется через docker buildx imagetools inspect или cosign. SLSA-уровни - это шкала зрелости: L1 - provenance просто есть; L2 - она подписана; L3 - сборка изолирована и происхождение нельзя подделать.

Docker Content Trust (DCT) - предшественник на базе Notary v1. Это legacy: его уже сворачивают (для Docker Official Images - с августа 2025), с 31 мая 2026 DCT нельзя включить на новых реестрах, а полное удаление намечено на 2028. В новых проектах DCT не бери - только cosign/Sigstore или Notation.

Доверенные базовые образы, distroless и pinning по digest

Лучшая дыра - та, которой нет. Меньше пакетов в образе - меньше CVE и меньше поверхность атаки. Отсюда distroless-образы (gcr.io/distroless): только рантайм и приложение, без shell, без apt, без пакетного менеджера. Атакующему, пробившему процесс, не на чем закрепиться - нет даже /bin/sh. Минус - отлаживать тяжелее (есть :debug-варианты с busybox).

Pinning по digest - намертво фиксируй базовый образ не по тегу, а по хешу:

Код: Выделить всё

FROM debian:12.5-slim@sha256:4f7e3...
Тег :12.5-slim мейнтейнер может перезалить под тем же именем - и твоя сборка молча поедет. Digest неизменяем: один и тот же хеш - всегда тот же байт-в-байт слой. Это и воспроизводимость, и защита от тихой подмены базового слоя.

Защита от typosquatting. Атака проста: публикуют пакет reqeusts вместо requests или expressjs вместо express, и одна опечатка в Dockerfile или package.json тащит в образ зловред. Контрмеры: тяни базовые образы только из доверенных пространств имён (официальные, проверенные вендоры), для RU-реалий - зеркала Docker Hub и приватные реестры Yandex Container Registry или VK Cloud Container Registry, куда образы попадают через карантин и скан. Не подключай реестр в Dockerfile по случайному хосту.

Политики в реестре (admission). Финальный рубеж - не пускать неподписанное в прод. Admission-контроллер в Kubernetes (Kyverno, Connaisseur, sigstore-policy-controller) на лету проверяет cosign-подпись каждого образа перед запуском пода: нет валидной подписи от твоего CI - под не стартует. Так все предыдущие шаги превращаются из "хорошо бы" в железное правило, которое нельзя обойти, забыв вручную проверить.

Типичные грабли и антипаттерны
  • Скан как галочка. Сканировать раз в релиз и не отслеживать новые CVE по сохранённому SBOM. Дыры появляются после сборки, образ при этом не меняется.
  • Подпись по тегу вместо digest. Подписал app:latest - подпись отвяжется при следующем пуше. Всегда @sha256:....
  • verify без проверки identity. В keyless-режиме cosign verify без --certificate-identity и --certificate-oidc-issuer примет любую валидную подпись из Rekor, в том числе чужую. Подпись есть - доверие нулевое.
  • Гонять severity вслепую. Фейлить CI на каждом CRITICAL без учёта Status и достижимости - либо тонешь в шуме, либо отключаешь скан вообще.
  • Слепое доверие к самому инструментарию. Сканеры и их базы - тоже звено цепочки (история с компрометацией Trivy). Пинуй версии сканеров, тяни их из доверенных источников.
  • Тег вместо digest в FROM - невоспроизводимые сборки и тихая подмена базового слоя.
Мини-лаба: пройди цепочку руками
  • Собери простой образ и запушь в свой реестр (или локальный registry:2).
  • Прогони trivy image --severity HIGH,CRITICAL и docker scout cves по нему. Сравни находки и разбери поле Status.
  • Сгенерь SBOM: syft <образ> -o cyclonedx-json=app.cdx.json. Затем просканируй уже SBOM: grype sbom:app.cdx.json. Убедись, что результат совпал с прямым сканом, но быстрее.
  • Пересобери через docker buildx build --provenance=mode=max --sbom=true --push и глянь аттестации: docker buildx imagetools inspect <образ>.
  • Подпиши по digest через cosign (ключевой или keyless) и проверь подпись. Намеренно поменяй тег и убедись, что верификация по старому digest всё равно проходит, а по тегу - ловит расхождение.
Контрольные вопросы
  • Почему SBOM нужен отдельно от сканера, если сканер и так видит все пакеты?
  • Чем подпись по digest принципиально надёжнее подписи по тегу?
  • Что именно проверяют --certificate-identity и --certificate-oidc-issuer в keyless-верификации и почему без них cosign verify почти бесполезен?
  • Зачем distroless и pinning по digest, если образ и так прошёл сканирование?
Итог

Безопасность цепочки поставки - это не один инструмент, а конвейер из трёх связанных шагов. Сканируй (trivy, grype, docker scout) - знай, что внутри дырявого. Описывай (SBOM через syft/docker sbom в SPDX или CycloneDX) - чтобы отвечать на завтрашние CVE без пересборки. Подписывай (cosign/Sigstore, keyless через OIDC, buildx provenance) - чтобы доказать происхождение. И замыкай всё политикой в реестре, чтобы неподписанное и дырявое физически не доезжало до прода. Distroless, pinning по digest и доверенные реестры сужают саму поверхность атаки. Тогда следующий громкий CVE для тебя - не паника на сутки, а запрос к SBOM на пару минут.
👍4 ❤️4 🔥 😄 🤔4
Аватара пользователя
RedisWhale
Сообщения: 1
Зарегистрирован: 19 май 2026, 18:55

Re: Supply chain: сканирование, SBOM и подпись образов

Сообщение RedisWhale »

Долго не доходило, зачем SBOM если есть trivy. Дошло на фразе про завтрашний CVE по сохранённой описи без пересборки - реально другой смысл, спасибо.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
skutler
Сообщения: 1
Зарегистрирован: 14 май 2026, 08:21

Re: Supply chain: сканирование, SBOM и подпись образов

Сообщение skutler »

Засада с cosign verify в keyless подтверждаю на своей шкуре: без --certificate-identity он у нас молча принимал левую подпись из Rekor. Добавили regexp на repo - сразу отвалилось лишнее.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Безопасность глубже: rootless, user namespaces, seccomp, capabilities
Следующая глава →
Глубокая оптимизация образов: distroless, scratch, кэш

Все главы курса «Docker: контейнеризация от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: docker no space left on device как очиститьmulti-stage сборка docker как уменьшить образ и почистить кэшкак поднять приватный docker registry push pull и digestharbor реестр образов что это и чем отличается от registry

Вернуться в «Docker с нуля»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость