Что на самом деле может проверять ваш пайплайн (помимо сканирования безопасности)

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

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

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

Сканирование зависимостей

Почти ни одно приложение сегодня не пишется с нуля. Ваш проект подтягивает библиотеки из npm, PyPI, Go modules, NuGet или Maven. Каждая из этих зависимостей — это код, написанный кем-то другим, и в этом коде могут быть публично известные уязвимости.

Сканирование зависимостей сверяет ваш список зависимостей с базами уязвимостей. Оно сообщает, есть ли у ваших библиотек известные проблемы безопасности в той версии, которую вы используете. Речь не о поиске zero-day. Речь о том, чтобы отловить уже задокументированные уязвимости, для которых часто уже есть патчи.

Запускайте это сканирование при каждом изменении файла зависимостей — package.json, go.mod, requirements.txt или любого другого, принятого в вашей экосистеме. Как минимум, запускайте его один раз во время сборки. Если запускать только на pull request, можно пропустить уязвимость, попавшую в код через слияние обновления зависимостей напрямую в основную ветку.

Вот минимальный YAML-фрагмент для задачи GitHub Actions, которая запускает npm audit и останавливает пайплайн, если найдена уязвимость высокого уровня серьезности:

name: dependency-scan
on:
  pull_request:
    paths:
      - 'package.json'
      - 'package-lock.json'

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm audit --audit-level=high

Сканирование образов контейнеров

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

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

Правильное время для запуска этого сканирования — сразу после сборки образа, до того как он будет отправлен в реестр или развернут где-либо. Как только образ попал в продакшн, его сканирование все еще полезно для видимости, но ущерб уже возможен.

Сканирование инфраструктуры как кода

Когда вы пишете инфраструктуру как код с помощью Terraform, CloudFormation или Pulumi, вы определяете облачные ресурсы в текстовых файлах. Эти файлы могут содержать неверные конфигурации, открывающие дыры в безопасности: публично доступные бакеты для хранения, базы данных, доступные из интернета, отключенное шифрование или излишне разрешающие IAM-роли.

Сканирование IaC проверяет ваши определения инфраструктуры на соответствие лучшим практикам безопасности до того, как вы их примените. Это не замена инструментам управления безопасностью облачных сред, которые сканируют работающие окружения. Это превентивная проверка, которая ловит проблемы до того, как они станут развернутыми ресурсами.

Запускайте это сканирование, когда кто-то открывает pull request, изменяющий код инфраструктуры. Это тот момент, когда вы еще можете исправить конфигурацию, не разбираясь с неправильно настроенным ресурсом, который уже работает.

Сканирование секретов

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

Сканирование секретов обнаруживает шаблоны, похожие на учетные данные: строки, соответствующие формату ключей AWS, токенов GitHub, вебхуков Slack или обычных паролей. Оно помечает их до того, как коммит попадет в основную ветку.

Это сканирование наиболее эффективно, если запускать его на каждый коммит или хотя бы на каждый pull request. Чем раньше вы поймаете утекший секрет, тем меньше будет воздействие. Если сканировать только после слияния кода, секрет уже будет в репозитории, и любой, у кого есть доступ, мог его получить.

Сканирование лицензий

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

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

Политика как код

Политика как код — это не один тип сканирования. Это способ кодировать правила и обеспечивать их выполнение программно в вашем пайплайне. Вместо того чтобы полагаться на готовый сканер, вы определяете собственные проверки: «каждое изменение базы данных должно быть проверено администратором БД», «каждый образ контейнера должен поступать из одобренного реестра», «каждое развертывание в продакшн должно иметь успешный нагрузочный тест».

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

Выбор того, что запускать и когда

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

Вот практическая отправная точка:

  • Каждый коммит: сканирование секретов, сканирование зависимостей (если изменился файл зависимостей)
  • Pull request: сканирование IaC, сканирование лицензий, сканирование образов контейнеров (если образ был пересобран)
  • Перед развертыванием: сканирование образов контейнеров (если еще не сделано), проверки политики как код

Краткий чек-лист для вашего пайплайна

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

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

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