CI/CD для ИБ-специалиста: как понимать процессы разработки без умения писать код
Код не становится доверенным в момент слияния веток. Он становится доверенным только тогда, когда пайплайн логически и криптографически подтверждает его безопасность на каждом событии репозитория. seberd.
Для специалиста по информационной безопасности, привыкшего работать с сетевым периметром, правилами межсетевых экранов и оповещениями SIEM, процесс разработки часто выглядит как чёрный ящик. Из него периодически появляются готовые приложения, которые нужно либо подписать, либо проверить сканером, либо закрыть уязвимости в уже работающем сервисе. Но чтобы выстроить реальный контроль, а не просто генерировать отчёты, которые разработка игнорирует, нужно понять физический и логический путь кода.
Современная доставка программного обеспечения — это конвейер с жёсткими точками принуждения. Если безопасник знает, где эти точки находятся, он может встроить проверки так, что небезопасный код физически не сможет попасть на сервера, и при этом не станет узким местом, останавливающим бизнес.
Локальная работа: коммиты, ветки и неизменяемость истории
Стандартом де-факто в разработке является Git — распределённая система контроля версий. Это не просто «общая сетевая папка с файлами», а база данных, хранящая полную историю изменений в виде связного графа.
Репозиторий — это хранилище проекта. У каждого программиста на рабочем ноутбуке находится его полная копия, которая периодически синхронизируется с центральным сервером (GitLab, GitHub, Gitea).
Ветка (Branch) — изолированная линия разработки. Начиная новую задачу, разработчик создаёт ветку. Это позволяет ломать сборку, экспериментировать и писать черновики, не затрагивая стабильную версию кода (обычно она называется main, master или trunk).
Коммит (Commit) — атомарное изменение, сохранённая точка во времени. Каждый коммит содержит срез файлов проекта, метаданные автора и криптографический хэш предыдущего коммита. Эта связь хэшей образует непрерывную цепочку.
Главное свойство Git, которое часто упускают из виду при расследовании инцидентов, — неизменяемость истории. Если разработчик случайно закоммитил файл с паролями от базы данных или приватными SSH-ключами, а затем спохватился и удалил его в следующем коммите, секрет не исчезает. Он остаётся в истории репозитория навсегда. Любой, у кого есть доступ на чтение этого репозитория, может откатиться на предыдущий коммит и извлечь учётные данные.
Единственный способ по-настоящему удалить секрет — переписать историю Git (например, через git filter-repo), принудительно изменив хэши всех последующих коммитов. Это операция с принудительной перезаписью истории, которая ломает локальные копии у всех остальных участников команды. В мире Git не существует корзины, из которой можно безвозвратно стереть ошибку.
Сетевой барьер: push, pull request и точки принуждения
Локальная работа разработчика невидима для системы безопасности, пока он не решит поделиться результатом с командой. Здесь возникают два критических события, которые часто путают: сетевая передача и процедурный запрос.
Push (Пуш) — это сетевая операция отправки локальных коммитов на центральный сервер. Разработчик выполняет команду, и его клиент устанавливает соединение с сервером, передавая пакеты данных. Если на стороне сервера нет преград, любой код, включая вредоносные скрипты и секреты, мгновенно записывается на диск и становится доступным всем, у кого есть права на чтение.
В выстроенных процессах применяется механизм pre-receive hooks (серверных перехватчиков). Это скрипты, которые выполняются на сервере Git в момент получения сетевых пакетов, но до их финальной записи в базу. Типичный пример — Secret Push Protection. Перехватчик анализирует содержимое коммитов прямо в потоке. Если регулярное выражение или энтропийный анализатор находит токен облачного провайдера, сервер разрывает соединение и отклоняет пуш. Разработчик получает ошибку и вынужден чистить историю локально, до того как секрет попал в общий репозиторий.
Pull Request (PR) или Merge Request (MR) — это не сетевая команда, а заявка на слияние в интерфейсе системы контроля версий. Это запрос на слияние (merge) изолированной ветки в основную.
Пока PR открыт, код физически уже лежит в репозитории (в своей ветке), но логически он изолирован от продакшена. Именно на этом этапе настраивается Branch Protection (защита ветки). Политика может гласить: «Прямой push в ветку main запрещён всем, включая администраторов. Слияние возможно только после одобрения двух ревьюеров и успешного прохождения всех автоматических тестов».
Сканер уязвимостей сам по себе ничего не блокирует. Невозможность нажать кнопку «Merge» создаёт правило защиты ветки, которое проверяет статус задач безопасности в конвейере.
Конвейер автоматизации: CI, раннеры и инфраструктурные риски
Когда код попадает в репозиторий (через push или создание PR), в работу вступает CI (Continuous Integration) — система непрерывной интеграции. Это фабрика, которая превращает исходный текст в проверяемый продукт.
Сценарий выполнения описывается в конфигурационном файле (например, .gitlab-ci.yml) и называется пайплайном. Пайплайн состоит из стадий и задач. Но сам пайплайн — это просто инструкция. Физически команды на серверах выполняет раннер (Runner) — агент, который подхватывает задачу и исполняет её в изолированном окружении.
Раннеры обладают высокими привилегиями. Чтобы собрать проект, прогнать интеграционные тесты и отправить собранный образ в реестр, раннеру нужны права доступа к внутренним сетям, базам данных и облачным API. Если злоумышленник или инсайдер может изменить файл пайплайна, он может добавить команду вывода переменных окружения, и раннер отправит корпоративные секреты на внешний сервер. Поэтому в безопасных контурах используют одноразовые (эфемерные) контейнеры для раннеров, которые уничтожаются вместе со всей файловой системой после каждого запуска, а права сервисных аккаунтов CI минимизируются до предела.
Сам пайплайн тоже состоит из кода и подвержен атакам на цепочку поставок (Supply Chain Attack). В современных CI-системах принято подключать готовые экшены или шаблоны из маркетплейсов. Если в конфиге указано uses: popular-action@v1, а владелец этого экшена был скомпрометирован и подменил код внутри тега v1, ваш раннер выполнит вредоносный код с правами вашего CI.
Проблему решает фиксация версии по SHA-хэшу. Вместо ссылки на изменчивый тег используется неизменяемый хэш коммита (@sha256:abcd1234...). Это гарантирует, что пайплайн использует именно ту версию кода, которую проверили архитекторы, даже если автор экшена изменит тег задним числом. Защита исходного кода теряет смысл, если среда, которая его компилирует, уже скомпрометирована.
Анализ кода и зависимостей: SAST, SCA и конфликт с разработкой
В пайплайн встраиваются сканеры безопасности. Чтобы корректно интерпретировать их отчёты и не требовать от разработки невозможного, нужно понимать разницу между ними.
SAST (Static Application Security Testing) анализирует исходный код на предмет уязвимостей в логике (SQL-инъекции, XSS, небезопасная десериализация).
SCA (Software Composition Analysis) анализирует сторонние библиотеки. Разработчики редко пишут всё с нуля; современные приложения на 80% состоят из открытых пакетов. SCA строит дерево зависимостей и сверяет его с базами CVE.
Сканирование секретов ищет захардкоженные пароли, ключи и токены.
Представим типичный случай: безопасник получает отчёт о критической уязвимости в библиотеке логирования. Отчёт есть, а понимания, как эта библиотека попала в прод, кто её добавил и как её теперь заблокировать, чтобы не остановить релиз, — нет. SCA решает эту проблему, показывая не только уязвимый пакет, но и то, какой именно внутренний модуль проекта его подтянул, что позволяет точечно назначить задачу на исправление конкретному разработчику.
С инструментами SAST часто возникает острый конфликт. Сканер находит SQL-инъекцию, которую оставил уволившийся три года назад сотрудник. Пайплайн падает, и команда не может выпустить срочный фикс опечатки в интерфейсе, потому что система безопасности блокирует сборку из-за исторического техдолга. Звучит как идеальная защита? На практике это прямой путь к саботажу. Разработчики начнут требовать обходных путей, а в итоге сканер просто отключат «временно, до рефакторинга».
Компромиссным вариантом становится анализ только изменённого кода (дельты). Пайплайн настраивается так, чтобы блокировать слияние только в том случае, если уязвимость появилась в новых строках кода текущего Pull Request. Исторический техдолг выносится в отдельный бэклог и не тормозит ежедневные релизы. Без этого компромисса разработчики найдут способ отключить сканер, и это «временно» растянется на годы.
Результат сборки: артефакты, реестры и криптографические подписи
Если CI-пайплайн прошел успешно, на выходе получается не исходный код, а артефакт: Docker-образ, JAR-архив или скомпилированный бинарник. Это то, что реально будет запущено на серверах. Артефакты хранятся в хранилищах образов (Registry) — специализированных репозиториях вроде Harbor, Nexus или Yandex Artifact Registry.
Как среда выполнения может быть уверена, что образ, который она скачивает из реестра, действительно собран из проверенного кода, а не подменён инсайдером, получившим доступ к самому хранилищу?
Механизм защиты — криптографическая подпись артефактов. CI-пайплайн собирает образ и подписывает его хэш приватным ключом (используя стандарты вроде Cosign или Notary). Подпись прикрепляется к образу прямо в реестре.
В среде оркестрации, такой как Kubernetes, за проверку отвечает механизм допуска (admission controller) — компонент кластера, который перехватывает запрос на запуск контейнера, находит прикреплённую подпись и сверяет её с публичным ключом. Нет подписи или она невалидна — контроллер отклоняет запрос, и контейнер не запустится, даже если у него технически валидный образ. Подпись не делает код безопасным, но она гарантирует, что в прод попало именно то, что прошло через все фильтры CI.
Попадание в продакшен: CD, среды и стратегии раскатки
CD (Continuous Delivery или Continuous Deployment) — это процесс доставки артефакта на сервера. Delivery подразумевает, что артефакт готов, но для его установки в прод требуется ручная команда. Deployment — это полная автоматизация, где код, прошедший тесты, сам уезжает на боевые сервера.
Даже если все шлюзы CI пройдены, уязвимость нулевого дня или логическая ошибка могут проскочить в прод. Задача CD на этом этапе — минимизировать зону поражения с помощью стратегий раскатки.
Canary Deployment (Стратегия выпуска на малой доле трафика): Новая версия приложения устанавливается только на 5% серверов или открывается для 5% пользователей. Система мониторит ошибки, потребление ресурсов и аномалии в логах. Если замечен взлом или сбой — автоматический откат.
Сине-зелёное развёртывание (Blue-Green): Существуют две идентичные среды (Синяя и Зелёная). Прод работает на Синей. Зелёная обновляется и тестируется под нагрузкой. Переключение происходит мгновенно сменой маршрутизатора трафика, что позволяет сделать откат за секунды, просто вернув запросы на старую среду.
Пайплайн отвечает за доставку и целостность, но не за защиту периметра уже запущенного сервиса, сегментацию сетей внутри кластера или работу WAF.
Словарь интерфейсов: как концепции выглядят в GitLab, GitHub и Jenkins
Чтобы говорить с разработчиками и DevOps-инженерами на одном языке, нужно знать, как абстрактные концепции называются в конкретных инструментах. Сводная таблица ниже показывает концептуальное соответствие, но стоит учитывать, что вендоры постоянно меняют синтаксис и пути в меню настроек.
КонцепцияGitLab CIGitHub ActionsJenkinsСценарий (конфиг).gitlab-ci.yml.github/workflows/*.ymlJenkinsfileИсполнительRunner (Shared / Specific)Runner (GitHub-hosted / Self-hosted)Agent / NodeСекретыCI/CD Variables (Masked)Secrets and VariablesCredentials BindingЗащита веткиProtected BranchesBranch Protection Rules(Зависит от плагинов SCM)Хранилище образовContainer RegistryGitHub Packages (GHCR)(Внешний: Nexus/Harbor)Событие триггераpush, merge_request_eventpush, pull_requestWebhook, Poll SCM
При аудите настроек CI/CD обращайте внимание на использование OIDC (OpenID Connect). Это современный стандарт, позволяющий пайплайну получать временные токены доступа к облачной инфраструктуре напрямую от провайдера идентичности, без хранения долгоживущих статичных ключей (Access Keys) в секретах самого CI. Если в переменных окружения пайплайна до сих пор лежат статичные ключи от облака с правами администратора — это точка критического риска, компрометация которой обнуляет все настройки безопасности самого приложения.



















