Обычно, когда начинается разговор о DevSecOps, все сразу вспоминают о необходимости встраивать в конвейер SAST, DAST, SCA инструменты, проводить различные тесты на безопасность приложения и регулярно сканировать работающее в продуктивной среде решение.
Но при этом не стоит забывать и о необходимости обеспечивать безопасность самого конвейера CI/CD. В сегодняшней статье мы рассмотрим обеспечение безопасности конвейера, построенного на основе Jenkins. Но сначала рассмотрим краткое описание этого решения, что Jenkins из себя представляет и для чего используется.
Что за Jenkins такой?
Jenkins - это один из самых известных инструментов для создания конвейеров автоматизации и интеграции их с остальными инструментами, участвующими в процессе CI/CD. У него активное сообщество, которое предоставило тысячи плагинов для расширения основных функциональных возможностей данного решения, что является основной причиной того, что Jenkins является отраслевым стандартом для создания процессов сборки, тестирования и развертывания.
Jenkins может подключать и автоматизировать процессы SDLC (Software Development Life Cycle) многих разработчиков в их облачной и локальной инфраструктуре, используя множество своих плагинов. Однако без надлежащего контроля Jenkins также может стать слабым местом вашей организации. Помимо очевидных угроз для цепочки поставок, таких как взлом SolarWinds, скомпрометированная система Jenkins может угрожать всей производственной среде и стать отправной точкой для внедрения вредоносного программного обеспечения и утечки конфиденциальных данных.
Обновление как основа
Как бы это не звучало скучно и банально, но безопасность любого приложения начинается с безопасности операционной системы, на которой все развернуто. Сохранение уязвимой версии ОС чрезвычайно рискованно, поскольку злоумышленники могут взломать ваш сервер с помощью общедоступных эксплойтов. Поэтому важно устанавливать все последние обновления для операционной системы. Однако патчингом ОС дело не ограничивается.
Нам необходимо также обновлять наше прикладное ПО и если обновление базовой версии выполняется, как правило, вручную, то обновление плагинов является простым и выполняется несколькими щелчками мыши в пользовательском интерфейсе Jenkins.
Защита операционной системы
Большинство развертываний серверов Jenkins, вероятно, будут работать на базе Linux, поэтому необходимо применять общие рекомендации по обеспечению безопасной настройки Linux для предотвращения проникновения на сервер, такие как:
Прежде всего безопасность на уровне сети. Открывайте только необходимые порты. Настройте фильтрацию пакетов таким образом, чтобы было открыто только обращение к разрешенным портам.
Никаких sudo. Убедитесь, что пользователь Jenkins не может повысить свои права доступа с помощью команды sudo. Вы можете сделать это, изменив файл /etc/sudoers. Запустите Jenkins от имени пользователя, не являющегося администратором. Упростите права доступа к каталогу JENKINS_HOME только для соответствующего пользователя Jenkins.
Аутентификация
Аутентификацию в Jenkins можно обеспечить различными методами. Однако, наилучшая практика обеспечения безопасности Jenkins заключается в том, чтобы не использовать встроенные методы, а вместо этого использовать централизованного стороннего поставщика для аутентификации, такого как GitLab, Github, LDAP, SAML, Google. Используя эти методы, к паролям можно применить определенные правила, такие как усложнение пароля, что помогает предотвратить доступ злоумышленников к серверу.
Но если вы все-таки хотите использовать локальных пользователей Jenkins в качестве временного решения, мы рекомендуем отключить опцию “Разрешить пользователям регистрироваться” и управлять зарегистрированными пользователями вручную. Это позволит контролировать появление пользователей в системе.
Авторизация
Jenkins предоставляет различные встроенные методы авторизации, такие как “Anyone can do anything”, “Legacy mode” or “Logged-in users can do anything”. Мы настоятельно рекомендуем не использовать эти встроенные методы, а использовать плагины для более продвинутых методов авторизации.
Наиболее известными плагинами для этой цели являются Matrix Authorization Strategy и Role-based Authorization Strategy, которые обеспечивают большую гибкость при реализации принципа наименьших привилегий путем определения привилегий пользователей, прошедших проверку подлинности пользователей или конкретных пользователей. Кроме того, вы также можете определить привилегии для каждого проекта или назначить созданные роли для каждого пользователя.
Безопасный Controller
Конвейеры, запущенные на узле Controller, имеют прямой доступ к файловой системе Jenkins, настроенному рабочему пространству, зашифрованным секретам и дополнительным ресурсам. Конечно, в учебных и тестовых конфигурациях мы часто разворачиваем все на одном сервере, однако в продуктивной среде это недопустимо. Важно понимать, что запуск сборок на узле Controller чрезвычайно опасен и должен быть отключен.
Для выполнения соответствующих настроек выберите “Manage Jenkins” → ”Manage Nodes and Clouds” → ”Built-In Node” → ”Configure” → установите “Number of executors” в 0.
Ограничим входящие подключения
Взаимодействие агента с контроллером по умолчанию осуществляется агентами, инициирующими TCP-соединения с контроллером через выделенный порт. Этот метод называется входящим агентом.
Если вы не используете этот метод, то входящий порт на узле контроллера должен быть полностью отключен. Если в вашей среде требуются входящие соединения, важно убедиться, что эти соединения зашифрованы. Jenkins предлагает параметр под названием “Протокол входящего TCP-агента/4 (шифрование TLS)”, который должен быть включен для запуска входящих подключений по защищенному протоколу TLS.
Эти настройки можно найти в разделе “Manage Jenkins” → “Configure Global Security” → ״Agents״.
Также, если плагин SSH-сервера установлен, убедитесь, что SSH отключен, настроив “Manage Jenkins” → “Configure Global Security” → “SSH Server” → “Disable”.
Ограничение прав доступа агента
По умолчанию конвейеры сборки будут запускаться с разрешениями внутреннего пользователя SYSTEM. Это дает сборкам возможность запускать код на любом узле, создавать и удалять задания, запускать и отменять другие сборки и многое другое. Однако, запуск сборок с такими разрешениями может вызвать серьезные проблемы с безопасностью, если, например, Jenkins запускает вредоносные конвейеры сборки с платформы SCM, которые администратор Jenkins не отслеживает. Вот пример журнала сборок с привилегиями по умолчанию, в котором во второй строке указаны разрешения:
Используя плагин Authorize Project, вы можете настроить, какой пользователь и, следовательно, какие разрешения будут запускать сборку. Практическим правилом будет установка наименьших привилегий для каждого проекта. Вот пример того, как журнал сборки после изменения его разрешений в “Manage Jenkins” → “Configure Global Security” → “Access Control for Builds” будет иметь те же права, что и у пользователя, запустившего сборку.
Запуск контейнерных агентов
Существует множество методов и руководств по запуску агентов на различных узлах – физическая машина, виртуальная машина, контейнер, кластер Kubernetes и многое другое. С точки зрения безопасности мы хотим свести к минимуму влияние взломанной сборки на другие сборки или на всю систему в целом. Таким образом, мы предпочитаем, чтобы каждая среда сборки создавалась как новый контейнер с нуля.
Например, можно создать образ контейнера со всеми необходимыми зависимостями и развернув задание в удаленной службе docker. Это гарантирует, что каждая сборка будет выполняться в отдельном чистом контейнере.
Также, можно использовать плагин Kubernetes для динамической подготовки агентов, создавая, таким образом, полноценную временную среду, зависящую исключительно от системы SCM. Ниже представлен пример такой конфигурации в Jenkinsfile:
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
some-label: some-label-value
spec:
containers:
- name: maven
image: maven:alpine
command:
- cat
tty: true
'''
}
}
stages {
stage('Run maven') {
steps {
container('maven') {
sh 'mvn -version'
}
}
}
}
}
Безопасный HTML
При использовании настроек по умолчанию в Jenkins используется рендеринг вводимого текста – это означает, что все описания обрабатываются как текст, а все HTML-теги экранируются для защиты от межсайтового скриптинга. Вы также можете настроить рендеринг HTML с помощью OWASP Markup Formatter, который реализует подмножество HTML без рискованных тегов, таких как <script>. После установки этого плагина вам следует настроить “Manage Jenkins” → “Configure Global Security” → “Markup Formatter” → “Safe HTML”.
Защищенные учетные данные
Приложение Jenkins не предлагает встроенных вариантов для ограничения доступа к учетным данным для конкретных пользователей и сборок, но несколько популярных плагинов делают это в исключительных случаях.
Все добавленные учетные данные в Jenkins определяются либо как “Глобальные” – что делает их доступными для Jenkins, узлов, элементов, всех дочерних элементов, в общем, для всего, либо как “Системные” – что делает их доступными только для Jenkins и узлов. Кроме того, вы можете создавать “Домены” для учетных данных, что помогает организовать доступ к ним.
Создание учетных данных в “Доменах” не дает никаких преимуществ в плане безопасности и оставляет их одинаково уязвимыми – это означает, что любая сборка может получить доступ к этим учетным данным. Например, разработчики, которые модифицируют и фиксируют файл Jenkins на платформе SCM, могут получить доступ к конфиденциальным секретам без доступа к Jenkins. В этой статье демонстрируются некоторые методы, которые злоумышленники могут использовать для использования этой концепции.
Чтобы противостоять этому, мы можем использовать плагин Folders для определения учетных данных для определенных папок, к которым будут доступны только конвейеры из этой папки. Чтобы воспользоваться этой функцией, мы создадим новую папку (через “New Item” → “Folder”) или воспользуемся существующей, перейдем в Credentials” → “Folders” и создадим там наши учетные данные.
Заключение
В этой статье мы рассмотрели основные моменты, связанные с безопасной настройкой Jenkins для обеспечения конвейера CI/CD. Конечно, помимо приведенных в статье советов, необходимо также обеспечить безопасность всей инфраструктуры, участвующей в процессе. Например, безопасность сети и физическую безопасность, но базовые вещи, связанные с безопасностью Jenkins мы рассмотрели в этой статье.
Более продвинутые инструменты и практические кейсы мы рассматриваем в рамках онлайн-курса Внедрение и работа в DevSecOps. По ссылке вы можете зарегистрироваться на бесплатный вебинар курса, чтобы подробнее ознакомиться с форматом обучения и нашими экспертами.