В конце курса «Внедрение и работа в DevSecOps» студентов ждёт выполнение проекта. Это самостоятельная работа, необходимая для закрепления полученных знаний. Предлагаем вашему вниманию проект Сергея Кушнерчука, одного из выпускников курса.
Данный проект является одним из микросервисов реальной системы, реализующей оказание юридических услуг, поэтому это практическая, а не теоретическая работа.
Postmortem.
Некоторое время назад произошел ряд инцидентов, связанных с информационной безопасностью, что привело к финансовым и репутационным потерям компании.
Было принято решение внедрить этапы безопасной разработки в весь жизненный цикл приложения, начав с разработки, тестирования и сборки приложения.
План работы по внедрению этапов DevSecOps.
- Анализ состояния вопросов безопасности в проекте.
- определение технологического стека приложения;
- предварительная оценка состояния кодовой базы;
- предварительная оценка текущего цикла сборки и тестирования приложения.
- Подготовка и внедрение в конвейер этапов безопасной разработки (первая итерация)
- определение этапов, которые будут внедрены;
- выбор инструментов;
- реализация выбранных решений и подготовка нового конвейера.
- Оценка результатов работы
- Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложений.
1. Анализ состояния вопросов безопасности в проекте.
Определение технологического стека приложения.
Технологический стек, используемый в работе приложения:
- Python 3.10
- Docker
- MariaDB
- MongoDB
- FluentD
- RabbitMQ
- MinIO
- Debian 10
Все операции по разработке, сборке и тестированию на момент написания работы выполнялись в GitLab EE 16.2.4 Ultimate.
На момент принятия проекта в работу какие-либо меры безопасной разработки в проекте отсутствовали. По результатам первичного анализа (без использования средств автоматизации) был обнаружен ряд грубых нарушений, таких как:
Наличие в исходном коде чувствительных данных и необоснованное использование повышенных привилегий:
Запуск образа приложения от пользователя root:
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"]
Отсутствие проверок линтеров и каких-либо сканеров безопасности в цикле сборки образа приложения.
Различие docker-образов python, используемых для тестирования и сборки приложения:
- тестирование — python:3.8.5
- сборка итогового образа: python:3.10-slim
Предварительная оценка текущего цикла сборки и тестирования приложения.
Существующий конвейер — простой и прямолинейный, какие-либо меры безопасности отсутствуют полностью:
stages:
- test
- 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:
- python
stage: test
image: python:3.8.5
services:
- name: mariadb:10.3
command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
script:
- curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python
- ln -s /opt/poetry/bin/poetry /usr/local/bin/poetry
- poetry config virtualenvs.create false
- poetry install --no-root
- rm -f .dev.env
only:
- branches
- tags
when: manual
Build stage image:
stage: build
tags:
- python
script:
- docker build -t "$REGISTRY_IMAGE:latest" .
- sleep 2
- docker push "$REGISTRY_IMAGE:latest"
only:
- development
when: manual
Build production image:
stage: build
tags:
- python
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+$/
when: manual
- if: $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+\-patch$/
when: manual
script:
- DATE=`date +%d-%m-%Y`
- TAG=$DATE_$CI_COMMIT_TAG
- docker build --pull -t "$REGISTRY_IMAGE:$TAG" .
- docker push "$REGISTRY_IMAGE:$TAG"
before_script:
- set -xe
- docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASSWORD" $REGISTRY
after_script:
- docker logout $REGISTRY
Мы начнем подготовку нового конвейера с чистого листа, сначала внедрив некоторые наши этапы.
2. Подготовка и внедрение в конвейер этапов безопасной разработки.
Планирование внедряемых этапов.
Для реализации поставленных целей на начальном этапе мы планируем реализовать:
- Настройку проекта средствами GitLab.
- Определение наличия чувствительных данных в кодовой базе.
- Проверку зависимостей.
- SAST
- Проверку исходного кода линтерами.
- Сканирование итоговых образов на наличие уязвимостей.
- Защиту итогового конвейера от изменений.
Выбор инструментов
Мы будем использовать на первой итерации только штатные средства, которые предоставляет нам GitLab — его арсенал достаточно обширен и покрывает все наши потребности на данном этапе. Однако, если вы не являетесь владельцем ultimate-версии GitLab, то вам придется искать другие варианты решения спланированных задач, а также будут недоступны штатные инструменты, формирующие отчеты о безопасности.
Реализация выбранных решений и подготовка конвейера.
Настройка проекта.
Push rules: Settings -> Repository -> Push rules
Выставляем нужные настройки, как указано на изображении ниже:
Основным флажком, который точно должен быть установлен, является Prevent pushing secret files, остальные настройки выставляются в соответствии с регламентом разработки.
Защищенная ветка: Settings -> Repository -> Protected branches
Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:
Merge requests: Settings -> Merge requests -> Merge requests
Для предотвращения слияния реквестов, которые еще не разрешены, или имеют не успешные конвейеры, необходимо выставить параметры:
Определение наличия чувствительных данных в проекте.
Задача поиска утечек паролей, токенов, сертификатов и других чувствительных данных в коде проекта задача не совсем простая — высока вероятность как пропуска реальных данных, так и ложноположительных срабатываний, поэтому этот этап не должен являться блокирующим для выпуска приложения. Он должен быть включен в регламент code review и являться основанием как для выдачи замечаний, так и чистки истории от попавших в репозиторий данных.
Существует несколько утилит для решения такой задачи, но мы пока ограничимся штатным средством, которое предоставляет GitLab, так как не хотим потерять возможности удобного встроенного средства анализа безопасности — Security dashboard и Vulnerability report, тем более что при необходимости мы можем добавить свои правила для поиска чувствительных данных — под капотом для решения этой задачи GitLab использует gitleaks
Добавим этот этап:
variables:
SECURE_LOG_LEVEL: 'debug'
SECRET_DETECTION_HISTORIC_SCAN: 'true'
SEARCH_MAX_DEPTH: 1000
include:
- template: Security/Secret-Detection.gitlab-ci.yml
stages:
- security
secret_detection:
stage: security
artifacts:
reports:
sast: gl-secret-detection-report.json
После запуска конвейера видим, что помимо найденного ранее, обнаружено еще три места, где возможно есть чувствительные данные:
Однако это ложные срабатывания, которые мы можем пометить как *#gitleaks:allow, чтобы эти строки в будущем не учитывались при анализе:
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") #
*#gitleaks:allow
return f"mysql://{user}:{password}@{server}/{db}?charset=utf8mb4" # *#gitleaks:allow
Проверка зависимостей.
Добавим проверку используемых зависимостей на уязвимости (эта же задача сформирует нам отчет о лицензиях используемых зависимостей):
variables:
LICENSE_MANAGEMENT_VERSION: 4
include:
- template: Security/Dependency-Scanning.gitlab-ci.yml
В результате работы конвейера получаем список из 21 уязвимости:
SAST
Добавим этап SAST:
variables:
SAST_EXPERIMENTAL_FEATURES: "true"
include:
- template: Security/SAST.gitlab-ci.yml
После запуска конвейера мы получим 104 найденные проблемы SAST:
Проверка исходного кода линтерами.
flake8, mypy
Эти линтеры уже были добавлены в проект ранее как зависимости, в проекте есть их конфигурационные файлы, однако в конвейерах не использовались. Сделаем так, чтобы пока эти этапы не влияли на конечный результат сборки, однако, в случае наличия проблем, это было визуально видно:
stages:
- 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
Сканирование итоговых образов на наличие уязвимостей.
Для реализации этапа сначала вернем в конвейер базовую сборку:
build:
needs:
- 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:
- branches
Для проверки образов также будем использовать уже встроенную возможность GitLab (под капотом там trivy с некоторой оберткой)
include:
- 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:
- post_security
container_scanning:
stage: post_security
needs:
- build
Для такой проверки мы сделаем отдельный этап post_security, который будет запускаться после сборки приложения:
Результат работы этого этапа представлен ниже:
Итак, конвейер, содержащий необходимые базовые операции по проверкам безопасности, а также тестированию и базовой сборке образа приложения выглядит так:
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:
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/SAST.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
stages:
- security
- linters
- test
- build
- 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:
- name: mariadb:10.3
command: [ 'mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci' ]
script:
- pip3 install poetry
- poetry config virtualenvs.create false
- poetry install
- python -m pytest --cov
only:
- branches
build:
needs:
- 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:
- branches
container_scanning:
stage: post_security
needs:
- build
В ходе работы были приняты несколько дополнительных решений:
- Сделать так, чтобы все эти этапы могли быть переиспользованы в других аналогичных сервисах с минимальными изменениями, или вовсе без них.
- Максимально освободить разработчиков от погружения в вопросы DevSecOps в процессе жизненного цикла приложения.
- Защитить конвейер от случайных или злонамеренных изменений, внесенных разработчиками, при этом оставив возможность простого управления им.
Для внедрения принятых решений мы будем использовать возможность GitLab выполнять включение конвейеров из других репозиториев. Для этого мы перенесем его в другой репозиторий той же группы GitLab, а в нашем проекте выполним его включение:
include:
- project: 'services/ci'
ref: master
file: 'python-ci.yml'
Таким образом мы решаем первую и вторую поставленные задачи — разработчикам больше нет необходимости думать о настройке CI/CD — им достаточно взять уже готовый файл для размещения в уже существующих проектах, а для новых проектов можно подготовить шаблон, который уже будет содержать все необходимое.
Теперь мы можем выполнить слияние наших изменений в продуктовую ветку, и приступить к решению задачи по защите конвейера от изменений.
Защита итогового конвейера от изменений
На период внедрения и тестирования этапов DevSecOps было принято решение о запрете вмешательства разработчиков в подготовку и работу конвейера. Технически наиболее просто достичь этого можно двумя способами:
- Используя настройку Settings -> CI/CD -> General pipelines, в которой указать путь к файлу конвейера, например:
python-ci.yml@services/ci:master
- Установив блокировку на файл конвейера в проекте пользователем с максимальным уровнем привилегий (на весь этап работы это член команды DevSecOps) — это блокирует его во всех ветках проекта:
Первый вариант нам не подходит, так как мы хотим в будущем сделать вариативный конвейер, управление жизненным циклом которого будет осуществляться с помощью переменных, размещаемых в его файле, поэтому мы будем использовать блокировку файла конвейера, которая в будущем будет снята.
Оценка результатов работы.
Итогом первой итерации стал конвейер, который показывает, насколько сильно подвержено рискам безопасности приложение.
Также было принято решение отложить выпуск последующих релизов до устранения критических проблем, поэтому этап полной сборки и деплоя продуктового образа пока в конвейере не реализован. Сроки восстановления релизов на данном этапе не ясны — и это хорошо показывает, как халатное отношение к вопросам безопасности может навредить бизнесу.
4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.
Для устранения всех выявленных и недопущения новых недостатков мы можем спланировать следующие этапы нашей работы:
- Разработка и согласование плана для формирования пула задач, которые будут решаться командами разработки и DevSecOps приоритетно.
- Интеграция и использование разработчиками необходимых инструментов безопасности в их локальном окружении.
- Более тонкая настройка инструментов (или их замена на более совершенные) для исключения ложных и пропуска настоящих срабатываний, а также внедрение нового инструментария для решения других возникших задач.
- Разработка скриптов автоматизации (при необходимости).
- Определение и применение блок-факторов безопасности для сборки или выпуска релизов приложения (на данном этапе это критические проблемы для выпуска релизов, тестовые сборки будут продолжаться чтобы не блокировать работу команд разработки полностью).
- Внедрение автоматизированных проверок работающих docker-контейнеров на тестовых и продуктовых контурах на наличие открытых портов, правил монтирования файловых систем, установки ограничений используемых ресурсов и привилегий и т.п.
- Разделение итогового конвейера на составные части, чтобы этапы безопасности можно было подключать в других схожих проектах, но которые имеют отличающийся регламент разработки и дальнейшее развитие этих этапов.
- Отправка сведений об обнаруженных в ходе сборки или работы приложения проблемах безопасности в учетную систему.
Итоги.
Отсутствие этапов безопасной разработки в проекте в течение полутора лет его существования наглядно показало, к каким последствиям это может привести — конкретно в этом случае это стоило компании нескольких недель простоя, потери ряда клиентов и части репутации из-за произошедших инцидентов.
Из всего произошедшего были сделаны правильные выводы — теперь вопросы информационной безопасности закладываются уже на этапе формирования требований и технических заданий, с привлечением профильных специалистов, включая не только автоматические проверки на этапах сборки и тестирования, но и привлечения сторонних подрядчиков для проведения аудита как приложений, так и инфраструктуры компании.
Хотите знать больше про DevSecOps? Добро пожаловать на специализированный курс в Otus!
<!DOCTYPE html>
<html dir="ltr" lang="ru-RU">
<head>
<meta charset="UTF-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="profile" href="http://gmpg.org/xfn/11" />
<title>Внедрение этапов DevSecOps в цикл разработки, поставки и эксплуатации приложения OTUS</title>
<!-- All in One SEO 4.5.2.1 - aioseo.com -->
<meta name="description" content="В конце курса «Внедрение и работа в DevSecOps» студентов ждёт выполнение проекта. Это самостоятельная работа, необходимая для закрепления полученных знаний. Предлагаем вашему вниманию проект Сергея Кушнерчука, одного из выпускников курса. Данный проект является одним из микросервисов реальной системы, реализующей оказание юридических услуг, поэтому это практическая, а не теоретическая работа. Postmortem. Некоторое время назад произошел ряд инцидентов," />
<meta name="robots" content="max-image-preview:large" />
<link rel="canonical" href="https://otus.ru/journal/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya/" />
<meta name="generator" content="All in One SEO (AIOSEO) 4.5.2.1" />
<script type="application/ld+json" class="aioseo-schema">
{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#article","name":"\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u044d\u0442\u0430\u043f\u043e\u0432 DevSecOps \u0432 \u0446\u0438\u043a\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f OTUS","headline":"\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u044d\u0442\u0430\u043f\u043e\u0432 DevSecOps \u0432 \u0446\u0438\u043a\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f","author":{"@id":"https:\/\/otus.ru\/journal\/author\/a-pavlenko\/#author"},"publisher":{"@id":"https:\/\/otus.ru\/journal\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/otus.ru\/journal\/wp-content\/uploads\/2023\/11\/oj-1080x720-1.png","width":2245,"height":1587},"datePublished":"2023-11-03T22:18:23+00:00","dateModified":"2023-11-07T13:29:49+00:00","inLanguage":"ru-RU","mainEntityOfPage":{"@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#webpage"},"isPartOf":{"@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#webpage"},"articleSection":"\u041f\u043e\u043b\u0435\u0437\u043d\u043e\u0435, devsecops, \u043f\u0440\u043e\u0435\u043a\u0442"},{"@type":"BreadcrumbList","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/otus.ru\/journal\/#listItem","position":1,"name":"\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430","item":"https:\/\/otus.ru\/journal\/","nextItem":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#listItem"},{"@type":"ListItem","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#listItem","position":2,"name":"\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u044d\u0442\u0430\u043f\u043e\u0432 DevSecOps \u0432 \u0446\u0438\u043a\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f","previousItem":"https:\/\/otus.ru\/journal\/#listItem"}]},{"@type":"Organization","@id":"https:\/\/otus.ru\/journal\/#organization","name":"\u041e\u0442\u0443\u0441 \u043e\u043d\u043b\u0430\u0439\u043d-\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0435","url":"https:\/\/otus.ru\/journal\/","sameAs":["https:\/\/www.youtube.com\/channel\/UCetgtvy93o3i3CvyGXKFU3g"],"contactPoint":{"@type":"ContactPoint","telephone":"+74999389202","contactType":"Customer Support"}},{"@type":"Person","@id":"https:\/\/otus.ru\/journal\/author\/a-pavlenko\/#author","url":"https:\/\/otus.ru\/journal\/author\/a-pavlenko\/","name":"A. Pavlenko","image":{"@type":"ImageObject","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d4c499a104d7c2522fa41f89e6819499?s=96&d=mm&r=g","width":96,"height":96,"caption":"A. Pavlenko"}},{"@type":"WebPage","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#webpage","url":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/","name":"\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u044d\u0442\u0430\u043f\u043e\u0432 DevSecOps \u0432 \u0446\u0438\u043a\u043b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f OTUS","description":"\u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u0443\u0440\u0441\u0430 \u00ab\u0412\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430 \u0432 DevSecOps\u00bb \u0441\u0442\u0443\u0434\u0435\u043d\u0442\u043e\u0432 \u0436\u0434\u0451\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u042d\u0442\u043e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430\u044f \u0434\u043b\u044f \u0437\u0430\u043a\u0440\u0435\u043f\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u043d\u0438\u0439. \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u0432\u0430\u0448\u0435\u043c\u0443 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044e \u043f\u0440\u043e\u0435\u043a\u0442 \u0421\u0435\u0440\u0433\u0435\u044f \u041a\u0443\u0448\u043d\u0435\u0440\u0447\u0443\u043a\u0430, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0432\u044b\u043f\u0443\u0441\u043a\u043d\u0438\u043a\u043e\u0432 \u043a\u0443\u0440\u0441\u0430. \u0414\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0435\u0439 \u043e\u043a\u0430\u0437\u0430\u043d\u0438\u0435 \u044e\u0440\u0438\u0434\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0443\u0441\u043b\u0443\u0433, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044d\u0442\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f, \u0430 \u043d\u0435 \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430. Postmortem. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0437\u0430\u0434 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u043b \u0440\u044f\u0434 \u0438\u043d\u0446\u0438\u0434\u0435\u043d\u0442\u043e\u0432,","inLanguage":"ru-RU","isPartOf":{"@id":"https:\/\/otus.ru\/journal\/#website"},"breadcrumb":{"@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#breadcrumblist"},"author":{"@id":"https:\/\/otus.ru\/journal\/author\/a-pavlenko\/#author"},"creator":{"@id":"https:\/\/otus.ru\/journal\/author\/a-pavlenko\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/otus.ru\/journal\/wp-content\/uploads\/2023\/11\/oj-1080x720-1.png","@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#mainImage","width":2245,"height":1587},"primaryImageOfPage":{"@id":"https:\/\/otus.ru\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/#mainImage"},"datePublished":"2023-11-03T22:18:23+00:00","dateModified":"2023-11-07T13:29:49+00:00"},{"@type":"WebSite","@id":"https:\/\/otus.ru\/journal\/#website","url":"https:\/\/otus.ru\/journal\/","name":"OTUS JOURNAL","description":"Blog about IT","inLanguage":"ru-RU","publisher":{"@id":"https:\/\/otus.ru\/journal\/#organization"}}]}
</script>
<!-- All in One SEO -->
<link rel='dns-prefetch' href='//otus.ru' />
<link rel='dns-prefetch' href='//fonts.googleapis.com' />
<link rel='stylesheet' id='wp-block-library-css' href='https://otus.ru/journal/wp-includes/css/dist/block-library/style.min.css?ver=6.4.7' type='text/css' media='all' />
<style id='classic-theme-styles-inline-css' type='text/css'>
/*! This file is auto-generated */
.wp-block-button__link{color:#fff;background-color:#32373c;border-radius:9999px;box-shadow:none;text-decoration:none;padding:calc(.667em + 2px) calc(1.333em + 2px);font-size:1.125em}.wp-block-file__button{background:#32373c;color:#fff;text-decoration:none}
</style>
<style id='global-styles-inline-css' type='text/css'>
body{--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1);--wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1);}:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}body .is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}body .is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}body .is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}body .is-layout-flex{flex-wrap: wrap;align-items: center;}body .is-layout-flex > *{margin: 0;}body .is-layout-grid{display: grid;}body .is-layout-grid > *{margin: 0;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}:where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;}
.wp-block-navigation a:where(:not(.wp-element-button)){color: inherit;}
:where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}
:where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}
.wp-block-pullquote{font-size: 1.5em;line-height: 1.6;}
</style>
<link rel='stylesheet' id='wbcr-comments-plus-url-span-css' href='https://otus.ru/journal/wp-content/plugins/clearfy/components/comments-plus/assets/css/url-span.css?ver=2.2.0' type='text/css' media='all' />
<link rel='stylesheet' id='wpel-style-css' href='https://otus.ru/journal/wp-content/plugins/wp-external-links/public/css/wpel.css?ver=2.59' type='text/css' media='all' />
<link rel='stylesheet' id='ez-toc-css' href='https://otus.ru/journal/wp-content/plugins/easy-table-of-contents/assets/css/screen.min.css?ver=2.0.61' type='text/css' media='all' />
<style id='ez-toc-inline-css' type='text/css'>
div#ez-toc-container .ez-toc-title {font-size: 120%;}div#ez-toc-container .ez-toc-title {font-weight: 500;}div#ez-toc-container ul li {font-size: 95%;}div#ez-toc-container nav ul ul li {font-size: 90%;}
.ez-toc-container-direction {direction: ltr;}.ez-toc-counter ul{counter-reset: item ;}.ez-toc-counter nav ul li a::before {content: counters(item, ".", decimal) ". ";display: inline-block;counter-increment: item;flex-grow: 0;flex-shrink: 0;margin-right: .2em; float: left; }.ez-toc-widget-direction {direction: ltr;}.ez-toc-widget-container ul{counter-reset: item ;}.ez-toc-widget-container nav ul li a::before {content: counters(item, ".", decimal) ". ";display: inline-block;counter-increment: item;flex-grow: 0;flex-shrink: 0;margin-right: .2em; float: left; }
</style>
<link rel='stylesheet' id='contentberg-fonts-css' href='https://fonts.googleapis.com/css?family=Roboto%3A400%2C500%2C700%7CPT+Serif%3A400%2C400i%2C600%7CIBM+Plex+Serif%3A500' type='text/css' media='all' />
<link rel='stylesheet' id='contentberg-core-css' href='https://otus.ru/journal/wp-content/themes/contentberg/style.css?ver=1.8.3' type='text/css' media='all' />
<link rel='stylesheet' id='contentberg-lightbox-css' href='https://otus.ru/journal/wp-content/themes/contentberg/css/lightbox.css?ver=1.8.3' type='text/css' media='all' />
<link rel='stylesheet' id='font-awesome-css' href='https://otus.ru/journal/wp-content/themes/contentberg/css/fontawesome/css/font-awesome.min.css?ver=1.8.3' type='text/css' media='all' />
<script type="text/javascript" id="breeze-prefetch-js-extra">
/* <![CDATA[ */
var breeze_prefetch = {"local_url":"https:\/\/otus.ru\/journal","ignore_remote_prefetch":"1","ignore_list":["\/wp-admin\/"]};
/* ]]> */
</script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/breeze/assets/js/js-front-end/breeze-prefetch-links.min.js" id="breeze-prefetch-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-includes/js/jquery/jquery.min.js" id="jquery-core-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-includes/js/jquery/jquery-migrate.min.js" id="jquery-migrate-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/lazysizes.js" id="lazysizes-js"></script>
<link rel="https://api.w.org/" href="https://otus.ru/journal/wp-json/" /><link rel="alternate" type="application/json" href="https://otus.ru/journal/wp-json/wp/v2/posts/7873" /><link rel='shortlink' href='https://otus.ru/journal/?p=7873' />
<link rel="alternate" type="application/json+oembed" href="https://otus.ru/journal/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F" />
<link rel="alternate" type="text/xml+oembed" href="https://otus.ru/journal/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F&format=xml" />
<script>var Sphere_Plugin = {"ajaxurl":"https:\/\/otus.ru\/journal\/wp-admin\/admin-ajax.php"};</script><link rel="icon" href="https://otus.ru/journal/wp-content/uploads/2020/11/cropped-OTUS_logo_OTUS-COMP-LOGO-WHITE-1-32x32.png" sizes="32x32" />
<link rel="icon" href="https://otus.ru/journal/wp-content/uploads/2020/11/cropped-OTUS_logo_OTUS-COMP-LOGO-WHITE-1-192x192.png" sizes="192x192" />
<link rel="apple-touch-icon" href="https://otus.ru/journal/wp-content/uploads/2020/11/cropped-OTUS_logo_OTUS-COMP-LOGO-WHITE-1-180x180.png" />
<meta name="msapplication-TileImage" content="https://otus.ru/journal/wp-content/uploads/2020/11/cropped-OTUS_logo_OTUS-COMP-LOGO-WHITE-1-270x270.png" />
<style type="text/css" id="wp-custom-css">
#menu-item-10406 .wpel-icon {
display: none;
}
#menu-item-10407 .wpel-icon {
display: none;
}
.otus-login-site a .wpel-icon {
display: none;
}
.menu-menju-navykov-container a .wpel-icon {
display: none;
}
.otus-login-site a
{
background: #ffd709;
border-radius: 12px;
color: #0f0f10;
font-size: 14px;
font-weight: 700;
line-height: 20px;
display: block;
text-align: center;
padding: 8px 25px;
}
.main-footer.dark {
background: linear-gradient(90deg, #a64fc5, #4f54e6);
border-color: transparent;
}
.main-footer.bold .copyright {
color: #fff;
}
.main-footer.bold .to-top i {
color: #fff;
}
.main-footer.bold .back-to-top {
color: #fff;
}
.nav__scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.scrollable-menu .menu {
display: flex;
}
.nav__scroll
{
background: linear-gradient(90deg, #a64fc5, #4f54e6);
}
.scrollable-menu .menu .menu-item {
flex: 0 0 auto;
padding: 15px 15px;
}
.scrollable-menu .menu .menu-item a {
color: #fff;
}
.nav__scroll::-webkit-scrollbar{background-color:#fff;height:5px;}
.nav__scroll::-webkit-scrollbar-thumb{background-color:#dcdcdc;}
.nav__scroll::-webkit-scrollbar-track{-webkit-border-radius:0;border-radius:0;background-color:#fff;}/
body {
min-width: 320px;
}
.banner-click img {
margin: 0 auto;
display: block;
}
.banner-click {
cursor: pointer;
}
.banner-footer-area {
margin-bottom: 20px;
}
.banner-left-area {
margin-top: 40px;
} </style>
<!--Start VDZ Yandex Metrika Plugin-->
<!-- Yandex.Metrika counter --><script type="text/javascript" >(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");ym(34531570, "init", {clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true, trackHash:true, ecommerce:"dataLayer"});</script>
<noscript><div><img src="https://mc.yandex.ru/watch/34531570" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<!-- /Yandex.Metrika counter --><!--START ADD EVENTS FROM CF7--><script type='text/javascript'>document.addEventListener( 'wpcf7submit', function( event ) {
//event.detail.contactFormId;
if(ym){
//console.log(event.detail);
ym(34531570, 'reachGoal', 'VDZ_SEND_CONTACT_FORM_7');
ym(34531570, 'params', {
page_url: window.location.href,
status: event.detail.status,
locale: event.detail.contactFormLocale,
form_id: event.detail.contactFormId,
});
}
}, false );
</script><!--END ADD EVENTS FROM CF7-->
<!--End VDZ Yandex Metrika Plugin-->
</head>
<body class="post-template-default single single-post postid-7873 single-format-standard right-sidebar lazy-normal has-lb">
<div class="main-wrap">
<header id="main-head" class="main-head head-nav-below has-search-modal simple simple-boxed">
<div class="inner inner-head" data-sticky-bar="0">
<div class="wrap cf wrap-head">
<div class="left-contain">
<span class="mobile-nav"><i class="fa fa-bars"></i></span>
<div class="title">
<a href="https://otus.ru/journal/" title="OTUS JOURNAL" rel="home" data-wpel-link="internal">
<span class="text-logo"><img src="/journal/wp-content/themes/contentberg/img/logo_site.svg" alt="OTUS JOURNAL"></span>
</a>
</div>
</div>
<div class="navigation-wrap inline">
<nav class="navigation inline simple light" data-sticky-bar="0">
<div class="menu-rubriki-container"><ul id="menu-rubriki" class="menu"><li id="menu-item-109" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-cat-1 menu-item-109"><a href="https://otus.ru/journal/category/pro-it/" data-wpel-link="internal"><span>Про IT</span></a></li>
<li id="menu-item-113" class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-cat-4 menu-item-113"><a href="https://otus.ru/journal/category/polza/" data-wpel-link="internal"><span>Полезное</span></a></li>
<li id="menu-item-114" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-cat-3 menu-item-114"><a href="https://otus.ru/journal/category/lifestyle/" data-wpel-link="internal"><span>Лайфстайл</span></a></li>
<li id="menu-item-10406" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10406"><a href="https://otus.ru/catalog/courses" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right"><span>Обучение</span><span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10407" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10407"><a href="https://otus.ru/about" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right"><span>Информация</span><span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
</ul></div> </nav>
</div>
<div class="actions">
<div class="otus-login-site">
<a href="https://otus.ru/login/" target="_blank" data-wpel-link="external" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Войти<span class="wpel-icon wpel-image wpel-icon-6"></span></a>
</div>
<a href="#" title="Search" class="search-link"><i class="fa fa-search"></i></a>
</div>
</div>
</div>
</header> <!-- .main-head -->
<div class="nav nav_disable nav_colored nav_transparent course-categories__nav nav__scroll ">
<div class="container wrap">
<div class="links inline simple light scrollable-menu">
<div class="menu-menju-navykov-container"><ul id="menu-menju-navykov" class="menu"><li id="menu-item-10413" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10413"><a href="https://otus.ru/categories/programming/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Программирование<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10414" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10414"><a href="https://otus.ru/categories/architecture/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Архитектура<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10415" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10415"><a href="https://otus.ru/categories/operations/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Инфраструктура<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10416" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10416"><a href="https://otus.ru/categories/information-security-courses/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Безопасность<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10417" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10417"><a href="https://otus.ru/categories/data-science/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Data Science<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10418" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10418"><a href="https://otus.ru/categories/gamedev/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">GameDev<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10419" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10419"><a href="https://otus.ru/categories/marketing-business/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Управление<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10420" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10420"><a href="https://otus.ru/categories/analytics/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Аналитика и анализ<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li id="menu-item-10421" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10421"><a href="https://otus.ru/categories/testing/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Тестирование<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
</ul></div> </div>
</div>
</div>
<div class="main wrap">
<div class="ts-row cf">
<div class="col-8 main-content cf">
<article id="post-7873" class="the-post post-7873 post type-post status-publish format-standard has-post-thumbnail category-polza tag-devsecops tag-proekt">
<header class="post-header the-post-header cf">
<div class="post-meta the-post-meta">
<span class="post-cat">
<a href="https://otus.ru/journal/category/polza/" class="category" data-wpel-link="internal">Полезное</a>
</span>
<h1 class="post-title">
Внедрение этапов DevSecOps в цикл разработки, поставки и эксплуатации приложения
</h1>
<a href="https://otus.ru/journal/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya/" class="date-link" data-wpel-link="internal"><time class="post-date">3 ноября, 2023</time></a>
</div>
<div class="featured">
<a href="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x720-1.png" class="image-link" data-wpel-link="internal"><img width="770" height="515" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20770%20515%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="attachment-contentberg-main size-contentberg-main lazyload wp-post-image" alt="Внедрение этапов DevSecOps в цикл разработки, поставки и эксплуатации приложения" title="Внедрение этапов DevSecOps в цикл разработки, поставки и эксплуатации приложения" decoding="async" fetchpriority="high" data-srcset="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x720-1-770x515.png 770w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x720-1-270x180.png 270w" data-src="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x720-1-770x515.png" data-sizes="(max-width: 770px) 100vw, 770px" /> </a>
</div>
</header><!-- .post-header -->
<div class="post-content description cf entry-content content-normal">
<div id="ez-toc-container" class="ez-toc-v2_0_61 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction">
<div class="ez-toc-title-container">
<p class="ez-toc-title " >Содержание</p>
<span class="ez-toc-title-toggle"><a href="#" class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" aria-label="Toggle Table of Content"><span class="ez-toc-js-icon-con"><span class=""><span class="eztoc-hide" style="display:none;">Toggle</span><span class="ez-toc-icon-toggle-span"><svg style="fill: #999;color:#999" xmlns="http://www.w3.org/2000/svg" class="list-377408" width="20px" height="20px" viewBox="0 0 24 24" fill="none"><path d="M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z" fill="currentColor"></path></svg><svg style="fill: #999;color:#999" class="arrow-unsorted-368013" xmlns="http://www.w3.org/2000/svg" width="10px" height="10px" viewBox="0 0 24 24" version="1.2" baseProfile="tiny"><path d="M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z"/></svg></span></span></span></a></span></div>
<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-1" href="#Postmortem" title="Postmortem.">Postmortem.</a></li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-2" href="#%D0%9F%D0%BB%D0%B0%D0%BD_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B_%D0%BF%D0%BE_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D1%8E_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_DevSecOps" title="План работы по внедрению этапов DevSecOps.">План работы по внедрению этапов DevSecOps.</a></li></ul></li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class="ez-toc-link ez-toc-heading-3" href="#1_%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D1%8F_%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2_%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B2_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5" title="1. Анализ состояния вопросов безопасности в проекте.">1. Анализ состояния вопросов безопасности в проекте.</a><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-4" href="#%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D1%82%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B3%D0%BE_%D1%81%D1%82%D0%B5%D0%BA%D0%B0_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F" title="Определение технологического стека приложения.">Определение технологического стека приложения.</a></li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-5" href="#%D0%9F%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE_%D1%86%D0%B8%D0%BA%D0%BB%D0%B0_%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B8_%D0%B8_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F" title="Предварительная оценка текущего цикла сборки и тестирования приложения.">Предварительная оценка текущего цикла сборки и тестирования приложения.</a></li></ul></li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class="ez-toc-link ez-toc-heading-6" href="#2_%D0%9F%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B8_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D0%B9_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8" title="2. Подготовка и внедрение в конвейер этапов безопасной разработки.">2. Подготовка и внедрение в конвейер этапов безопасной разработки.</a><ul class='ez-toc-list-level-5' ><li class='ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-7" href="#%D0%9F%D0%BB%D0%B0%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D1%8F%D0%B5%D0%BC%D1%8B%D1%85_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2" title="Планирование внедряемых этапов.">Планирование внедряемых этапов.</a></li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-8" href="#%D0%92%D1%8B%D0%B1%D0%BE%D1%80_%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2" title="Выбор инструментов">Выбор инструментов</a></li><li class='ez-toc-page-1 ez-toc-heading-level-5'><a class="ez-toc-link ez-toc-heading-9" href="#%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B9_%D0%B8_%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80%D0%B0" title="Реализация выбранных решений и подготовка конвейера.">Реализация выбранных решений и подготовка конвейера.</a></li></ul></li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class="ez-toc-link ez-toc-heading-10" href="#%D0%9E%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%BE%D0%B2_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B" title="Оценка результатов работы.">Оценка результатов работы.</a></li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class="ez-toc-link ez-toc-heading-11" href="#4_%D0%9F%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D0%BB%D0%B0%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D0%BB%D0%B5%D0%B4%D1%83%D1%8E%D1%89%D0%B8%D1%85_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_%D0%BF%D0%BE_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D1%8E_DevSecOps_%D0%B2_%D0%B6%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F" title="4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.">4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.</a></li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class="ez-toc-link ez-toc-heading-12" href="#%D0%98%D1%82%D0%BE%D0%B3%D0%B8" title="Итоги.">Итоги.</a></li></ul></nav></div>
<p><em>В конце курса «<a href="https://otus.ru/lessons/devsecops/?utm_source=oj&utm_medium=affilate&utm_campaign=devsec" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">Внедрение и работа в DevSecOps<span class="wpel-icon wpel-image wpel-icon-6"></span></a>» студентов ждёт выполнение проекта. Это самостоятельная работа, необходимая для закрепления полученных знаний. Предлагаем вашему вниманию проект <strong><a href="https://www.linkedin.com/in/sergey-kushnerchuk/" title="" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Сергея Кушнерчука<span class="wpel-icon wpel-image wpel-icon-6"></span></a></strong>, одного из выпускников курса.</em></p>
<p>Данный проект является одним из микросервисов реальной системы, реализующей оказание юридических услуг, поэтому это практическая, а не теоретическая работа.</p>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="Postmortem"></span>Postmortem.<span class="ez-toc-section-end"></span></h5>
<p>Некоторое время назад произошел ряд инцидентов, связанных с информационной безопасностью, что привело к финансовым и репутационным потерям компании.</p>
<p>Было принято решение внедрить этапы безопасной разработки в весь жизненный цикл приложения, начав с разработки, тестирования и сборки приложения.</p>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%9F%D0%BB%D0%B0%D0%BD_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B_%D0%BF%D0%BE_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D1%8E_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_DevSecOps"></span>План работы по внедрению этапов DevSecOps.<span class="ez-toc-section-end"></span></h5>
<ol>
<li>Анализ состояния вопросов безопасности в проекте.</li>
<li>определение технологического стека приложения;</li>
<li>предварительная оценка состояния кодовой базы;</li>
<li>предварительная оценка текущего цикла сборки и тестирования приложения.</li>
<li>Подготовка и внедрение в конвейер этапов безопасной разработки (первая итерация)</li>
<li>определение этапов, которые будут внедрены;</li>
<li>выбор инструментов;</li>
<li>реализация выбранных решений и подготовка нового конвейера.</li>
<li>Оценка результатов работы</li>
<li>Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложений.</li>
</ol>
<h4 class="wp-block-heading"><span class="ez-toc-section" id="1_%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D1%8F_%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2_%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B2_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B5"></span>1. Анализ состояния вопросов безопасности в проекте.<span class="ez-toc-section-end"></span></h4>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D1%82%D0%B5%D1%85%D0%BD%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B3%D0%BE_%D1%81%D1%82%D0%B5%D0%BA%D0%B0_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F"></span>Определение технологического стека приложения.<span class="ez-toc-section-end"></span></h5>
<p>Технологический стек, используемый в работе приложения:</p>
<ul>
<li>Python 3.10</li>
<li>Docker</li>
<li>MariaDB</li>
<li>MongoDB</li>
<li>FluentD</li>
<li>RabbitMQ</li>
<li>MinIO</li>
<li>Debian 10</li>
</ul>
<p>Все операции по разработке, сборке и тестированию на момент написания работы выполнялись в <strong>GitLab EE 16.2.4 Ultimate</strong>.</p>
<p>На момент принятия проекта в работу какие-либо меры безопасной разработки в проекте отсутствовали. По результатам первичного анализа (без использования средств автоматизации) был обнаружен ряд грубых нарушений, таких как:</p>
<p><strong>Наличие в исходном коде чувствительных данных и необоснованное использование повышенных привилегий:</strong><img decoding="async" alt="secret_data-20219-d5af7d.png" src="https://cdn.otus.ru/media/private/d5/af/secret_data-20219-d5af7d.png?hash=4yafPRZt4fpddOJqU-AY2g&expires=1699078074"></p>
<p><strong>Запуск образа приложения от пользователя root</strong>:</p>
<pre class="wp-block-preformatted">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"]
</pre>
<p><strong>Отсутствие проверок линтеров и каких-либо сканеров безопасности в цикле сборки образа приложения.</strong></p>
<p><strong>Различие docker-образов python, используемых для тестирования и сборки приложения:</strong></p>
<ul>
<li>тестирование — python:3.8.5</li>
<li>сборка итогового образа: python:3.10-slim</li>
</ul>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%9F%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%BE%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B3%D0%BE_%D1%86%D0%B8%D0%BA%D0%BB%D0%B0_%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B8_%D0%B8_%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F"></span>Предварительная оценка текущего цикла сборки и тестирования приложения.<span class="ez-toc-section-end"></span></h5>
<p>Существующий конвейер — простой и прямолинейный, какие-либо меры безопасности отсутствуют полностью:</p>
<pre class="wp-block-preformatted">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
</pre>
<p>Мы начнем подготовку нового конвейера с чистого листа, сначала внедрив некоторые наши этапы.</p>
<h4 class="wp-block-heading"><span class="ez-toc-section" id="2_%D0%9F%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B8_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2_%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D0%B9_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8"></span>2. Подготовка и внедрение в конвейер этапов безопасной разработки.<span class="ez-toc-section-end"></span></h4>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%9F%D0%BB%D0%B0%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D1%8F%D0%B5%D0%BC%D1%8B%D1%85_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2"></span>Планирование внедряемых этапов.<span class="ez-toc-section-end"></span></h5>
<p>Для реализации поставленных целей на начальном этапе мы планируем реализовать:</p>
<ol>
<li>Настройку проекта средствами GitLab.</li>
<li>Определение наличия чувствительных данных в кодовой базе.</li>
<li>Проверку зависимостей.</li>
<li>SAST</li>
<li>Проверку исходного кода линтерами.</li>
<li>Сканирование итоговых образов на наличие уязвимостей.</li>
<li>Защиту итогового конвейера от изменений.</li>
</ol>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%92%D1%8B%D0%B1%D0%BE%D1%80_%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2"></span>Выбор инструментов<span class="ez-toc-section-end"></span></h5>
<p>Мы будем использовать на первой итерации только штатные средства, которые предоставляет нам GitLab — его арсенал достаточно обширен и покрывает все наши потребности на данном этапе. Однако, если вы не являетесь владельцем ultimate-версии GitLab, то вам придется искать другие варианты решения спланированных задач, а также будут недоступны штатные инструменты, формирующие отчеты о безопасности.</p>
<h5 class="wp-block-heading"><span class="ez-toc-section" id="%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F_%D0%B2%D1%8B%D0%B1%D1%80%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B9_%D0%B8_%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BD%D0%B2%D0%B5%D0%B9%D0%B5%D1%80%D0%B0"></span>Реализация выбранных решений и подготовка конвейера.<span class="ez-toc-section-end"></span></h5>
<p><strong>Настройка проекта.</strong></p>
<p><strong>Push rules:</strong> Settings -> Repository -> Push rules</p>
<p>Выставляем нужные настройки, как указано на изображении ниже:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/19/a5/push_rules-20219-19a5a2.png?hash=og3t64Adftg0HWJP0RmmnA&expires=1699077998" alt="push_rules-20219-19a5a2.png"/></figure>
<p>Основным флажком, который точно должен быть установлен, является Prevent pushing secret files, остальные настройки выставляются в соответствии с регламентом разработки.</p>
<p><strong>Защищенная ветка:</strong> Settings -> Repository -> Protected branches</p>
<p>Ветка, которая содержит стабильный код и из которой выпускается код в production, должна быть защищена от случайных изменений. В данном случае только maintainer проекта принимает решение о слиянии кода из остальных веток в нее, а прямая заливка запрещена вообще всем:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/c9/49/protected_branch-20219-c94911.png?hash=FpC9cd5ot5QSuA_c0J_auA&expires=1699077985" alt="protected_branch-20219-c94911.png"/></figure>
<p><strong>Merge requests:</strong> Settings -> Merge requests -> Merge requests</p>
<p>Для предотвращения слияния реквестов, которые еще не разрешены, или имеют не успешные конвейеры, необходимо выставить параметры:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/01/6b/merge_requests-20219-016b69.png?hash=8P3Ui0dCOrDjS9TQvZ-7QA&expires=1699077939" alt="merge_requests-20219-016b69.png"/></figure>
<p><strong>Определение наличия чувствительных данных в проекте.</strong></p>
<p>Задача поиска утечек паролей, токенов, сертификатов и других чувствительных данных в коде проекта задача не совсем простая — высока вероятность как пропуска реальных данных, так и ложноположительных срабатываний, поэтому этот этап не должен являться блокирующим для выпуска приложения. Он должен быть включен в регламент code review и являться основанием как для выдачи замечаний, так и чистки истории от попавших в репозиторий данных.</p>
<p>Существует несколько утилит для решения такой задачи, но мы пока ограничимся штатным средством, которое предоставляет GitLab, так как не хотим потерять возможности удобного встроенного средства анализа безопасности — Security dashboard и Vulnerability report, тем более что при необходимости мы можем добавить свои правила для поиска чувствительных данных — под капотом для решения этой задачи GitLab использует <a href="https://github.com/gitleaks/gitleaks" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">gitleaks<span class="wpel-icon wpel-image wpel-icon-6"></span></a></p>
<p>Добавим этот этап:</p>
<pre class="wp-block-preformatted">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
</pre>
<p>После запуска конвейера видим, что помимо найденного ранее, обнаружено еще три места, где возможно есть чувствительные данные:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/ed/ff/gitleaks-20219-edffda.png?hash=VOefy854QVl-lJy6tR2rRQ&expires=1699077906" alt="gitleaks-20219-edffda.png"/></figure>
<p>Однако это ложные срабатывания, которые мы можем пометить как *#gitleaks:allow, чтобы эти строки в будущем не учитывались при анализе:</p>
<pre class="wp-block-preformatted">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
</pre>
<p><strong>Проверка зависимостей.</strong></p>
<p>Добавим проверку используемых зависимостей на уязвимости (эта же задача сформирует нам отчет о лицензиях используемых зависимостей):</p>
<pre class="wp-block-preformatted">variables:
LICENSE_MANAGEMENT_VERSION: 4
include:
<strong>-</strong> template: Security/Dependency-Scanning.gitlab-ci.yml
</pre>
<p>В результате работы конвейера получаем список из 21 уязвимости:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/a7/de/depends-20219-a7de43.png?hash=aErJiUICHe2JUtAwyaZUfQ&expires=1699077878" alt="depends-20219-a7de43.png"/></figure>
<p><strong>SAST</strong></p>
<p>Добавим этап SAST:</p>
<pre class="wp-block-preformatted">variables:
SAST_EXPERIMENTAL_FEATURES: "true"
include:
<strong>-</strong> template: Security/SAST.gitlab-ci.yml
</pre>
<p>После запуска конвейера мы получим 104 найденные проблемы SAST:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/5b/e4/sast-20219-5be4d7.png?hash=xyzwcLkUw9-nGuGYr_rK4Q&expires=1699078047" alt="sast-20219-5be4d7.png"/></figure>
<p><strong>Проверка исходного кода линтерами.</strong></p>
<p><strong>flake8, mypy</strong></p>
<p>Эти линтеры уже были добавлены в проект ранее как зависимости, в проекте есть их конфигурационные файлы, однако в конвейерах не использовались. Сделаем так, чтобы пока эти этапы не влияли на конечный результат сборки, однако, в случае наличия проблем, это было визуально видно:</p>
<pre class="wp-block-preformatted">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
</pre>
<p><strong>Сканирование итоговых образов на наличие уязвимостей.</strong></p>
<p>Для реализации этапа сначала вернем в конвейер базовую сборку:</p>
<pre class="wp-block-preformatted">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
</pre>
<p>Для проверки образов также будем использовать уже встроенную возможность GitLab (под капотом там <a href="https://github.com/knqyf263/trivy" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">trivy<span class="wpel-icon wpel-image wpel-icon-6"></span></a> с некоторой оберткой)</p>
<pre class="wp-block-preformatted">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
</pre>
<p>Для такой проверки мы сделаем отдельный этап post_security, который будет запускаться после сборки приложения:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/0f/38/pipeline-20219-0f38f4.png?hash=havL3dYSWFTOhnab5SGmnw&expires=1699077960" alt="pipeline-20219-0f38f4.png"/></figure>
<p>Результат работы этого этапа представлен ниже:</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/db/53/trivy-20219-db53c8.png?hash=iVbJGXJRNn3IMAHY2Z3muw&expires=1699078090" alt="trivy-20219-db53c8.png"/></figure>
<p>Итак, конвейер, содержащий необходимые базовые операции по проверкам безопасности, а также тестированию и базовой сборке образа приложения выглядит так:</p>
<pre class="wp-block-preformatted">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
</pre>
<p>В ходе работы были приняты несколько дополнительных решений:</p>
<ol>
<li>Сделать так, чтобы все эти этапы могли быть переиспользованы в других аналогичных сервисах с минимальными изменениями, или вовсе без них.</li>
<li>Максимально освободить разработчиков от погружения в вопросы DevSecOps в процессе жизненного цикла приложения.</li>
<li>Защитить конвейер от случайных или злонамеренных изменений, внесенных разработчиками, при этом оставив возможность простого управления им.</li>
</ol>
<p>Для внедрения принятых решений мы будем использовать возможность GitLab выполнять включение конвейеров из других репозиториев. Для этого мы перенесем его в другой репозиторий той же группы GitLab, а в нашем проекте выполним его включение:</p>
<pre class="wp-block-preformatted">include:
<strong>-</strong> project: 'services/ci'
ref: master
file: 'python-ci.yml'
</pre>
<p>Таким образом мы решаем первую и вторую поставленные задачи — разработчикам больше нет необходимости думать о настройке CI/CD — им достаточно взять уже готовый файл для размещения в уже существующих проектах, а для новых проектов можно подготовить шаблон, который уже будет содержать все необходимое.</p>
<p>Теперь мы можем выполнить слияние наших изменений в продуктовую ветку, и приступить к решению задачи по защите конвейера от изменений.</p>
<p><strong>Защита итогового конвейера от изменений</strong></p>
<p>На период внедрения и тестирования этапов DevSecOps было принято решение о запрете вмешательства разработчиков в подготовку и работу конвейера. Технически наиболее просто достичь этого можно двумя способами:</p>
<ol>
<li>Используя настройку Settings -> CI/CD -> General pipelines, в которой указать путь к файлу конвейера, например:</li>
</ol>
<pre class="wp-block-preformatted">python-ci.yml@services/ci:master
</pre>
<ol>
<li>Установив блокировку на файл конвейера в проекте пользователем с максимальным уровнем привилегий (на весь этап работы это член команды DevSecOps) — это блокирует его во всех ветках проекта:</li>
</ol>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/49/1a/lock-20219-491a77.png?hash=kvGTTUjy28SRQvokfubRsg&expires=1699077921" alt="lock-20219-491a77.png"/></figure>
<p>Первый вариант нам не подходит, так как мы хотим в будущем сделать вариативный конвейер, управление жизненным циклом которого будет осуществляться с помощью переменных, размещаемых в его файле, поэтому мы будем использовать блокировку файла конвейера, которая в будущем будет снята.</p>
<h4 class="wp-block-heading"><span class="ez-toc-section" id="%D0%9E%D1%86%D0%B5%D0%BD%D0%BA%D0%B0_%D1%80%D0%B5%D0%B7%D1%83%D0%BB%D1%8C%D1%82%D0%B0%D1%82%D0%BE%D0%B2_%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B"></span>Оценка результатов работы.<span class="ez-toc-section-end"></span></h4>
<p>Итогом первой итерации стал конвейер, который показывает, насколько сильно подвержено рискам безопасности приложение.</p>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/a4/a4/result_pipeline-20219-a4a4f1.png?hash=XKNzQzNU5jQzSct1ZLxOAA&expires=1699078025" alt="result_pipeline-20219-a4a4f1.png"/></figure>
<figure class="wp-block-image"><img decoding="async" src="https://cdn.otus.ru/media/private/dd/26/result-20219-dd26cf.png?hash=BBhEsQLTrlBPPUi8QJgYcQ&expires=1699078020" alt="result-20219-dd26cf.png"/></figure>
<p>Также было принято решение отложить выпуск последующих релизов до устранения критических проблем, поэтому этап полной сборки и деплоя продуктового образа пока в конвейере не реализован. Сроки восстановления релизов на данном этапе не ясны — и это хорошо показывает, как халатное отношение к вопросам безопасности может навредить бизнесу.</p>
<h4 class="wp-block-heading"><span class="ez-toc-section" id="4_%D0%9F%D1%80%D0%B5%D0%B4%D0%B2%D0%B0%D1%80%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D0%BB%D0%B0%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D1%81%D0%BB%D0%B5%D0%B4%D1%83%D1%8E%D1%89%D0%B8%D1%85_%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2_%D0%BF%D0%BE_%D0%B2%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D1%8E_DevSecOps_%D0%B2_%D0%B6%D0%B8%D0%B7%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9_%D1%86%D0%B8%D0%BA%D0%BB_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F"></span>4. Предварительное планирование следующих этапов по внедрению DevSecOps в жизненный цикл приложения.<span class="ez-toc-section-end"></span></h4>
<p>Для устранения всех выявленных и недопущения новых недостатков мы можем спланировать следующие этапы нашей работы:</p>
<ol>
<li>Разработка и согласование плана для формирования пула задач, которые будут решаться командами разработки и DevSecOps приоритетно.</li>
<li>Интеграция и использование разработчиками необходимых инструментов безопасности в их локальном окружении.</li>
<li>Более тонкая настройка инструментов (или их замена на более совершенные) для исключения ложных и пропуска настоящих срабатываний, а также внедрение нового инструментария для решения других возникших задач.</li>
<li>Разработка скриптов автоматизации (при необходимости).</li>
<li>Определение и применение блок-факторов безопасности для сборки или выпуска релизов приложения (на данном этапе это критические проблемы для выпуска релизов, тестовые сборки будут продолжаться чтобы не блокировать работу команд разработки полностью).</li>
<li>Внедрение автоматизированных проверок работающих docker-контейнеров на тестовых и продуктовых контурах на наличие открытых портов, правил монтирования файловых систем, установки ограничений используемых ресурсов и привилегий и т.п.</li>
<li>Разделение итогового конвейера на составные части, чтобы этапы безопасности можно было подключать в других схожих проектах, но которые имеют отличающийся регламент разработки и дальнейшее развитие этих этапов.</li>
<li>Отправка сведений об обнаруженных в ходе сборки или работы приложения проблемах безопасности в учетную систему.</li>
</ol>
<h4 class="wp-block-heading"><span class="ez-toc-section" id="%D0%98%D1%82%D0%BE%D0%B3%D0%B8"></span>Итоги.<span class="ez-toc-section-end"></span></h4>
<p>Отсутствие этапов безопасной разработки в проекте в течение полутора лет его существования наглядно показало, к каким последствиям это может привести — конкретно в этом случае это стоило компании нескольких недель простоя, потери ряда клиентов и части репутации из-за произошедших инцидентов.</p>
<p>Из всего произошедшего были сделаны правильные выводы — теперь вопросы информационной безопасности закладываются уже на этапе формирования требований и технических заданий, с привлечением профильных специалистов, включая не только автоматические проверки на этапах сборки и тестирования, но и привлечения сторонних подрядчиков для проведения аудита как приложений, так и инфраструктуры компании.</p>
<p><em>Хотите знать больше про DevSecOps? Добро пожаловать на <a href="https://otus.ru/lessons/devsecops/?utm_source=oj&utm_medium=affilate&utm_campaign=devsec" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">специализированный курс в Otus<span class="wpel-icon wpel-image wpel-icon-6"></span></a>!</em></p>
</div><!-- .post-content -->
<div class="the-post-foot cf">
<div class="tag-share cf">
<div class="post-tags"><a href="https://otus.ru/journal/tag/devsecops/" rel="tag" data-wpel-link="internal">devsecops</a><a href="https://otus.ru/journal/tag/proekt/" rel="tag" data-wpel-link="internal">проект</a></div>
<div class="post-share">
<div class="post-share-icons cf">
<span class="counters">
</span>
<a href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F" class="link facebook wpel-icon-right" target="_blank" title="Share on Facebook" data-wpel-link="external" rel="nofollow external noopener noreferrer"><i class="fa fa-facebook"></i><span class="wpel-icon wpel-image wpel-icon-6"></span></a>
<a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F&text=%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2%20DevSecOps%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%20%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8%2C%20%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B8%20%D0%B8%20%D1%8D%D0%BA%D1%81%D0%BF%D0%BB%D1%83%D0%B0%D1%82%D0%B0%D1%86%D0%B8%D0%B8%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F" class="link twitter wpel-icon-right" target="_blank" title="Share on Twitter" data-wpel-link="external" rel="nofollow external noopener noreferrer"><i class="fa fa-twitter"></i><span class="wpel-icon wpel-image wpel-icon-6"></span></a>
<a href="https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F" class="link linkedin wpel-icon-right" target="_blank" title="LinkedIn" data-wpel-link="external" rel="nofollow external noopener noreferrer"><i class="fa fa-linkedin"></i><span class="wpel-icon wpel-image wpel-icon-6"></span></a>
<a href="https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fotus.ru%2Fjournal%2Fvnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya%2F&media=https%3A%2F%2Fotus.ru%2Fjournal%2Fwp-content%2Fuploads%2F2023%2F11%2Foj-1080x720-1.png&description=%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%8D%D1%82%D0%B0%D0%BF%D0%BE%D0%B2%20DevSecOps%20%D0%B2%20%D1%86%D0%B8%D0%BA%D0%BB%20%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8%2C%20%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B8%20%D0%B8%20%D1%8D%D0%BA%D1%81%D0%BF%D0%BB%D1%83%D0%B0%D1%82%D0%B0%D1%86%D0%B8%D0%B8%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F" class="link pinterest wpel-icon-right" target="_blank" title="Pinterest" data-wpel-link="external" rel="nofollow external noopener noreferrer"><i class="fa fa-pinterest-p"></i><span class="wpel-icon wpel-image wpel-icon-6"></span></a>
</div>
</div>
</div>
</div>
<div class="post-nav">
<div class="post previous cf">
<a href="https://otus.ru/journal/dvizhok-unity3d-opisanie-pljusy-i-minusy/" title="Prev Post" class="nav-icon" data-wpel-link="internal">
<i class="fa fa-angle-left"></i>
</a>
<span class="content">
<a href="https://otus.ru/journal/dvizhok-unity3d-opisanie-pljusy-i-minusy/" class="image-link" rel="previous" data-wpel-link="internal">
<img width="150" height="106" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20150%20106%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="attachment-thumbnail size-thumbnail lazyload wp-post-image" alt="Движок Unity3D: описание, плюсы и минусы" decoding="async" data-srcset="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-150x106.jpg 150w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-300x212.jpg 300w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-1024x724.jpg 1024w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-768x543.jpg 768w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-1536x1086.jpg 1536w" data-src="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7204-1-150x106.jpg" data-sizes="(max-width: 150px) 100vw, 150px" title="Движок Unity3D: описание, плюсы и минусы" /> </a>
<div class="post-meta">
<span class="label">Prev Post</span>
<div class="post-meta post-meta-b">
<h2 class="post-title">
<a href="https://otus.ru/journal/dvizhok-unity3d-opisanie-pljusy-i-minusy/" data-wpel-link="internal">Движок Unity3D: описание, плюсы и минусы</a>
</h2>
<div class="below">
<a href="https://otus.ru/journal/dvizhok-unity3d-opisanie-pljusy-i-minusy/" class="meta-item date-link" data-wpel-link="internal"><time class="post-date" datetime="2023-11-02T21:03:22+00:00">2 ноября, 2023</time></a>
<span class="meta-sep"></span>
<span class="meta-item read-time">4 Mins Read</span>
</div>
</div> </div>
</span>
</div>
<div class="post next cf">
<a href="https://otus.ru/journal/koncepciya-oop-razrabotki/" title="Next Post" class="nav-icon" data-wpel-link="internal">
<i class="fa fa-angle-right"></i>
</a>
<span class="content">
<a href="https://otus.ru/journal/koncepciya-oop-razrabotki/" class="image-link" rel="next" data-wpel-link="internal">
<img width="150" height="106" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20150%20106%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="attachment-thumbnail size-thumbnail lazyload wp-post-image" alt="Концепция ООП-разработки" decoding="async" data-srcset="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-150x106.jpg 150w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-300x212.jpg 300w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-1024x724.jpg 1024w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-768x543.jpg 768w, https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-1536x1086.jpg 1536w" data-src="https://otus.ru/journal/wp-content/uploads/2023/11/oj-1080x7205-1-150x106.jpg" data-sizes="(max-width: 150px) 100vw, 150px" title="Концепция ООП-разработки" /> </a>
<div class="post-meta">
<span class="label">Next Post</span>
<div class="post-meta post-meta-b">
<h2 class="post-title">
<a href="https://otus.ru/journal/koncepciya-oop-razrabotki/" data-wpel-link="internal">Концепция ООП-разработки</a>
</h2>
<div class="below">
<a href="https://otus.ru/journal/koncepciya-oop-razrabotki/" class="meta-item date-link" data-wpel-link="internal"><time class="post-date" datetime="2023-11-04T18:48:00+00:00">4 ноября, 2023</time></a>
<span class="meta-sep"></span>
<span class="meta-item read-time">10 Mins Read</span>
</div>
</div> </div>
</span>
</div>
</div>
<section class="related-posts grid-3">
<h4 class="section-head"><span class="title">Читать ещё</span></h4>
<div class="ts-row posts cf">
<article class="post col-4">
<a href="https://otus.ru/journal/uroven-gotovnosti-cto-k-2026/" title="Уровень готовности CTO к 2026" class="image-link" data-wpel-link="internal">
<img width="270" height="180" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20270%20180%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="image lazyload wp-post-image" alt="Уровень готовности CTO к 2026" title="Уровень готовности CTO к 2026" decoding="async" loading="lazy" data-srcset="https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-3-270x180.jpg 270w, https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-3-770x515.jpg 770w, https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-3-370x245.jpg 370w" data-src="https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-3-270x180.jpg" data-sizes="(max-width: 270px) 100vw, 270px" /> </a>
<div class="content">
<h3 class="post-title"><a href="https://otus.ru/journal/uroven-gotovnosti-cto-k-2026/" class="post-link" data-wpel-link="internal">Уровень готовности CTO к 2026</a></h3>
<div class="post-meta">
<time class="post-date" datetime="2025-11-16T19:50:59+00:00">16 ноября, 2025</time>
</div>
</div>
</article >
<article class="post col-4">
<a href="https://otus.ru/journal/novye-uroki-noyabrya-tolko-top-temy-po-programmirovaniju/" title="Новые уроки ноября: только топ-темы по программированию" class="image-link" data-wpel-link="internal">
<img width="270" height="180" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20270%20180%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="image lazyload wp-post-image" alt="Новые уроки ноября: только топ-темы по программированию" title="Новые уроки ноября: только топ-темы по программированию" decoding="async" loading="lazy" data-srcset="https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-2-270x180.jpg 270w, https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-2-770x515.jpg 770w, https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-2-370x245.jpg 370w" data-src="https://otus.ru/journal/wp-content/uploads/2025/11/oj-1080x720-kopiya-2-270x180.jpg" data-sizes="(max-width: 270px) 100vw, 270px" /> </a>
<div class="content">
<h3 class="post-title"><a href="https://otus.ru/journal/novye-uroki-noyabrya-tolko-top-temy-po-programmirovaniju/" class="post-link" data-wpel-link="internal">Новые уроки ноября: только топ-темы по программированию</a></h3>
<div class="post-meta">
<time class="post-date" datetime="2025-11-09T23:24:11+00:00">9 ноября, 2025</time>
</div>
</div>
</article >
<article class="post col-4">
<a href="https://otus.ru/journal/schjot-idjot-na-chasy/" title="Счёт идёт на часы" class="image-link" data-wpel-link="internal">
<img width="270" height="180" src="data:image/svg+xml,%3Csvg%20viewBox%3D%270%200%20270%20180%27%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%3E%3C%2Fsvg%3E" class="image lazyload wp-post-image" alt="Счёт идёт на часы" title="Счёт идёт на часы" decoding="async" loading="lazy" data-srcset="https://otus.ru/journal/wp-content/uploads/2025/10/oj-1080x720-kopiya-7-270x180.png 270w, https://otus.ru/journal/wp-content/uploads/2025/10/oj-1080x720-kopiya-7-770x515.png 770w, https://otus.ru/journal/wp-content/uploads/2025/10/oj-1080x720-kopiya-7-370x245.png 370w" data-src="https://otus.ru/journal/wp-content/uploads/2025/10/oj-1080x720-kopiya-7-270x180.png" data-sizes="(max-width: 270px) 100vw, 270px" /> </a>
<div class="content">
<h3 class="post-title"><a href="https://otus.ru/journal/schjot-idjot-na-chasy/" class="post-link" data-wpel-link="internal">Счёт идёт на часы</a></h3>
<div class="post-meta">
<time class="post-date" datetime="2025-10-30T15:04:59+00:00">30 октября, 2025</time>
</div>
</div>
</article >
</div>
</section>
</article> <!-- .the-post -->
</div>
<aside class="col-4 sidebar">
<div class="inner">
<ul>
<li id="search-2" class="widget widget_search"><h5 class="widget-title"><span>Поиск по блогу</span></h5>
<form method="get" class="search-form" action="https://otus.ru/journal/">
<label>
<span class="screen-reader-text">Search for:</span>
<input type="search" class="search-field" placeholder="Введите запрос и нажмите Enter" value="" name="s" title="Search for:" />
</label>
<button type="submit" class="search-submit"><i class="fa fa-search"></i></button>
</form>
</li>
<li id="tag_cloud-5" class="widget widget_tag_cloud"><h5 class="widget-title"><span>Метки</span></h5><div class="tagcloud"><a href="https://otus.ru/journal/tag/android-2/" class="tag-cloud-link tag-link-74 tag-link-position-1" style="font-size: 12.472222222222pt;" aria-label="Android (34 элемента)" data-wpel-link="internal">Android</a>
<a href="https://otus.ru/journal/tag/c-3/" class="tag-cloud-link tag-link-91 tag-link-position-2" style="font-size: 10.916666666667pt;" aria-label="C (23 элемента)" data-wpel-link="internal">C</a>
<a href="https://otus.ru/journal/tag/c-2/" class="tag-cloud-link tag-link-81 tag-link-position-3" style="font-size: 12.666666666667pt;" aria-label="C# (35 элементов)" data-wpel-link="internal">C#</a>
<a href="https://otus.ru/journal/tag/c/" class="tag-cloud-link tag-link-20 tag-link-position-4" style="font-size: 12.472222222222pt;" aria-label="c++ (34 элемента)" data-wpel-link="internal">c++</a>
<a href="https://otus.ru/journal/tag/computer-science/" class="tag-cloud-link tag-link-209 tag-link-position-5" style="font-size: 15.972222222222pt;" aria-label="computer science (78 элементов)" data-wpel-link="internal">computer science</a>
<a href="https://otus.ru/journal/tag/css/" class="tag-cloud-link tag-link-288 tag-link-position-6" style="font-size: 8.6805555555556pt;" aria-label="CSS (13 элементов)" data-wpel-link="internal">CSS</a>
<a href="https://otus.ru/journal/tag/data-science/" class="tag-cloud-link tag-link-151 tag-link-position-7" style="font-size: 8pt;" aria-label="Data Science (11 элементов)" data-wpel-link="internal">Data Science</a>
<a href="https://otus.ru/journal/tag/devops/" class="tag-cloud-link tag-link-98 tag-link-position-8" style="font-size: 10.138888888889pt;" aria-label="devops (19 элементов)" data-wpel-link="internal">devops</a>
<a href="https://otus.ru/journal/tag/docker/" class="tag-cloud-link tag-link-143 tag-link-position-9" style="font-size: 8.2916666666667pt;" aria-label="Docker (12 элементов)" data-wpel-link="internal">Docker</a>
<a href="https://otus.ru/journal/tag/gamedev/" class="tag-cloud-link tag-link-25 tag-link-position-10" style="font-size: 11.694444444444pt;" aria-label="gamedev (28 элементов)" data-wpel-link="internal">gamedev</a>
<a href="https://otus.ru/journal/tag/hr/" class="tag-cloud-link tag-link-103 tag-link-position-11" style="font-size: 8pt;" aria-label="hr (11 элементов)" data-wpel-link="internal">hr</a>
<a href="https://otus.ru/journal/tag/html/" class="tag-cloud-link tag-link-217 tag-link-position-12" style="font-size: 11.208333333333pt;" aria-label="HTML (25 элементов)" data-wpel-link="internal">HTML</a>
<a href="https://otus.ru/journal/tag/ios/" class="tag-cloud-link tag-link-101 tag-link-position-13" style="font-size: 8.9722222222222pt;" aria-label="iOS (14 элементов)" data-wpel-link="internal">iOS</a>
<a href="https://otus.ru/journal/tag/it/" class="tag-cloud-link tag-link-50 tag-link-position-14" style="font-size: 10.527777777778pt;" aria-label="IT (21 элемент)" data-wpel-link="internal">IT</a>
<a href="https://otus.ru/journal/tag/java/" class="tag-cloud-link tag-link-75 tag-link-position-15" style="font-size: 15.680555555556pt;" aria-label="Java (73 элемента)" data-wpel-link="internal">Java</a>
<a href="https://otus.ru/journal/tag/javascript/" class="tag-cloud-link tag-link-83 tag-link-position-16" style="font-size: 14.319444444444pt;" aria-label="JavaScript (53 элемента)" data-wpel-link="internal">JavaScript</a>
<a href="https://otus.ru/journal/tag/linux/" class="tag-cloud-link tag-link-141 tag-link-position-17" style="font-size: 11.888888888889pt;" aria-label="Linux (29 элементов)" data-wpel-link="internal">Linux</a>
<a href="https://otus.ru/journal/tag/machine-learning/" class="tag-cloud-link tag-link-167 tag-link-position-18" style="font-size: 8.6805555555556pt;" aria-label="Machine Learning (13 элементов)" data-wpel-link="internal">Machine Learning</a>
<a href="https://otus.ru/journal/tag/otus-book/" class="tag-cloud-link tag-link-261 tag-link-position-19" style="font-size: 9.9444444444444pt;" aria-label="otus book (18 элементов)" data-wpel-link="internal">otus book</a>
<a href="https://otus.ru/journal/tag/php/" class="tag-cloud-link tag-link-45 tag-link-position-20" style="font-size: 10.527777777778pt;" aria-label="PHP (21 элемент)" data-wpel-link="internal">PHP</a>
<a href="https://otus.ru/journal/tag/python/" class="tag-cloud-link tag-link-27 tag-link-position-21" style="font-size: 16.944444444444pt;" aria-label="Python (99 элементов)" data-wpel-link="internal">Python</a>
<a href="https://otus.ru/journal/tag/qa/" class="tag-cloud-link tag-link-155 tag-link-position-22" style="font-size: 11.402777777778pt;" aria-label="qa (26 элементов)" data-wpel-link="internal">qa</a>
<a href="https://otus.ru/journal/tag/sql/" class="tag-cloud-link tag-link-38 tag-link-position-23" style="font-size: 12.861111111111pt;" aria-label="SQL (37 элементов)" data-wpel-link="internal">SQL</a>
<a href="https://otus.ru/journal/tag/team-lead/" class="tag-cloud-link tag-link-364 tag-link-position-24" style="font-size: 9.9444444444444pt;" aria-label="team lead (18 элементов)" data-wpel-link="internal">team lead</a>
<a href="https://otus.ru/journal/tag/unity/" class="tag-cloud-link tag-link-24 tag-link-position-25" style="font-size: 8pt;" aria-label="unity (11 элементов)" data-wpel-link="internal">unity</a>
<a href="https://otus.ru/journal/tag/algoritmy/" class="tag-cloud-link tag-link-30 tag-link-position-26" style="font-size: 9.9444444444444pt;" aria-label="Алгоритмы (18 элементов)" data-wpel-link="internal">Алгоритмы</a>
<a href="https://otus.ru/journal/tag/bazy-dannyh/" class="tag-cloud-link tag-link-40 tag-link-position-27" style="font-size: 10.138888888889pt;" aria-label="Базы данных (19 элементов)" data-wpel-link="internal">Базы данных</a>
<a href="https://otus.ru/journal/tag/matematika/" class="tag-cloud-link tag-link-44 tag-link-position-28" style="font-size: 10.916666666667pt;" aria-label="Математика (23 элемента)" data-wpel-link="internal">Математика</a>
<a href="https://otus.ru/journal/tag/arhitektura-po/" class="tag-cloud-link tag-link-10 tag-link-position-29" style="font-size: 9.4583333333333pt;" aria-label="архитектура ПО (16 элементов)" data-wpel-link="internal">архитектура ПО</a>
<a href="https://otus.ru/journal/tag/bazy-dannyh-2/" class="tag-cloud-link tag-link-251 tag-link-position-30" style="font-size: 10.138888888889pt;" aria-label="базы данных (19 элементов)" data-wpel-link="internal">базы данных</a>
<a href="https://otus.ru/journal/tag/vebinar/" class="tag-cloud-link tag-link-201 tag-link-position-31" style="font-size: 13.930555555556pt;" aria-label="вебинар (48 элементов)" data-wpel-link="internal">вебинар</a>
<a href="https://otus.ru/journal/tag/dajdzhest/" class="tag-cloud-link tag-link-308 tag-link-position-32" style="font-size: 10.722222222222pt;" aria-label="дайджест (22 элемента)" data-wpel-link="internal">дайджест</a>
<a href="https://otus.ru/journal/tag/zapis-vebinara/" class="tag-cloud-link tag-link-226 tag-link-position-33" style="font-size: 14.902777777778pt;" aria-label="запись вебинара (61 элемент)" data-wpel-link="internal">запись вебинара</a>
<a href="https://otus.ru/journal/tag/zapis-uroka/" class="tag-cloud-link tag-link-272 tag-link-position-34" style="font-size: 16.069444444444pt;" aria-label="запись урока (80 элементов)" data-wpel-link="internal">запись урока</a>
<a href="https://otus.ru/journal/tag/informacionnaya-bezopasnost/" class="tag-cloud-link tag-link-232 tag-link-position-35" style="font-size: 10.138888888889pt;" aria-label="информационная безопасность (19 элементов)" data-wpel-link="internal">информационная безопасность</a>
<a href="https://otus.ru/journal/tag/karera-v-it/" class="tag-cloud-link tag-link-292 tag-link-position-36" style="font-size: 9.9444444444444pt;" aria-label="карьера в IT (18 элементов)" data-wpel-link="internal">карьера в IT</a>
<a href="https://otus.ru/journal/tag/podborka/" class="tag-cloud-link tag-link-7 tag-link-position-37" style="font-size: 12.666666666667pt;" aria-label="подборка (35 элементов)" data-wpel-link="internal">подборка</a>
<a href="https://otus.ru/journal/tag/podborka-statej/" class="tag-cloud-link tag-link-219 tag-link-position-38" style="font-size: 15.777777777778pt;" aria-label="подборка статей (75 элементов)" data-wpel-link="internal">подборка статей</a>
<a href="https://otus.ru/journal/tag/programmirovanie/" class="tag-cloud-link tag-link-65 tag-link-position-39" style="font-size: 22pt;" aria-label="программирование (332 элемента)" data-wpel-link="internal">программирование</a>
<a href="https://otus.ru/journal/tag/proekt/" class="tag-cloud-link tag-link-321 tag-link-position-40" style="font-size: 11.888888888889pt;" aria-label="проект (29 элементов)" data-wpel-link="internal">проект</a>
<a href="https://otus.ru/journal/tag/proektnaya-rabota/" class="tag-cloud-link tag-link-310 tag-link-position-41" style="font-size: 11.597222222222pt;" aria-label="проектная работа (27 элементов)" data-wpel-link="internal">проектная работа</a>
<a href="https://otus.ru/journal/tag/seti/" class="tag-cloud-link tag-link-181 tag-link-position-42" style="font-size: 12.958333333333pt;" aria-label="сети (38 элементов)" data-wpel-link="internal">сети</a>
<a href="https://otus.ru/journal/tag/testirovanie/" class="tag-cloud-link tag-link-69 tag-link-position-43" style="font-size: 13.930555555556pt;" aria-label="тестирование (48 элементов)" data-wpel-link="internal">тестирование</a>
<a href="https://otus.ru/journal/tag/upravlenie-komandoj/" class="tag-cloud-link tag-link-63 tag-link-position-44" style="font-size: 11.694444444444pt;" aria-label="управление командой (28 элементов)" data-wpel-link="internal">управление командой</a>
<a href="https://otus.ru/journal/tag/habr-2/" class="tag-cloud-link tag-link-203 tag-link-position-45" style="font-size: 13.930555555556pt;" aria-label="хабр (48 элементов)" data-wpel-link="internal">хабр</a></div>
</li>
</ul>
</div>
</aside>
</div> <!-- .ts-row -->
</div> <!-- .main -->
<footer class="main-footer dark bold">
<section class="lower-footer cf">
<div class="wrap">
<div class="links">
<div class="menu-menju-navykov-container"><ul id="menu-menju-navykov-1" class="menu"><li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10413"><a href="https://otus.ru/categories/programming/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Программирование<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10414"><a href="https://otus.ru/categories/architecture/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Архитектура<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10415"><a href="https://otus.ru/categories/operations/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Инфраструктура<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10416"><a href="https://otus.ru/categories/information-security-courses/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Безопасность<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10417"><a href="https://otus.ru/categories/data-science/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Data Science<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10418"><a href="https://otus.ru/categories/gamedev/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">GameDev<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10419"><a href="https://otus.ru/categories/marketing-business/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Управление<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10420"><a href="https://otus.ru/categories/analytics/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Аналитика и анализ<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-10421"><a href="https://otus.ru/categories/testing/" data-wpel-link="external" target="_blank" rel="nofollow external noopener noreferrer" class="wpel-icon-right">Тестирование<span class="wpel-icon wpel-image wpel-icon-6"></span></a></li>
</ul></div> </div>
<p class="copyright"> © 2015-2026 OTUS </p>
<div class="to-top">
<a href="#" class="back-to-top"><i class="fa fa-angle-up"></i> Top</a>
</div>
</div>
</section>
</footer>
</div> <!-- .main-wrap -->
<div class="mobile-menu-container off-canvas" id="mobile-menu">
<a href="#" class="close"><i class="fa fa-times"></i></a>
<div class="logo">
</div>
<ul class="mobile-menu"></ul>
</div>
<div class="search-modal-wrap">
<div class="search-modal-box" role="dialog" aria-modal="true">
<form method="get" class="search-form" action="https://otus.ru/journal/">
<input type="search" class="search-field" name="s" placeholder="Search..." value="" required />
<button type="submit" class="search-submit visuallyhidden">Submit</button>
<p class="message">
Type above and press <em>Enter</em> to search. Press <em>Esc</em> to cancel. </p>
</form>
</div>
</div>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/clearfy/components/comments-plus/assets/js/url-span.js" id="wbcr-comments-plus-url-span-js"></script>
<script type="text/javascript" id="ez-toc-scroll-scriptjs-js-extra">
/* <![CDATA[ */
var eztoc_smooth_local = {"scroll_offset":"30"};
/* ]]> */
</script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/easy-table-of-contents/assets/js/smooth_scroll.min.js" id="ez-toc-scroll-scriptjs-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/easy-table-of-contents/vendor/js-cookie/js.cookie.min.js" id="ez-toc-js-cookie-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/easy-table-of-contents/vendor/sticky-kit/jquery.sticky-kit.min.js" id="ez-toc-jquery-sticky-kit-js"></script>
<script type="text/javascript" id="ez-toc-js-js-extra">
/* <![CDATA[ */
var ezTOC = {"smooth_scroll":"1","visibility_hide_by_default":"","scroll_offset":"30","fallbackIcon":"<span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span>"};
/* ]]> */
</script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/plugins/easy-table-of-contents/assets/js/front.min.js" id="ez-toc-js-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/custom-script.js" id="custom-script-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/magnific-popup.js" id="magnific-popup-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/jquery.fitvids.js" id="jquery-fitvids-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-includes/js/imagesloaded.min.js" id="imagesloaded-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/object-fit-images.js" id="object-fit-images-js"></script>
<script type="text/javascript" id="contentberg-theme-js-extra">
/* <![CDATA[ */
var Bunyad = {"custom_ajax_url":"\/journal\/vnedrenie-etapov-devsecops-v-cikl-razrabotki-postavki-i-ekspluatacii-prilozheniya\/"};
/* ]]> */
</script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/theme.js" id="contentberg-theme-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/theia-sticky-sidebar.js" id="theia-sticky-sidebar-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/jquery.slick.js" id="jquery-slick-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-content/themes/contentberg/js/jarallax.js" id="jarallax-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-includes/js/masonry.min.js" id="masonry-js"></script>
<script type="text/javascript" src="https://otus.ru/journal/wp-includes/js/jquery/jquery.masonry.min.js" id="jquery-masonry-js"></script>
</body>
</html>
<!-- Cache served by breeze CACHE - Last modified: Tue, 10 Mar 2026 16:27:11 GMT -->