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 && \ apt-get install -y --no-install-recommends curl build-essential gcc libssl-dev default-libmysqlclient-dev \ libc-dev libxml2-dev libxslt-dev wkhtmltopdf libmagic1 && \ curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python && \ cd /usr/local/bin && \ ln -s /opt/poetry/bin/poetry && \ poetry config virtualenvs.create false && \ apt-get remove curl -y && \ 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 && \ apt-get install -y --no-install-recommends curl build-essential gcc libssl-dev default-libmysqlclient-dev \ libc-dev libxml2-dev libxslt-dev wkhtmltopdf libmagic1 && \ curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python && \ cd /usr/local/bin && \ ln -s /opt/poetry/bin/poetry && \ poetry config virtualenvs.create false && \ apt-get remove curl -y && \ 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 -> Repository -> Push rules</p>
65
<p><strong>Push rules:</strong>Settings -> Repository -> 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 -> Repository -> Protected branches</p>
68
<p><strong>Защищенная ветка:</strong>Settings -> Repository -> Protected branches</p>
69
<p>Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:</p>
69
<p>Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:</p>
70
<p><strong>Merge requests:</strong>Settings -> Merge requests -> Merge requests</p>
70
<p><strong>Merge requests:</strong>Settings -> Merge requests -> 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 && flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy && pip3 install sqlalchemy-stubs && mypy . allow_failure: true<p><strong>Сканирование итоговых образов на наличие уязвимостей.</strong></p>
87
stages:<strong>-</strong>linters flake8: stage: linters script: | pip3 install flake8 && flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy && pip3 install sqlalchemy-stubs && 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 && flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy && pip3 install sqlalchemy-stubs && 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 && flake8 . allow_failure: true mypy: stage: linters script: | pip3 install mypy && pip3 install sqlalchemy-stubs && 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 -> CI/CD -> General pipelines, в которой указать путь к файлу конвейера, например:</li>
102
<ol><li>Используя настройку Settings -> CI/CD -> 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