Почему нужно сканировать образы контейнеров перед развертыванием (и как это делать)

Вы только что собрали новый образ контейнера. Сборка прошла успешно, тесты зеленые, образ уже в реестре. Кажется, что всё готово к выкату в продакшн. Но есть проблема, которую вы не видите: образ может содержать известные уязвимости безопасности, позволяющие злоумышленнику захватить ваш сервер.

Образы контейнеров — не герметичные коробки. Каждый образ собирается из слоев: базовый образ, скачанный из интернета, системные библиотеки, рантаймы языков программирования и ваш собственный код приложения. Каждый слой может нести уязвимости. Базовый образ, который был безопасен на прошлой неделе, может содержать критическую CVE, обнаруженную вчера. Библиотека, добавленная три месяца назад, может иметь недавно выявленный дефект. Эти проблемы не объявляют о себе сами. Их нужно проверять.

Что такое сканирование уязвимостей?

Сканирование уязвимостей — это автоматизированный процесс, который открывает образ контейнера, проверяет каждый пакет и библиотеку внутри и сравнивает их с базой данных известных уязвимостей. Эти уязвимости отслеживаются как CVE (Common Vulnerabilities and Exposures). Каждая CVE имеет уровень серьезности: низкий, средний, высокий или критический.

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

Сканирование формирует отчет: какие пакеты затронуты, насколько серьезна проблема и до какой исправленной версии нужно обновиться.

Почему нельзя отсканировать один раз и забыть

Базы данных уязвимостей обновляются каждый день. Новые CVE публикуются постоянно. Базовый образ, прошедший сканирование в прошлом месяце, сейчас может иметь критическую уязвимость. Библиотека, зафиксированная на определенной версии, может иметь недавно обнаруженную уязвимость, которой не было, когда вы ее выбирали.

Вот почему сканирование должно выполняться при каждой сборке образа. Не только при первой сборке. Не только когда вспомните. Каждая сборка. Сканирование должно быть частью вашего пайплайна, автоматизированным и обязательным.

Где разместить сканирование в пайплайне

Сканирование располагается между этапом сборки и этапом продвижения. Типичный поток выглядит так:

  1. Собрать образ
  2. Запушить образ в реестр
  3. Запустить сканирование уязвимостей
  4. Оценить результаты в соответствии с политикой
  5. Если сканирование пройдено, продвинуть образ в следующее окружение
  6. Если сканирование не пройдено, остановить пайплайн и исправить образ

Сканирование должно выполняться после сборки образа, но до того, как он попадет в staging или production. Таким образом, уязвимые образы никогда не покидают реестр.

Следующая блок-схема визуализирует этот процесс принятия решений:

flowchart TD A[Сборка образа] --> B[Пуш в реестр] B --> C[Запуск сканирования уязвимостей] C --> D{Сканирование пройдено?} D -->|Да| E[Продвижение в следующее окружение] D -->|Нет| F[Блокировка пайплайна] F --> G[Исправление образа] G --> A

Вот практический пример использования Trivy в workflow GitHub Actions, который сканирует образ и останавливает пайплайн при обнаружении любой критической уязвимости:

scan-image:
  runs-on: ubuntu-latest
  steps:
    - name: Pull image from registry
      run: docker pull my-registry/my-app:${{ github.sha }}

    - name: Run Trivy vulnerability scan
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: my-registry/my-app:${{ github.sha }}
        format: table
        exit-code: 1
        severity: CRITICAL

Параметр exit-code: 1 указывает Trivy возвращать ненулевой код возврата при обнаружении уязвимостей, что останавливает пайплайн. Параметр severity: CRITICAL задает порог политики: только критические находки вызывают сбой. Измените severity на CRITICAL,HIGH, если хотите блокировать и те, и другие.

Настройка политики сканирования

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

Ключевой момент — последовательность. Не принимайте решение под каждый деплой. Определите политику один раз, пропишите ее в конфигурации пайплайна и позвольте ей работать автоматически. Если политика блокирует развертывание, это сигнал исправить образ, а не обойти политику.

Инструменты, которые можно использовать

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

  • Trivy — Open source, быстрый, широко используется. Хорошо работает в CI-пайплайнах.
  • Snyk — Коммерческий продукт с бесплатным тарифом. Интегрируется со многими реестрами и CI-системами.
  • Grype — Open source от Anchore. Часто используется в паре с Syft для генерации SBOM.
  • Clair — Open source, изначально от CoreOS. Используется многими сервисами реестров.
  • Встроенные сканеры реестров — Docker Hub, Amazon ECR и Google Artifact Registry предлагают автоматическое сканирование образов, хранящихся в их реестрах.

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

Что делать, когда сканирование не пройдено

Когда пайплайн останавливается из-за уязвимости, не игнорируйте это. Исправление обычно относится к одной из следующих категорий:

Обновить базовый образ. Это самое распространенное исправление. Базовые образы, такие как Alpine, Ubuntu или distroless, регулярно выпускают обновленные версии. Переключитесь на последнюю исправленную версию и пересоберите образ.

Обновить зависимости приложения. Если уязвимость в библиотеке, которую использует ваш код, обновите зависимость в исходном коде и пересоберите образ.

Удалить неиспользуемые инструменты. Иногда уязвимости возникают из-за инструментов, случайно оставленных внутри образа: отладчиков, компиляторов, менеджеров пакетов. Они не нужны во время выполнения. Многоэтапная сборка решает эту проблему, оставляя в production-образе только финальные артефакты времени выполнения.

После исправления пересоберите образ и запустите сканирование снова. Повторяйте, пока образ не пройдет проверку.

Чем сканирование уязвимостей не является

Сканирование не заменяет другие практики безопасности. Оно не покрывает пентест, контроль доступа, сетевую безопасность или мониторинг времени выполнения. Но это дешевый и эффективный уровень защиты, который работает автоматически. Без него критические уязвимости могут попасть в продакшн незамеченными.

Практический чек-лист

  • Добавьте этап сканирования уязвимостей после сборки образа в ваш CI-пайплайн
  • Определите политику сканирования с четкими порогами (например, блокировать на критических и высоких)
  • Настройте пайплайн на остановку при нарушениях политики
  • Используйте многоэтапную сборку для уменьшения поверхности атаки ваших образов
  • Планируйте регулярные обновления базовых образов и зависимостей
  • Периодически просматривайте отчеты сканирования, даже для успешных сборок

Конкретный вывод

Образ контейнера, который не был отсканирован, — это неизвестный риск. Добавьте сканирование уязвимостей в ваш пайплайн сегодня. Выберите инструмент, установите политику и позвольте автоматизации ловить проблемы до того, как они попадут в продакшн. Это займет несколько минут на настройку и убережет вас от обнаружения критической CVE после того, как атакующий уже нашел ее.