Привет, Хабр! Меня зовут Алексей Федулаев, я руководитель направления Cloud Native Security в МТС Web Services. С этой статьёй мне помогал Андрей Моисеев — DevSecOps из моей команды, и Иван Орлов — Tech Lead из команды Development Platform. Сегодня мы поговорим об основах DevSecOps на примерах GitLab CI/CD. Расскажем о простых методах, которые помогут улучшить безопасность вашего сервиса или продукта.
Как CI/CD работает в облаке
Разработка собственного облачного решения требует участия огромного количества инженеров. Большая кодовая база, множество инженерных команд накладывают определённые ограничения на разработку, в том числе и на обеспечение безопасности разрабатываемых сервисов и их окружения. Поэтому очень важно подходить с умом к организации процессов разработки и особенно важно уделять внимание вопросам безопасности.
В MWS мы используем GitLab, поэтому на нём рассмотрим, какие базовые методы можно применять, чтобы обезопасить ваши продукты, которые могут быть и не связаны с облаками.
Почему важно корректно распределить роли
Представим ситуацию: разработчик пишет код, определённо кучу крутых фич и без ошибок. Затем он вносит изменения в ветку, в которой работал, допустим: main/release/etc. За такими изменениями не всегда получается уследить — разработчики зачастую выполняют несколько задач параллельно. Кто-то мог попросить пофиксить баг, кто-то — добавить контекст, выполнить минорные правки и так далее. Но в некоторых ветках по умолчанию может быть сделан деплой на Demo-stand или PreProd, и, когда обнаруживается проблема, она затрагивает всех — девопсов, тестировщиков, другие команды разработки, а это бывает больно и дорого.
Чтобы избежать ситуаций с непроверенным кодом в важных ветках, нужно правильно настроить права доступа и запретить прямой пуш в дефолтную ветку. Первый шаг — ревизия настроек в GitLab, а именно проверка существующих ролей, их соответствия реальным полномочиям сотрудников. Например, хорошим базовым подходом будет убрать все привилегии и выдать их заново, понимая, какой сотрудник и куда у него есть доступ. Это автоматически запрещает прямой пуш в main-ветку.
Давайте отдельно рассмотрим ситуацию с релизной веткой — тут цена ошибки сильно выше, потому что изменения могут пойти сразу на прод. Разработчик, столкнувшись с проблемами на сервере, может попытаться исправить ситуацию через git reset hard с последующим пушем. Это может привести к серьёзным последствиям, вплоть до падения продакшена, неработоспособности приложения и другим неприятным вещам. На практике любая проблема с GitLab становится проблемой девопса и он вынужден сталкиваться с огромным количеством однотипных обращений, а если девопс-культура не развита — это станет monkey’job.
Решение включает запрет force push и ограничение работы с определёнными ветками для конкретных групп пользователей. Например, ветка release MVP доступна только мейнтейнерам, с явным запретом force push. Защита может распространяться на несколько веток: main, pre-release, release, release MVP. При этом некоторые ветки, например pre-release, могут быть доступны как девелоперам, так и мейнтейнерам, но с отключённым force push.
Дополнительный механизм контроля — работа с защищёнными тегами. Комбинация protected-веток и тегов позволяет сделать ваш релизный флоу идемпотентным. Но при некорректной настройке вышеперечисленных механизмов существуют способы обхода правил. Например, разработчик может создать новую ветку, внести изменения, создать merge request и самостоятельно его подтвердить. Или дождаться, когда кто-то по невнимательности или в спешке внесёт изменения. Реальный пример: сотрудник указал себя тимлидом в профиле, хотя был обычным разработчиком. Он аппрувил собственные коммиты, а другие сотрудники, видя аппрув от предполагаемого тимлида, не проверяли изменения тщательно.
Как контролировать доступ к коду с помощью GitLab
У GitLab есть настройки — Approval Settings. Если у вас версия дороже, чем Community Edition, их можно кастомизировать. Эти настройки позволяют настроить права на подтверждение конкретных merge requests. Например, позволяют убрать возможность самому аппрувнуть свой MR.
В Community Edition GitLab в актуальной версии уже внесены настройки, запрещающие по умолчанию аппрувить свои изменения. Это работает даже в случаях, когда разработчик добавляет коммит в чужую ветку — он всё равно не сможет влить в неё изменения без аппрува.
Ещёв GitLab существует система политик безопасности (Security Policies). Эти политики имеют простой интерфейс настройки, похожий на конструктор, что делает их удобными в использовании. Главное преимущество политик безопасности в том, что они имеют приоритет над правилами, установленными на уровне отдельных репозиториев. Это позволяет централизованно управлять правилами безопасности для множества репозиториев с помощью одной политики, что значительно упрощает администрирование и поддержание единых стандартов безопасности.
Рассмотрим ситуацию, когда система контроля кода не позволяет просто так внести изменения и есть ответственный человек, проверяющий код. Возникает проблема, когда этот проверяющий может не знать какие-то детали разработки. Например, он может легко заметить очевидную проблему безопасности, такую как хранение ключей доступа непосредственно в коде репозитория. Но если речь идёт о специфической области, например фронтенд-разработке, где у проверяющего недостаточно компетенций, он может не распознать потенциальные проблемы или риски в предлагаемом коде.
Для решения этой проблемы нужно настроить правила в системе контроля качества кода (Lint). Эти правила автоматически активируются при создании каждого запроса на слияние (Merge Request). Хотя процесс проверки может быть утомительным и занимать много времени, это необходимая мера обеспечения качества и безопасности кода.
В качестве примера можно рассмотреть настройку правил Lint для различных веток — отдельно для бэкенда и фронтенда. При такой конфигурации запрос на слияние не может быть одобрен без проверки и подтверждения от специалистов соответствующего направления. Например, изменения во фронтенде должны быть одобрены фронтенд-разработчиками, а изменения в бэкенде — бэкенд-разработчиками. Эта функциональность доступна в том числе и в Community Edition GitLab.
Пользователь с расширенными привилегиями может легко обойти важные механизмы защиты. Например, отключить обязательное прохождение pipeline, проигнорировать failing-тесты или разрешить force push. При этом разработчик может руководствоваться предыдущими результатами локальных тестов, проверкой своей фичи на локальном стенде или высоким приоритетом выполняемой задачи (business).
Особую опасность представляет небрежное обращение с токенами доступа. При наличии требуемых полномочий любой пользователь может создать токен с максимальными привилегиями и поделиться им с коллегами через незащищённые каналы связи. При компрометации такого токена злоумышленники могут получить полный контроль над репозиторием.
GitLab поддерживает ролевое управление доступом, так, например, роль Owner должна быть только у одного ответственного лица — обычно это тимлид или DevOps-лид, который отвечает за репозиторий. Роль Maintainer подходит для опытных разработчиков и DevOps-инженеров, которые помогают с настройкой и поддержкой CI. Большинство участников команды должны работать с правами Developer — это обеспечивает достаточную функциональность для разработки, но ограничивает доступ к критичным настройкам.
При понижении прав с Maintainer до Developer пользователь теряет доступ к настройкам репозитория, что помогает предотвратить случайные или намеренные нарушения процессов безопасности. Такое чёткое разграничение ролей и ответственности является важным элементом безопасной разработки.
Часто встречается ситуация неправильного распределения прав доступа к репозиториям. Типичный пример, когда в проекте множество пользователей с максимальными правами — Owners и Maintainers, но практически нет обычных разработчиков. Такой подход создаёт серьёзные риски безопасности.
Как не допустить утечки секретов
Проблема утечки секретов — одна из самых простых, но в то же время самых критичных угроз безопасности в разработке. Утечка может произойти как случайно, так и намеренно, при этом разобраться в её характере довольно проблематично. Все мы люди и допускаем ошибки, например, при разработке можно случайно запушить свои токены доступа или ключи от тестового окружения, иногда для упрощения тестирования можно вообще хардкодить базовые секреты в конфигурационных файлах. И чаще всего потом уже очень сложно вспомнить — куда какой секрет добавил.
Последствия утечки секретов могут быть серьёзными: злоумышленники могут получить доступ к токенам доступа и пройти авторизацию по ним — с правами токена — или получить ключи ssh и развить атаку, например за счёт горизонтального продвижения по сети. Это техника, при которой нарушитель использует начальный доступ для поиска любых данных, которые может использовать для дальнейшей атаки — например, получив доступ к тестовой машине, он может проверить ssh-ключи, которые лежат там, и попробовать их использовать для доступа к инфраструктуре. Особенно опасна ситуация, когда разработчики намеренно оставляют креды и токены в коде «для удобства» или «по привычке».
GitLab предлагает встроенное решение для борьбы с утечкой секретов — Secret Detection. Этот инструмент использует сканер GitLeaks, который анализирует историю коммитов и файлы в репозитории на предмет наличия секретов по своим паттернам. При подключении этой джобы к CI/CD-pipeline она автоматически проверяет код и блокирует merge request при обнаружении потенциальных секретов.
Для эффективной защиты секретов важно заранее продумать процесс их хранения и ротации. Рекомендуется использовать специализированные хранилища секретов (Secret Management Systems) вместо хранения их в коде или конфигурационных файлах. Также необходимо обеспечить безопасное обращение с секретами, исключив возможность их случайной передачи или вывода в логи. При этом недостаточно использовать только поиск секретов, следует заранее определиться с процессом Secret Management. Подготовить хранилище секретов, процедуры ротации, отзыва и предоставления доступов.
Токены доступа требуют особого внимания к их жизненному циклу. Даже после удаления пользователем токен остаётся валидным на сервере, поэтому нужно корректно выполнять отзыв доступов с удалением токенов или ключей, в том числе на серверах.
В качестве примера утечки секретов можно привести ситуацию, когда компания X случайно добавила .git в слой публичного контейнера. При этом даже если удалить ключ из репозитория, сделать force push — его всё равно можно извлечь из истории git или, если образ был скачан, — из его слоёв. Чтобы гарантированно удалить секреты из истории гита и не наступить на те же грабли — можно использовать инструмент BFG. Подробнее об этом инструменте в Github по ссылке.
Отдельная проблема — работа с секретами в GitLab Variables. Существуют разные типы переменных — обычные, protected и masked. Protected-переменные доступны только в protected-ветках и билдах, однако их можно обойти, например конвертировав значение в Base64. Dependency confusion — атака основана на том, что пакетный менеджер может подтянуть вредоносную версию пакета из публичного registry вместо локального. Чтобы защититься от этого, используйте локальный registry с проксированием запросов. Используйте защищённые ветки, раннеры, переменные и теги для критичных операций. Некритичные операции могут выполняться на обычных раннерах.
GitLab предлагает альтернативные способы хранения секретов через систему переменных. При создании переменной доступны два основных типа защиты:
Masked Variables: определяют информацию внутри секрета как набор символов, которые не допускается выводить в логи сборки. Тем самым предотвращают случайную утечку. Masked Variables можно обойти через вывод значения в base64 encode — в таком случае GitLab не сможет понять, что выводится чувствительная информация, и отобразит строку в логах — дальше просто можно выполнить base64-decode и получить секрет в открытом виде.
Protected Variables: доступны только в защищённых ветках и сборках, обеспечивая дополнительный уровень безопасности.
Секреты, определённые как protected, доступны только в protected-ветках репозитория — также дополнительно стоит использовать отдельные protected-раннеры, которые могут запускаться только в рамках пайплайнов protected-веток.
Такой уровень сегментации позволяет отделить Dev-секреты, утечка которых может быть не особо важна, от Prod-секретов, которые используются в релизных версиях продуктов.
Но даже приведённых методов может быть недостаточно — если у вас есть возможность, рекомендуем применять системы хранения секретов, например HashiCorp Vault. Эти системы позволяют настраивать детальные политики доступа на основе различных параметров: ветки разработки, идентификатора проекта и сервисных аккаунтов. Такой подход значительно снижает риски, связанные с утечкой секретов, и обеспечивает централизованное управление доступом.
При внедрении инструментов безопасности они неизбежно повлияют на скорость сборки продукта, скорость деплоя на продакшен. При этом почти всегда есть возможность исключения файлов из сканирования (для снижения false-positive), и эта возможность может быть использована для отключения проверок безопасности. Так, в случае с gitleaks он предоставляет возможность игнорирования файндингов (найденных проблем) через указание их в файле .gitleaksignore — аналогичном флоу с .gitignore.
Чтобы контролировать процесс и не допускать таких ситуаций, нужно:
Выстраивать правильные процессы code review и merge approve с привлечением ответственных за флоу тестирования, флоу безопасности, чтобы каждый юнит проверял корректность своего порядка действий.
Реализовывать сканеры таким образом, чтобы без наличия определённых прав нельзя было повлиять на флоу их работы. Например, запретить внесение изменений в файл .gitleaksignore всем с ролью Developer — это можно сделать с помощью механизма CodeOwners.
Важно понимать: правильная организация работы с секретами — не вопрос внедрения пары сканеров или регламентов, а важная часть культуры безопасной разработки, которая требует внимания всей команды.
Как мы это внедрили в разработку облачной платформы MWS
В процессе начальной разработки облака и развития сопутствующих процессов фокус смещается на реализацию функциональных возможностей, построение рабочего решения, разработку интеграций для сервисов. В разработке принимают участие сотни инженеров, десятки команд, в процессе обрастания функционалом создаётся огромное количество репозиториев с кодом, тестовых репозиториев для проверки гипотез, самописных пайплайнов для автоматизации. Становится всё труднее сосуществовать, а администрирование и исправление проблем CI/CD ложится на разработчиков, потому что невозможно нанять столько DevOps-инженеров.
Мы в MWS решили унифицировать разработку с использованием сервисов Development Platform (некоторые используют аббревиатуру IDP), интегрировав часть решений и подходов, описанных выше в статье, в нашу Development Platform.
Для контроля за безопасными настройками GitLab в рамках Development Platform мы разработали сервис-помощник Star Maker. Он работает как единая информационная точка входа по всем сервисам и многое знает про устройство облака: как связаны сервисы, из каких компонентов состоят, какой состав команд, роли разработчиков в проекте, кто когда дежурит, где смотреть дашборды.
Цель Star Maker применительно к GitLab:
— настроить все проекты GitLab в соответствии с требованиями безопасности и compliance;
— минимизировать изменения настроек проектов и групп проектов командами разработки;
— сообщить о внесённых изменениях команде безопасности.
Например, с помощью Star Maker мы делаем ревью кода до попадания в master. Нам важно, чтобы код, попадающий в production, прошёл ревью от группы людей, прошедших обучение по compliance, — это тоже роль, в каждой команде такие люди есть, но это не любой Developer/Maintainer. Благодаря Star Maker мы можем помогать командам настраивать Approval rules и CodeOwners в своих проектах, чтобы approve кода от этих пользователей был обязательным. Также эти люди контролируют изменение конфигурации приложений в production.
Новые проекты и группы проектов мы умеем создавать через Star Maker API. Так можно гарантировать, что созданные проекты соответствуют принципам, рассмотренным в статье.
Для существующих проектов Star Maker выполняет непрерывное сканирование на предмет отклонения от рекомендуемых настроек — в случае расхождения сервис создаст Jira-тикет с предлагаемыми изменениями и, если команда согласна на изменение, приведёт настройки проектов к рекомендуемым.
Помимо Star Maker, в Development Platform интегрировано решение типа Vault, для безопасного хранения секретов. Ещё в платформу интегрированы разные инструменты DevSecOps. Например, инструмент gitleaks, который нам позволяет выявлять наличие секретов в коде на ранней стадии.
В следующих статьях мы расскажем подробно про сервисы Dev Platfrom в MWS. А пока можно посмотреть доклад Сергея Киселёва — руководителя направления Development Platform MWS на DevOops.
Простые правила безопасности
Чтобы существенно повысить безопасность проекта, нужно соблюдать простые правила «гигиены»:
1. Используйте сетевую изоляцию для ваших окружений DEV TEST PROD.
2. Применяйте методы изоляции GitLab — protected branch/runner/tag/variable.
3. Выстраивайте процесс code review.
4. Настройте CodeOwners и отключите self-approve.
6. Настройте правила аппрувов для подразделений, чей код был затронут коммитом.
7. Используйте RBAC и мониторьте изменения прав.
8. Используйте базовые проверки безопасности: secret management, sast, dast, sca, container security, IAC sast.
9. Обеспечьте неизменный флоу проверок безопасности.
10. Храните все зависимости в локальном registry и запретите на раннерах доступ в интернет.
В этом материале рассказали о базовых принципах безопасности CI/CD. В будущих статьях рассмотрим продвинутые атаки на CI/CD и методы защиты от них — не переключайтесь!
karabanov
В Community Edition GitLab аппрувы не доступны - кнопка там есть но она ни ни что не влияет, даже если не проставить аппрувы мерж можно будет выполнить всё равно. Настоящие аппрувы доступны начиная с Premium
Или вы нашли какой-то способ включить аппрувы в Community Edition GitLab?