HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <ul><ul><li><a>Postmortem.</a></li>
1 <ul><ul><li><a>Postmortem.</a></li>
2 <li><a>План работы по внедрению этапов DevSecOps.</a></li>
2 <li><a>План работы по внедрению этапов DevSecOps.</a></li>
3 </ul><li><a>1. Анализ состояния вопросов безопасности в проекте.</a><ul><li><a>Определение технологического стека приложения.</a></li>
3 </ul><li><a>1. Анализ состояния вопросов безопасности в проекте.</a><ul><li><a>Определение технологического стека приложения.</a></li>
4 <li><a>Предварительная оценка текущего цикла сборки и тестирования приложения.</a></li>
4 <li><a>Предварительная оценка текущего цикла сборки и тестирования приложения.</a></li>
5 </ul></li>
5 </ul></li>
6 <li><a>2. Подготовка и внедрение в конвейер этапов безопасной разработки.</a><ul><li><a>Планирование внедряемых этапов.</a></li>
6 <li><a>2. Подготовка и внедрение в конвейер этапов безопасной разработки.</a><ul><li><a>Планирование внедряемых этапов.</a></li>
7 <li><a>Выбор инструментов</a></li>
7 <li><a>Выбор инструментов</a></li>
8 <li><a>Реализация выбранных решений и подготовка конвейера.</a></li>
8 <li><a>Реализация выбранных решений и подготовка конвейера.</a></li>
9 </ul></li>
9 </ul></li>
10 <li><a>Оценка результатов работы.</a></li>
10 <li><a>Оценка результатов работы.</a></li>
11 <li><a>4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.</a></li>
11 <li><a>4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.</a></li>
12 <li><a>Итоги.</a></li>
12 <li><a>Итоги.</a></li>
13 </ul><p><em>В конце курса "<a>Внедрение и работа в DevSecOps</a>" студентов ждёт выполнение проекта. Это самостоятельная работа, необходимая для закрепления полученных знаний. Предлагаем вашему вниманию проект <strong><a>Сергея Кушнерчука</a></strong>, одного из выпускников курса.</em></p>
13 </ul><p><em>В конце курса "<a>Внедрение и работа в DevSecOps</a>" студентов ждёт выполнение проекта. Это самостоятельная работа, необходимая для закрепления полученных знаний. Предлагаем вашему вниманию проект <strong><a>Сергея Кушнерчука</a></strong>, одного из выпускников курса.</em></p>
14 <p>Данный проект является одним из микросервисов реальной системы, реализующей оказание юридических услуг, поэтому это практическая, а не теоретическая работа.</p>
14 <p>Данный проект является одним из микросервисов реальной системы, реализующей оказание юридических услуг, поэтому это практическая, а не теоретическая работа.</p>
15 <h5>Postmortem.</h5>
15 <h5>Postmortem.</h5>
16 <p>Некоторое время назад произошел ряд инцидентов, связанных с информационной безопасностью, что привело к финансовым и репутационным потерям компании.</p>
16 <p>Некоторое время назад произошел ряд инцидентов, связанных с информационной безопасностью, что привело к финансовым и репутационным потерям компании.</p>
17 <p>Было принято решение внедрить этапы безопасной разработки в весь жизненный цикл приложения, начав с разработки, тестирования и сборки приложения.</p>
17 <p>Было принято решение внедрить этапы безопасной разработки в весь жизненный цикл приложения, начав с разработки, тестирования и сборки приложения.</p>
18 <h5>План работы по внедрению этапов DevSecOps.</h5>
18 <h5>План работы по внедрению этапов DevSecOps.</h5>
19 <ol><li>Анализ состояния вопросов безопасности в проекте.</li>
19 <ol><li>Анализ состояния вопросов безопасности в проекте.</li>
20 <li>определение технологического стека приложения;</li>
20 <li>определение технологического стека приложения;</li>
21 <li>предварительная оценка состояния кодовой базы;</li>
21 <li>предварительная оценка состояния кодовой базы;</li>
22 <li>предварительная оценка текущего цикла сборки и тестирования приложения.</li>
22 <li>предварительная оценка текущего цикла сборки и тестирования приложения.</li>
23 <li>Подготовка и внедрение в конвейер этапов безопасной разработки (первая итерация)</li>
23 <li>Подготовка и внедрение в конвейер этапов безопасной разработки (первая итерация)</li>
24 <li>определение этапов, которые будут внедрены;</li>
24 <li>определение этапов, которые будут внедрены;</li>
25 <li>выбор инструментов;</li>
25 <li>выбор инструментов;</li>
26 <li>реализация выбранных решений и подготовка нового конвейера.</li>
26 <li>реализация выбранных решений и подготовка нового конвейера.</li>
27 <li>Оценка результатов работы</li>
27 <li>Оценка результатов работы</li>
28 <li>Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложений.</li>
28 <li>Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложений.</li>
29 </ol><h4>1. Анализ состояния вопросов безопасности в проекте.</h4>
29 </ol><h4>1. Анализ состояния вопросов безопасности в проекте.</h4>
30 <h5>Определение технологического стека приложения.</h5>
30 <h5>Определение технологического стека приложения.</h5>
31 <p>Технологический стек, используемый в работе приложения:</p>
31 <p>Технологический стек, используемый в работе приложения:</p>
32 <ul><li>Python 3.10</li>
32 <ul><li>Python 3.10</li>
33 <li>Docker</li>
33 <li>Docker</li>
34 <li>MariaDB</li>
34 <li>MariaDB</li>
35 <li>MongoDB</li>
35 <li>MongoDB</li>
36 <li>FluentD</li>
36 <li>FluentD</li>
37 <li>RabbitMQ</li>
37 <li>RabbitMQ</li>
38 <li>MinIO</li>
38 <li>MinIO</li>
39 <li>Debian 10</li>
39 <li>Debian 10</li>
40 </ul><p>Все операции по разработке, сборке и тестированию на момент написания работы выполнялись в<strong>GitLab EE 16.2.4 Ultimate</strong>.</p>
40 </ul><p>Все операции по разработке, сборке и тестированию на момент написания работы выполнялись в<strong>GitLab EE 16.2.4 Ultimate</strong>.</p>
41 <p>На момент принятия проекта в работу какие-либо меры безопасной разработки в проекте отсутствовали. По результатам первичного анализа (без использования средств автоматизации) был обнаружен ряд грубых нарушений, таких как:</p>
41 <p>На момент принятия проекта в работу какие-либо меры безопасной разработки в проекте отсутствовали. По результатам первичного анализа (без использования средств автоматизации) был обнаружен ряд грубых нарушений, таких как:</p>
42 <p><strong>Наличие в исходном коде чувствительных данных и необоснованное использование повышенных привилегий:</strong></p>
42 <p><strong>Наличие в исходном коде чувствительных данных и необоснованное использование повышенных привилегий:</strong></p>
43 <p><strong>Запуск образа приложения от пользователя root</strong>:</p>
43 <p><strong>Запуск образа приложения от пользователя root</strong>:</p>
44 FROM python:3.10-slim RUN apt-get update &amp;&amp; \ apt-get install -y --no-install-recommends curl build-essential gcc libssl-dev default-libmysqlclient-dev \ libc-dev libxml2-dev libxslt-dev wkhtmltopdf libmagic1 &amp;&amp; \ curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python &amp;&amp; \ cd /usr/local/bin &amp;&amp; \ ln -s /opt/poetry/bin/poetry &amp;&amp; \ poetry config virtualenvs.create false &amp;&amp; \ apt-get remove curl -y &amp;&amp; \ apt-get clean -y WORKDIR /app COPY ./app /app COPY ./docker-entrypoint.sh /app/ COPY ./wait-for-db.sh /app/ COPY pyproject.toml poetry.lock* /app/ ARG INSTALL_DEV=false RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi" ENV PYTHONPATH=/ ENTRYPOINT ["/app/docker-entrypoint.sh"]<p><strong>Отсутствие проверок линтеров и каких-либо сканеров безопасности в цикле сборки образа приложения.</strong></p>
44 FROM python:3.10-slim RUN apt-get update &amp;&amp; \ apt-get install -y --no-install-recommends curl build-essential gcc libssl-dev default-libmysqlclient-dev \ libc-dev libxml2-dev libxslt-dev wkhtmltopdf libmagic1 &amp;&amp; \ curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python &amp;&amp; \ cd /usr/local/bin &amp;&amp; \ ln -s /opt/poetry/bin/poetry &amp;&amp; \ poetry config virtualenvs.create false &amp;&amp; \ apt-get remove curl -y &amp;&amp; \ apt-get clean -y WORKDIR /app COPY ./app /app COPY ./docker-entrypoint.sh /app/ COPY ./wait-for-db.sh /app/ COPY pyproject.toml poetry.lock* /app/ ARG INSTALL_DEV=false RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi" ENV PYTHONPATH=/ ENTRYPOINT ["/app/docker-entrypoint.sh"]<p><strong>Отсутствие проверок линтеров и каких-либо сканеров безопасности в цикле сборки образа приложения.</strong></p>
45 <p><strong>Различие docker-образов python, используемых для тестирования и сборки приложения:</strong></p>
45 <p><strong>Различие docker-образов python, используемых для тестирования и сборки приложения:</strong></p>
46 <ul><li>тестирование - python:3.8.5</li>
46 <ul><li>тестирование - python:3.8.5</li>
47 <li>сборка итогового образа: python:3.10-slim</li>
47 <li>сборка итогового образа: python:3.10-slim</li>
48 </ul><h5>Предварительная оценка текущего цикла сборки и тестирования приложения.</h5>
48 </ul><h5>Предварительная оценка текущего цикла сборки и тестирования приложения.</h5>
49 <p>Существующий конвейер - простой и прямолинейный, какие-либо меры безопасности отсутствуют полностью:</p>
49 <p>Существующий конвейер - простой и прямолинейный, какие-либо меры безопасности отсутствуют полностью:</p>
50 stages:<strong>-</strong>test<strong>-</strong>build # Значения переменных ниже убраны намеренно variables: MYSQL_ROOT_PASSWORD: MYSQL_USER: DB_USER: DB_PASSWORD: DB_HOST: DB_PORT: BASE_URL: AUTHJWT_SECRET_KEY: EMAILS_FROM_EMAIL: EMAIL_ANALYZE: STAGE_SERVER_IP: STAGE_SERVER_USER: Run tests: tags:<strong>-</strong>python stage: test image: python:3.8.5 services:<strong>-</strong>name: mariadb:10.3 command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ] script:<strong>-</strong>curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python<strong>-</strong>ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry<strong>-</strong>poetry config virtualenvs.create false<strong>-</strong>poetry install --no-root<strong>-</strong>rm -f .dev.env only:<strong>-</strong>branches<strong>-</strong>tags when: manual Build stage image: stage: build tags:<strong>-</strong>python script:<strong>-</strong>docker build -t "$REGISTRY_IMAGE:latest" .<strong>-</strong>sleep 2<strong>-</strong>docker push "$REGISTRY_IMAGE:latest" only:<strong>-</strong>development when: manual Build production image: stage: build tags:<strong>-</strong>python rules:<strong>-</strong>if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+$/ when: manual<strong>-</strong>if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+\-patch$/ when: manual script:<strong>-</strong>DATE=`date +%d-%m-%Y`<strong>-</strong>TAG=$DATE_$CI_COMMIT_TAG<strong>-</strong>docker build --pull -t "$REGISTRY_IMAGE:$TAG" .<strong>-</strong>docker push "$REGISTRY_IMAGE:$TAG" before_script:<strong>-</strong>set -xe<strong>-</strong>docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" $REGISTRY after_script:<strong>-</strong>docker logout $REGISTRY<p>Мы начнем подготовку нового конвейера с чистого листа, сначала внедрив некоторые наши этапы.</p>
50 stages:<strong>-</strong>test<strong>-</strong>build # Значения переменных ниже убраны намеренно variables: MYSQL_ROOT_PASSWORD: MYSQL_USER: DB_USER: DB_PASSWORD: DB_HOST: DB_PORT: BASE_URL: AUTHJWT_SECRET_KEY: EMAILS_FROM_EMAIL: EMAIL_ANALYZE: STAGE_SERVER_IP: STAGE_SERVER_USER: Run tests: tags:<strong>-</strong>python stage: test image: python:3.8.5 services:<strong>-</strong>name: mariadb:10.3 command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ] script:<strong>-</strong>curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python<strong>-</strong>ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry<strong>-</strong>poetry config virtualenvs.create false<strong>-</strong>poetry install --no-root<strong>-</strong>rm -f .dev.env only:<strong>-</strong>branches<strong>-</strong>tags when: manual Build stage image: stage: build tags:<strong>-</strong>python script:<strong>-</strong>docker build -t "$REGISTRY_IMAGE:latest" .<strong>-</strong>sleep 2<strong>-</strong>docker push "$REGISTRY_IMAGE:latest" only:<strong>-</strong>development when: manual Build production image: stage: build tags:<strong>-</strong>python rules:<strong>-</strong>if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+$/ when: manual<strong>-</strong>if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+\-patch$/ when: manual script:<strong>-</strong>DATE=`date +%d-%m-%Y`<strong>-</strong>TAG=$DATE_$CI_COMMIT_TAG<strong>-</strong>docker build --pull -t "$REGISTRY_IMAGE:$TAG" .<strong>-</strong>docker push "$REGISTRY_IMAGE:$TAG" before_script:<strong>-</strong>set -xe<strong>-</strong>docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" $REGISTRY after_script:<strong>-</strong>docker logout $REGISTRY<p>Мы начнем подготовку нового конвейера с чистого листа, сначала внедрив некоторые наши этапы.</p>
51 <h4>2. Подготовка и внедрение в конвейер этапов безопасной разработки.</h4>
51 <h4>2. Подготовка и внедрение в конвейер этапов безопасной разработки.</h4>
52 <h5>Планирование внедряемых этапов.</h5>
52 <h5>Планирование внедряемых этапов.</h5>
53 <p>Для реализации поставленных целей на начальном этапе мы планируем реализовать:</p>
53 <p>Для реализации поставленных целей на начальном этапе мы планируем реализовать:</p>
54 <ol><li>Настройку проекта средствами GitLab.</li>
54 <ol><li>Настройку проекта средствами GitLab.</li>
55 <li>Определение наличия чувствительных данных в кодовой базе.</li>
55 <li>Определение наличия чувствительных данных в кодовой базе.</li>
56 <li>Проверку зависимостей.</li>
56 <li>Проверку зависимостей.</li>
57 <li>SAST</li>
57 <li>SAST</li>
58 <li>Проверку исходного кода линтерами.</li>
58 <li>Проверку исходного кода линтерами.</li>
59 <li>Сканирование итоговых образов на наличие уязвимостей.</li>
59 <li>Сканирование итоговых образов на наличие уязвимостей.</li>
60 <li>Защиту итогового конвейера от изменений.</li>
60 <li>Защиту итогового конвейера от изменений.</li>
61 </ol><h5>Выбор инструментов</h5>
61 </ol><h5>Выбор инструментов</h5>
62 <p>Мы будем использовать на первой итерации только штатные средства, которые предоставляет нам GitLab - его арсенал достаточно обширен и покрывает все наши потребности на данном этапе. Однако, если вы не являетесь владельцем ultimate-версии GitLab, то вам придется искать другие варианты решения спланированных задач, а также будут недоступны штатные инструменты, формирующие отчеты о безопасности.</p>
62 <p>Мы будем использовать на первой итерации только штатные средства, которые предоставляет нам GitLab - его арсенал достаточно обширен и покрывает все наши потребности на данном этапе. Однако, если вы не являетесь владельцем ultimate-версии GitLab, то вам придется искать другие варианты решения спланированных задач, а также будут недоступны штатные инструменты, формирующие отчеты о безопасности.</p>
63 <h5>Реализация выбранных решений и подготовка конвейера.</h5>
63 <h5>Реализация выбранных решений и подготовка конвейера.</h5>
64 <p><strong>Настройка проекта.</strong></p>
64 <p><strong>Настройка проекта.</strong></p>
65 <p><strong>Push rules:</strong>Settings -&gt; Repository -&gt; Push rules</p>
65 <p><strong>Push rules:</strong>Settings -&gt; Repository -&gt; Push rules</p>
66 <p>Выставляем нужные настройки, как указано на изображении ниже:</p>
66 <p>Выставляем нужные настройки, как указано на изображении ниже:</p>
67 <p>Основным флажком, который точно должен быть установлен, является Prevent pushing secret files, остальные настройки выставляются в соответствии с регламентом разработки.</p>
67 <p>Основным флажком, который точно должен быть установлен, является Prevent pushing secret files, остальные настройки выставляются в соответствии с регламентом разработки.</p>
68 <p><strong>Защищенная ветка:</strong>Settings -&gt; Repository -&gt; Protected branches</p>
68 <p><strong>Защищенная ветка:</strong>Settings -&gt; Repository -&gt; Protected branches</p>
69 <p>Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:</p>
69 <p>Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:</p>
70 <p><strong>Merge requests:</strong>Settings -&gt; Merge requests -&gt; Merge requests</p>
70 <p><strong>Merge requests:</strong>Settings -&gt; Merge requests -&gt; Merge requests</p>
71 <p>Для предотвращения слияния реквестов, которые еще не разрешены, или имеют не успешные конвейеры, необходимо выставить параметры:</p>
71 <p>Для предотвращения слияния реквестов, которые еще не разрешены, или имеют не успешные конвейеры, необходимо выставить параметры:</p>
72 <p><strong>Определение наличия чувствительных данных в проекте.</strong></p>
72 <p><strong>Определение наличия чувствительных данных в проекте.</strong></p>
73 <p>Задача поиска утечек паролей, токенов, сертификатов и других чувствительных данных в коде проекта задача не совсем простая - высока вероятность как пропуска реальных данных, так и ложноположительных срабатываний, поэтому этот этап не должен являться блокирующим для выпуска приложения. Он должен быть включен в регламент code review и являться основанием как для выдачи замечаний, так и чистки истории от попавших в репозиторий данных.</p>
73 <p>Задача поиска утечек паролей, токенов, сертификатов и других чувствительных данных в коде проекта задача не совсем простая - высока вероятность как пропуска реальных данных, так и ложноположительных срабатываний, поэтому этот этап не должен являться блокирующим для выпуска приложения. Он должен быть включен в регламент code review и являться основанием как для выдачи замечаний, так и чистки истории от попавших в репозиторий данных.</p>
74 <p>Существует несколько утилит для решения такой задачи, но мы пока ограничимся штатным средством, которое предоставляет GitLab, так как не хотим потерять возможности удобного встроенного средства анализа безопасности - Security dashboard и Vulnerability report, тем более что при необходимости мы можем добавить свои правила для поиска чувствительных данных - под капотом для решения этой задачи GitLab использует<a>gitleaks</a></p>
74 <p>Существует несколько утилит для решения такой задачи, но мы пока ограничимся штатным средством, которое предоставляет GitLab, так как не хотим потерять возможности удобного встроенного средства анализа безопасности - Security dashboard и Vulnerability report, тем более что при необходимости мы можем добавить свои правила для поиска чувствительных данных - под капотом для решения этой задачи GitLab использует<a>gitleaks</a></p>
75 <p>Добавим этот этап:</p>
75 <p>Добавим этот этап:</p>
76 variables: SECURE_LOG_LEVEL: 'debug' SECRET_DETECTION_HISTORIC_SCAN: 'true' SEARCH_MAX_DEPTH: 1000 include:<strong>-</strong>template: Security/Secret-Detection.gitlab-ci.yml stages:<strong>-</strong>security secret_detection: stage: security artifacts: reports: sast: gl-secret-detection-report.json<p>После запуска конвейера видим, что помимо найденного ранее, обнаружено еще три места, где возможно есть чувствительные данные:</p>
76 variables: SECURE_LOG_LEVEL: 'debug' SECRET_DETECTION_HISTORIC_SCAN: 'true' SEARCH_MAX_DEPTH: 1000 include:<strong>-</strong>template: Security/Secret-Detection.gitlab-ci.yml stages:<strong>-</strong>security secret_detection: stage: security artifacts: reports: sast: gl-secret-detection-report.json<p>После запуска конвейера видим, что помимо найденного ранее, обнаружено еще три места, где возможно есть чувствительные данные:</p>
77 <p>Однако это ложные срабатывания, которые мы можем пометить как *#gitleaks:allow, чтобы эти строки в будущем не учитывались при анализе:</p>
77 <p>Однако это ложные срабатывания, которые мы можем пометить как *#gitleaks:allow, чтобы эти строки в будущем не учитывались при анализе:</p>
78 def get_url(): user = os.getenv("DB_USER", "") password = os.getenv("DB_PASSWORD", "") server = os.getenv("DB_HOST", "") db = os.getenv("DB_NAME", "") print(f"mysql://{user}:{password}@{server}/{db}?charset=utf8mb4") #<em>*#gitleaks:allow</em><em>return f"mysql://{user}:{password}@{server}/{db}?charset=utf8mb4" # *</em>#gitleaks:allow<p><strong>Проверка зависимостей.</strong></p>
78 def get_url(): user = os.getenv("DB_USER", "") password = os.getenv("DB_PASSWORD", "") server = os.getenv("DB_HOST", "") db = os.getenv("DB_NAME", "") print(f"mysql://{user}:{password}@{server}/{db}?charset=utf8mb4") #<em>*#gitleaks:allow</em><em>return f"mysql://{user}:{password}@{server}/{db}?charset=utf8mb4" # *</em>#gitleaks:allow<p><strong>Проверка зависимостей.</strong></p>
79 <p>Добавим проверку используемых зависимостей на уязвимости (эта же задача сформирует нам отчет о лицензиях используемых зависимостей):</p>
79 <p>Добавим проверку используемых зависимостей на уязвимости (эта же задача сформирует нам отчет о лицензиях используемых зависимостей):</p>
80 variables: LICENSE_MANAGEMENT_VERSION: 4 include:<strong>-</strong>template: Security/Dependency-Scanning.gitlab-ci.yml<p>В результате работы конвейера получаем список из 21 уязвимости:</p>
80 variables: LICENSE_MANAGEMENT_VERSION: 4 include:<strong>-</strong>template: Security/Dependency-Scanning.gitlab-ci.yml<p>В результате работы конвейера получаем список из 21 уязвимости:</p>
81 <p><strong>SAST</strong></p>
81 <p><strong>SAST</strong></p>
82 <p>Добавим этап SAST:</p>
82 <p>Добавим этап SAST:</p>
83 variables: SAST_EXPERIMENTAL_FEATURES: "true" include:<strong>-</strong>template: Security/SAST.gitlab-ci.yml<p>После запуска конвейера мы получим 104 найденные проблемы SAST:</p>
83 variables: SAST_EXPERIMENTAL_FEATURES: "true" include:<strong>-</strong>template: Security/SAST.gitlab-ci.yml<p>После запуска конвейера мы получим 104 найденные проблемы SAST:</p>
84 <p><strong>Проверка исходного кода линтерами.</strong></p>
84 <p><strong>Проверка исходного кода линтерами.</strong></p>
85 <p><strong>flake8, mypy</strong></p>
85 <p><strong>flake8, mypy</strong></p>
86 <p>Эти линтеры уже были добавлены в проект ранее как зависимости, в проекте есть их конфигурационные файлы, однако в конвейерах не использовались. Сделаем так, чтобы пока эти этапы не влияли на конечный результат сборки, однако, в случае наличия проблем, это было визуально видно:</p>
86 <p>Эти линтеры уже были добавлены в проект ранее как зависимости, в проекте есть их конфигурационные файлы, однако в конвейерах не использовались. Сделаем так, чтобы пока эти этапы не влияли на конечный результат сборки, однако, в случае наличия проблем, это было визуально видно:</p>
87 stages:<strong>-</strong>linters flake8: stage: linters script: | pip3 install flake8 &amp;&amp; flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy &amp;&amp; pip3 install sqlalchemy-stubs &amp;&amp; mypy . allow_failure: true<p><strong>Сканирование итоговых образов на наличие уязвимостей.</strong></p>
87 stages:<strong>-</strong>linters flake8: stage: linters script: | pip3 install flake8 &amp;&amp; flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy &amp;&amp; pip3 install sqlalchemy-stubs &amp;&amp; mypy . allow_failure: true<p><strong>Сканирование итоговых образов на наличие уязвимостей.</strong></p>
88 <p>Для реализации этапа сначала вернем в конвейер базовую сборку:</p>
88 <p>Для реализации этапа сначала вернем в конвейер базовую сборку:</p>
89 build: needs:<strong>-</strong>tests stage: build script: | docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY docker build -t $DEST_DOCKER_IMAGE . docker push $DEST_DOCKER_IMAGE docker logout $CI_REGISTRY only:<strong>-</strong>branches<p>Для проверки образов также будем использовать уже встроенную возможность GitLab (под капотом там<a>trivy</a>с некоторой оберткой)</p>
89 build: needs:<strong>-</strong>tests stage: build script: | docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY docker build -t $DEST_DOCKER_IMAGE . docker push $DEST_DOCKER_IMAGE docker logout $CI_REGISTRY only:<strong>-</strong>branches<p>Для проверки образов также будем использовать уже встроенную возможность GitLab (под капотом там<a>trivy</a>с некоторой оберткой)</p>
90 include:<strong>-</strong>template: Security/Container-Scanning.gitlab-ci.yml variables: CS_IMAGE: $DEST_DOCKER_IMAGE CS_REGISTRY_USER: $CI_REGISTRY_USER CS_REGISTRY_PASSWORD: $CI_REGISTRY_PASSWORD CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN: "true" stages:<strong>-</strong>post_security container_scanning: stage: post_security needs:<strong>-</strong>build<p>Для такой проверки мы сделаем отдельный этап post_security, который будет запускаться после сборки приложения:</p>
90 include:<strong>-</strong>template: Security/Container-Scanning.gitlab-ci.yml variables: CS_IMAGE: $DEST_DOCKER_IMAGE CS_REGISTRY_USER: $CI_REGISTRY_USER CS_REGISTRY_PASSWORD: $CI_REGISTRY_PASSWORD CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN: "true" stages:<strong>-</strong>post_security container_scanning: stage: post_security needs:<strong>-</strong>build<p>Для такой проверки мы сделаем отдельный этап post_security, который будет запускаться после сборки приложения:</p>
91 <p>Результат работы этого этапа представлен ниже:</p>
91 <p>Результат работы этого этапа представлен ниже:</p>
92 <p>Итак, конвейер, содержащий необходимые базовые операции по проверкам безопасности, а также тестированию и базовой сборке образа приложения выглядит так:</p>
92 <p>Итак, конвейер, содержащий необходимые базовые операции по проверкам безопасности, а также тестированию и базовой сборке образа приложения выглядит так:</p>
93 variables: BUILD_DOCKER_IMAGE: python:3.10-slim DEST_DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_PIPELINE_ID SECURE_LOG_LEVEL: 'debug' SECRET_DETECTION_HISTORIC_SCAN: "true" SEARCH_MAX_DEPTH: 1000 SAST_EXPERIMENTAL_FEATURES: "true" CS_IMAGE: $DEST_DOCKER_IMAGE CS_REGISTRY_USER: $CI_REGISTRY_USER CS_REGISTRY_PASSWORD: $CI_REGISTRY_PASSWORD CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN: "true" LICENSE_MANAGEMENT_VERSION: 4 include:<strong>-</strong>template: Security/Secret-Detection.gitlab-ci.yml<strong>-</strong>template: Security/Dependency-Scanning.gitlab-ci.yml<strong>-</strong>template: Security/SAST.gitlab-ci.yml<strong>-</strong>template: Security/Container-Scanning.gitlab-ci.yml stages:<strong>-</strong>security<strong>-</strong>linters<strong>-</strong>test<strong>-</strong>build<strong>-</strong>post_security secret_detection: stage: security artifacts: reports: sast: gl-secret-detection-report.json dependency_scanning: stage: security artifacts: reports: sast: gl-dependency-scanning-report.json sast: stage: security flake8: image: $BUILDER_DOCKER_IMAGE stage: linters script: | pip3 install flake8 &amp;&amp; flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy &amp;&amp; pip3 install sqlalchemy-stubs &amp;&amp; mypy . allow_failure: true tests: stage: test services:<strong>-</strong>name: mariadb:10.3 command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ] script:<strong>-</strong>pip3 install poetry<strong>-</strong>poetry config virtualenvs.create false<strong>-</strong>poetry install<strong>-</strong>python -m pytest --cov only:<strong>-</strong>branches build: needs:<strong>-</strong>tests stage: build script: | docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY docker build -t $DEST_DOCKER_IMAGE . docker push $DEST_DOCKER_IMAGE docker logout $CI_REGISTRY only:<strong>-</strong>branches container_scanning: stage: post_security needs:<strong>-</strong>build<p>В ходе работы были приняты несколько дополнительных решений:</p>
93 variables: BUILD_DOCKER_IMAGE: python:3.10-slim DEST_DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_PIPELINE_ID SECURE_LOG_LEVEL: 'debug' SECRET_DETECTION_HISTORIC_SCAN: "true" SEARCH_MAX_DEPTH: 1000 SAST_EXPERIMENTAL_FEATURES: "true" CS_IMAGE: $DEST_DOCKER_IMAGE CS_REGISTRY_USER: $CI_REGISTRY_USER CS_REGISTRY_PASSWORD: $CI_REGISTRY_PASSWORD CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN: "true" LICENSE_MANAGEMENT_VERSION: 4 include:<strong>-</strong>template: Security/Secret-Detection.gitlab-ci.yml<strong>-</strong>template: Security/Dependency-Scanning.gitlab-ci.yml<strong>-</strong>template: Security/SAST.gitlab-ci.yml<strong>-</strong>template: Security/Container-Scanning.gitlab-ci.yml stages:<strong>-</strong>security<strong>-</strong>linters<strong>-</strong>test<strong>-</strong>build<strong>-</strong>post_security secret_detection: stage: security artifacts: reports: sast: gl-secret-detection-report.json dependency_scanning: stage: security artifacts: reports: sast: gl-dependency-scanning-report.json sast: stage: security flake8: image: $BUILDER_DOCKER_IMAGE stage: linters script: | pip3 install flake8 &amp;&amp; flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy &amp;&amp; pip3 install sqlalchemy-stubs &amp;&amp; mypy . allow_failure: true tests: stage: test services:<strong>-</strong>name: mariadb:10.3 command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ] script:<strong>-</strong>pip3 install poetry<strong>-</strong>poetry config virtualenvs.create false<strong>-</strong>poetry install<strong>-</strong>python -m pytest --cov only:<strong>-</strong>branches build: needs:<strong>-</strong>tests stage: build script: | docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY docker build -t $DEST_DOCKER_IMAGE . docker push $DEST_DOCKER_IMAGE docker logout $CI_REGISTRY only:<strong>-</strong>branches container_scanning: stage: post_security needs:<strong>-</strong>build<p>В ходе работы были приняты несколько дополнительных решений:</p>
94 <ol><li>Сделать так, чтобы все эти этапы могли быть переиспользованы в других аналогичных сервисах с минимальными изменениями, или вовсе без них.</li>
94 <ol><li>Сделать так, чтобы все эти этапы могли быть переиспользованы в других аналогичных сервисах с минимальными изменениями, или вовсе без них.</li>
95 <li>Максимально освободить разработчиков от погружения в вопросы DevSecOps в процессе жизненного цикла приложения.</li>
95 <li>Максимально освободить разработчиков от погружения в вопросы DevSecOps в процессе жизненного цикла приложения.</li>
96 <li>Защитить конвейер от случайных или злонамеренных изменений, внесенных разработчиками, при этом оставив возможность простого управления им.</li>
96 <li>Защитить конвейер от случайных или злонамеренных изменений, внесенных разработчиками, при этом оставив возможность простого управления им.</li>
97 </ol><p>Для внедрения принятых решений мы будем использовать возможность GitLab выполнять включение конвейеров из других репозиториев. Для этого мы перенесем его в другой репозиторий той же группы GitLab, а в нашем проекте выполним его включение:</p>
97 </ol><p>Для внедрения принятых решений мы будем использовать возможность GitLab выполнять включение конвейеров из других репозиториев. Для этого мы перенесем его в другой репозиторий той же группы GitLab, а в нашем проекте выполним его включение:</p>
98 include:<strong>-</strong>project: 'services/ci' ref: master file: 'python-ci.yml'<p>Таким образом мы решаем первую и вторую поставленные задачи - разработчикам больше нет необходимости думать о настройке CI/CD - им достаточно взять уже готовый файл для размещения в уже существующих проектах, а для новых проектов можно подготовить шаблон, который уже будет содержать все необходимое.</p>
98 include:<strong>-</strong>project: 'services/ci' ref: master file: 'python-ci.yml'<p>Таким образом мы решаем первую и вторую поставленные задачи - разработчикам больше нет необходимости думать о настройке CI/CD - им достаточно взять уже готовый файл для размещения в уже существующих проектах, а для новых проектов можно подготовить шаблон, который уже будет содержать все необходимое.</p>
99 <p>Теперь мы можем выполнить слияние наших изменений в продуктовую ветку, и приступить к решению задачи по защите конвейера от изменений.</p>
99 <p>Теперь мы можем выполнить слияние наших изменений в продуктовую ветку, и приступить к решению задачи по защите конвейера от изменений.</p>
100 <p><strong>Защита итогового конвейера от изменений</strong></p>
100 <p><strong>Защита итогового конвейера от изменений</strong></p>
101 <p>На период внедрения и тестирования этапов DevSecOps было принято решение о запрете вмешательства разработчиков в подготовку и работу конвейера. Технически наиболее просто достичь этого можно двумя способами:</p>
101 <p>На период внедрения и тестирования этапов DevSecOps было принято решение о запрете вмешательства разработчиков в подготовку и работу конвейера. Технически наиболее просто достичь этого можно двумя способами:</p>
102 <ol><li>Используя настройку Settings -&gt; CI/CD -&gt; General pipelines, в которой указать путь к файлу конвейера, например:</li>
102 <ol><li>Используя настройку Settings -&gt; CI/CD -&gt; General pipelines, в которой указать путь к файлу конвейера, например:</li>
103 </ol>python-ci.yml@services/ci:master<ol><li>Установив блокировку на файл конвейера в проекте пользователем с максимальным уровнем привилегий (на весь этап работы это член команды DevSecOps) - это блокирует его во всех ветках проекта:</li>
103 </ol>python-ci.yml@services/ci:master<ol><li>Установив блокировку на файл конвейера в проекте пользователем с максимальным уровнем привилегий (на весь этап работы это член команды DevSecOps) - это блокирует его во всех ветках проекта:</li>
104 </ol><p>Первый вариант нам не подходит, так как мы хотим в будущем сделать вариативный конвейер, управление жизненным циклом которого будет осуществляться с помощью переменных, размещаемых в его файле, поэтому мы будем использовать блокировку файла конвейера, которая в будущем будет снята.</p>
104 </ol><p>Первый вариант нам не подходит, так как мы хотим в будущем сделать вариативный конвейер, управление жизненным циклом которого будет осуществляться с помощью переменных, размещаемых в его файле, поэтому мы будем использовать блокировку файла конвейера, которая в будущем будет снята.</p>
105 <h4>Оценка результатов работы.</h4>
105 <h4>Оценка результатов работы.</h4>
106 <p>Итогом первой итерации стал конвейер, который показывает, насколько сильно подвержено рискам безопасности приложение.</p>
106 <p>Итогом первой итерации стал конвейер, который показывает, насколько сильно подвержено рискам безопасности приложение.</p>
107 <p>Также было принято решение отложить выпуск последующих релизов до устранения критических проблем, поэтому этап полной сборки и деплоя продуктового образа пока в конвейере не реализован. Сроки восстановления релизов на данном этапе не ясны - и это хорошо показывает, как халатное отношение к вопросам безопасности может навредить бизнесу.</p>
107 <p>Также было принято решение отложить выпуск последующих релизов до устранения критических проблем, поэтому этап полной сборки и деплоя продуктового образа пока в конвейере не реализован. Сроки восстановления релизов на данном этапе не ясны - и это хорошо показывает, как халатное отношение к вопросам безопасности может навредить бизнесу.</p>
108 <h4>4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.</h4>
108 <h4>4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.</h4>
109 <p>Для устранения всех выявленных и недопущения новых недостатков мы можем спланировать следующие этапы нашей работы:</p>
109 <p>Для устранения всех выявленных и недопущения новых недостатков мы можем спланировать следующие этапы нашей работы:</p>
110 <ol><li>Разработка и согласование плана для формирования пула задач, которые будут решаться командами разработки и DevSecOps приоритетно.</li>
110 <ol><li>Разработка и согласование плана для формирования пула задач, которые будут решаться командами разработки и DevSecOps приоритетно.</li>
111 <li>Интеграция и использование разработчиками необходимых инструментов безопасности в их локальном окружении.</li>
111 <li>Интеграция и использование разработчиками необходимых инструментов безопасности в их локальном окружении.</li>
112 <li>Более тонкая настройка инструментов (или их замена на более совершенные) для исключения ложных и пропуска настоящих срабатываний, а также внедрение нового инструментария для решения других возникших задач.</li>
112 <li>Более тонкая настройка инструментов (или их замена на более совершенные) для исключения ложных и пропуска настоящих срабатываний, а также внедрение нового инструментария для решения других возникших задач.</li>
113 <li>Разработка скриптов автоматизации (при необходимости).</li>
113 <li>Разработка скриптов автоматизации (при необходимости).</li>
114 <li>Определение и применение блок-факторов безопасности для сборки или выпуска релизов приложения (на данном этапе это критические проблемы для выпуска релизов, тестовые сборки будут продолжаться чтобы не блокировать работу команд разработки полностью).</li>
114 <li>Определение и применение блок-факторов безопасности для сборки или выпуска релизов приложения (на данном этапе это критические проблемы для выпуска релизов, тестовые сборки будут продолжаться чтобы не блокировать работу команд разработки полностью).</li>
115 <li>Внедрение автоматизированных проверок работающих docker-контейнеров на тестовых и продуктовых контурах на наличие открытых портов, правил монтирования файловых систем, установки ограничений используемых ресурсов и привилегий и т.п.</li>
115 <li>Внедрение автоматизированных проверок работающих docker-контейнеров на тестовых и продуктовых контурах на наличие открытых портов, правил монтирования файловых систем, установки ограничений используемых ресурсов и привилегий и т.п.</li>
116 <li>Разделение итогового конвейера на составные части, чтобы этапы безопасности можно было подключать в других схожих проектах, но которые имеют отличающийся регламент разработки и дальнейшее развитие этих этапов.</li>
116 <li>Разделение итогового конвейера на составные части, чтобы этапы безопасности можно было подключать в других схожих проектах, но которые имеют отличающийся регламент разработки и дальнейшее развитие этих этапов.</li>
117 <li>Отправка сведений об обнаруженных в ходе сборки или работы приложения проблемах безопасности в учетную систему.</li>
117 <li>Отправка сведений об обнаруженных в ходе сборки или работы приложения проблемах безопасности в учетную систему.</li>
118 </ol><h4>Итоги.</h4>
118 </ol><h4>Итоги.</h4>
119 <p>Отсутствие этапов безопасной разработки в проекте в течение полутора лет его существования наглядно показало, к каким последствиям это может привести - конкретно в этом случае это стоило компании нескольких недель простоя, потери ряда клиентов и части репутации из-за произошедших инцидентов.</p>
119 <p>Отсутствие этапов безопасной разработки в проекте в течение полутора лет его существования наглядно показало, к каким последствиям это может привести - конкретно в этом случае это стоило компании нескольких недель простоя, потери ряда клиентов и части репутации из-за произошедших инцидентов.</p>
120 <p>Из всего произошедшего были сделаны правильные выводы - теперь вопросы информационной безопасности закладываются уже на этапе формирования требований и технических заданий, с привлечением профильных специалистов, включая не только автоматические проверки на этапах сборки и тестирования, но и привлечения сторонних подрядчиков для проведения аудита как приложений, так и инфраструктуры компании.</p>
120 <p>Из всего произошедшего были сделаны правильные выводы - теперь вопросы информационной безопасности закладываются уже на этапе формирования требований и технических заданий, с привлечением профильных специалистов, включая не только автоматические проверки на этапах сборки и тестирования, но и привлечения сторонних подрядчиков для проведения аудита как приложений, так и инфраструктуры компании.</p>
121 <p><em>Хотите знать больше про DevSecOps? Добро пожаловать на <a>специализированный курс в Otus</a>!</em></p>
121 <p><em>Хотите знать больше про DevSecOps? Добро пожаловать на <a>специализированный курс в Otus</a>!</em></p>
122  
122