Поговорим про DevSecOps. Jenkins with Bandit
В прошлой статье мы разобрали инструмент Bandit. Если вкратце, этот инструмент сканирует исходный код. Мы применили его на практике, но в нестандартной для DevSecOps форме, запустив его в обычной ОС. Но на практике подобные инструменты встраиваются в CI/CD пайплайн.
CI/CD пайплайн — набор практик и методов, которые позволяют автоматизировать процесс разработки. То бишь, мы просто вносим изменения в код, а этот код автоматически собирается, тестируется, встает на облачные сервера ну и т. д.
В этой статье мы интегрируем запуск Bandit для нашего кода на GitHub в Jenkins.
Jenkins — инструмент для автоматизации процесса разработки, который поддерживает практики CI/CD
Содержание:
- Устанавливаем Jenkins
- Начало работы с Jenkins
- Настройка Bandit
- Настройка агента Jenkins
- Собираем проект через агента
Устанавливаем Jenkins
Итак, все это безобразие будет сделано с помощью Docker.
Процесс установки Docker я оставлю за кадром, но приложу ссылку на иструкцию.
Итак, сначала напишем Dockerfile.
Dockerfile — это текстовый файл, содержащий набор инструкций, которые Docker использует для автоматической сборки образа контейнера.
FROM jenkins/jenkins:lts # Устанавливаем необходимые пакеты и Bandit USER root RUN apt-get update RUN apt-get upgrade -y RUN apt-get install virtualenv -y RUN virtualenv bandit-env RUN . bandit-env/bin/activate RUN apt-get install python3 -y RUN apt-get install python3-pip -y RUN apt install python3-bandit -y RUN apt-get install ssh -y USER jenkins
Сначала запустим Bandit в том же контейнере, что и Jenkins. А потом сделаем все по уму и добавим агента Jenkins, который сделает все в отдельном контейнере.
Итак, сформировали Dockerfile. Теперь сформируем Docker-compose.yml
Docker-compose.yml — файл конфигурации для многоконтейнерных приложений
version: '3.8' services: jenkins: build: . ports: - "8080:8080" - "50000:50000" volumes: - jenkins_home:/var/jenkins_home networks: - jenkins volumes: jenkins_home: networks: jenkins:
- version — версия файла Docker-compose
- services — контейнеры нашего приложения
- build — пусть к Dockerfile данного сервиса
- volumes — томы. Иными словами: постоянная память для контейнеров, которая не будет стираться после каждого перезапуска
- networks — сети для контейнеров, которые позволяют изолировать трафик между контейнерами
Запустим наше приложение командой Docker-compose up -d. Флаг -d означает работу в фоновом режиме. Таким образом мы не будем видеть логи контейнера в терминале.
Наша рабочая директория должна выглядеть так:
|-workspace |--docker-compose.yml |--Dockerfile
Когда пропишем команду, наш терминал будет выглядеть следующим образом (Название может отличаться):
Надписи CACHED означают, что изображение, по которому строится контейнер уже имеется в памяти ПК, поэтому и время построение очень маленькое. У Вас может быть больше.
Итак, что мы видим в приложении Docker Desktop:
Workspace — многоконтейнерное приложение. Имя этого приложения подтягивается от нашей рабочей директории.
Jenkins-1 — наш контейнер.
Перейдем по двум портам, которые у нас открыты. Порт 5000 нас пока что не интересует, поэтому перейдем по 8080.
Нам нужно ввести пароль, который лежит внутри контейнера.
Кликнув по имени контейнера мы можем посмотреть его логи:
c7e456fbad25460190ff7d2ec416734d — наш пароль. Введем его.
Теперь нас спрашивают, какие плагины мы хотим установить. Устанавливаем те, которые сообщество Jenkins использует больше всего.
После установки нас просят создать Админа
Потом предлагают поменять ссылку, по которой доступен Jenkins. Мы же не будем ничего менять
Наш Jenkins готов к использованию!
Начало работы с Jenkins
Главная страница Jenkins выглядит так:
Мы выберем свободную конфигурацию и имя BanditWithJenkins.
Настройка нашей джобы выглядет так:
Для нашей статьи хватит флажка GitHub project.
Теперь вставим ссылку на репозиторий GitHub из прошлой статьи:
Теперь внизу страницы добавим шаг сборки, а именно выполнение команды Shell. Для проверки работоспособности введем обычный Hello, world! И сохраним изменения.
Теперь попробуем собрать наш проект
Во вкладке Build у нас появилась сборка и она успешна
Заглянем внутрь данной сборки:
Здесь мы видим краткую информацию, такую как: время выполнения, создателя и т. д.
Но нас интересует вывод консоли
Результат нас устраивает. Теперь перейдем к Bandit.
Настройка Bandit
Нам нужно добавить новые команды в наш проект:
После запуска Jenkins проведет сканирование нашего кода, создаст уникальный текстовый файл и выведет его содержимое в консоль.
Bandit запустился, но ничего не просканировал. Потому что мы забыли добавить управление исходным кодом. Исправим это в настройках проекта. Репозиторий открытый, так что данные (Credentials) для доступа к нему можно не настраивать.
Ошибка сборки, давайте посмотрим, что случилось.
Первое выделение говорит нам о том, что мы правильно поменяли настройки и теперь наш проект клонирует репозиторий GitHub. А вот второе выделение по началу непонятно, но сейчас все объясню.
Когда Bandit находит угрозу, он возвращает false. Jenkins воспринимает false как ошибку, поэтому, делаем следующее:
Меняем команду, чтобы текущий шаг всегда возвращал true.
Сохранив изменения и запустив сборку видим следующее (#5 потому что 4-ую из-за человеческого фактора пришлось удалить, не обращайте внимания):
Все наши файлы доступны внутри контейнера по пути var/jenkins_home/workspace/BanditWithJenkins. (Метка Volume подтверждает, что все мы настроили правильно и при перезапуске контейнера эти файлы не пропадут)
Настройка агента Jenkins
Теперь настало время сделать все по уму. Небезопасно выполнять тесты на машине, где установлен Jenkins. Поэтому создадим Jenkins agent.
Итак, изменим наш Docker-compose файл:
version: '3.8' services: jenkins: build: . ports: - "8080:8080" - "50000:50000" volumes: - jenkins_home:/var/jenkins_home networks: - jenkins agent: build: /for_ubuntu ports: - "2673:2673" volumes: - agent_home:/var/agent_home networks: - jenkins volumes: jenkins_home: agent_home: networks: jenkins:
В папке for_ubuntu создадим еще один Dockerfile:
FROM ubuntu:latest # Устанавливаем необходимые пакеты и Bandit RUN apt-get update RUN apt-get upgrade -y RUN apt-get install virtualenv -y RUN virtualenv bandit-env RUN . bandit-env/bin/activate RUN apt-get install python3 -y RUN apt-get install python3-pip -y RUN apt install python3-bandit -y RUN apt-get install ssh -y RUN apt-get install curl -y RUN apt-get install -y openjdk-17-jdk CMD ["sleep", "infinity"]
Теперь наша рабочая директория должна выглядеть так:
|-workspace |--for_ubuntu |--Dockerfile |--docker-compose.yml |--Dockerfile
Команда Docker-compose up -d запустит наше приложение. В этот раз никаких команд не было кэшировано, отсюда и долгий запуск (703 секунды).
Итак, зайдя на http://localhost:8080 нас встречает окно логина в Jenkins. Что подтверждает правильность наших настроек, ведь если бы у Jenkins были удаленны все данные, то регистрация началась бы заново.
Jenkins Agent — компонент системы Jenkins на другой машине. Он будет выполнять команды, которые дал ему наш основной сервис.
Кстати, даже сам Jenkins говорит нам настроить агента:
Мы пойдем по длинному пути через кнопку «Настроить Jenkins».
Итак, остановимся подробнее на этом скриншоте:
Нас просят указать удаленные корневую директорию. Мы укажем наш Volume, который прописывали в Docker-compose «/var/agent_home»
В способе запуска выбираем «Запустить агент, подключив его к контроллеру», затем сохраняем изменения.
Нас перенаправит на страницу настройки нод\узлов. Нажимаем на наш недавно созданный агент.
И нам сразу же дают команды, которые мы должны выполнить внутри Docker контейнера:
Важное уточнение:
Нам нужен IP сервера. И именно на этот IP мы меняем localhost в командах, которые нам дали.
Пример:
Была команда curl -sO http://localhost:8080/jnlpJars/agent.jar
Стала curl -sO http://172.18.0.3:8080/jnlpJars/agent.jar
IP контейнера можно посмотреть через команду docker container inspect <container_name_or_id>
Заходим в Docker и выполняем эти команды.
Если не менять localhost, ответ следующий:
Также пропал крестик на иконке нашего агента
Собираем проект через агента
Все что осталось, так это перейти в настройки нашей джобы, поставить вот такой флажок:
А в его поле выбрать нашего агента:
Дабы доказать, что сканирование проводил агент, мы найдем файл с результатами именно в нем. И да, файл находится в томе нашего агента:
Что же, в этой статье мы поверхностно затронули Jenkins, научились настраивать и коннектить агентов внутри многоконтейнерного приложения и запускать джобы на агентах.
Критику, желательно конструктивную, жду в комментариях.
В следующих статьях, как и обещал, продолжим разговаривать про инструменты DevSecOps.
Всем добра!