Как встроить проверки безопасности в CI/CD пайплайн?
Security gates — обязательные проверки безопасности в пайплайне, которые блокируют продвижение артефакта при обнаружении проблем. Принцип shift-left security подразумевает выявление уязвимостей как можно раньше — на этапе написания кода и сборки, а не после деплоя в production.
Полный безопасный пайплайн (Jenkinsfile):
Пример
pipeline {
agent any
environment {
REGISTRY = 'registry.bank.local'
IMAGE_NAME = 'banking-service'
IMAGE_TAG = "${BUILD_NUMBER}-${GIT_COMMIT.take(8)}"
}
stages {
// ===== Stage 1: Статический анализ кода (SAST) =====
stage('SAST - Static Analysis') {
parallel {
stage('SonarQube') {
steps {
sh '''
./mvnw sonar:sonar \
-Dsonar.host.url=${SONAR_URL} \
-Dsonar.token=${SONAR_TOKEN} \
-Dsonar.qualitygate.wait=true
'''
}
}
stage('SpotBugs + FindSecBugs') {
steps {
sh './mvnw spotbugs:check'
}
}
stage('Secret Detection') {
steps {
// Поиск случайно добавленных секретов в исходном коде
sh 'trivy fs --scanners secret --exit-code 1 .'
}
}
}
}
// ===== Stage 2: Проверка зависимостей (SCA) =====
stage('SCA - Dependency Check') {
steps {
sh '''
./mvnw dependency-check:check \
-DfailBuildOnCVSS=7 \
-Dformat=JSON \
-DprettyPrint=true
'''
}
post {
always {
archiveArtifacts 'target/dependency-check-report.json'
}
}
}
// ===== Stage 3: Сборка и тесты =====
stage('Build & Test') {
steps {
sh './mvnw clean verify'
}
}
// ===== Stage 4: Сборка Docker-образа =====
stage('Docker Build') {
steps {
sh """
docker build \
--no-cache \
--label "org.opencontainers.image.revision=${GIT_COMMIT}" \
--label "org.opencontainers.image.created=\$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-t ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} .
"""
}
}
// ===== Stage 5: Сканирование образа =====
stage('Image Security Scan') {
parallel {
stage('Trivy Scan') {
steps {
sh """
trivy image \
--exit-code 1 \
--severity CRITICAL,HIGH \
--ignore-unfixed \
--format json \
--output trivy-report.json \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
"""
}
}
stage('Dockerfile Lint') {
steps {
sh 'hadolint Dockerfile --failure-threshold warning'
}
}
stage('SBOM Generation') {
steps {
sh """
trivy image \
--format cyclonedx \
--output sbom.cdx.json \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
"""
}
}
}
}
// ===== Stage 6: Проверка Kubernetes-манифестов =====
stage('IaC Security') {
steps {
sh '''
# Проверка Kubernetes манифестов на мисконфигурации
trivy config --exit-code 1 --severity CRITICAL,HIGH k8s/
# Дополнительная проверка с помощью kubesec
kubesec scan k8s/deployment.yaml
'''
}
}
// ===== Stage 7: Push и подпись образа =====
stage('Push & Sign') {
steps {
sh """
docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
# Подпись образа с помощью Cosign
cosign sign --key \${COSIGN_KEY} \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
# Прикрепление SBOM к образу в реестре
cosign attach sbom \
--sbom sbom.cdx.json \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
"""
}
}
// ===== Stage 8: Динамическое тестирование (DAST) =====
stage('DAST - Dynamic Testing') {
when {
branch 'develop'
}
steps {
sh '''
# Деплой в тестовую среду
kubectl apply -f k8s/ -n testing
# Запуск OWASP ZAP для динамического сканирования
docker run --rm owasp/zap2docker-stable zap-baseline.py \
-t http://banking-service.testing.svc:8080 \
-r zap-report.html
'''
}
}
}
post {
always {
archiveArtifacts artifacts: '*.json, *.html', allowEmptyArchive: true
}
failure {
// Уведомление команды безопасности о провале security gate
slackSend channel: '#security-alerts',
color: 'danger',
message: "Security gate FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
Пайплайн выстроен так, что каждый последующий этап выполняется только при успехе предыдущего. Если на любом этапе обнаружена критическая проблема (--exit-code 1), пайплайн останавливается и образ не попадает в реестр. Параллельные этапы (parallel) ускоряют выполнение, сохраняя полноту проверок.
Сводная таблица типов проверок безопасности:
| Тип | Инструменты | Что проверяет | Когда запускать |
|---|---|---|---|
| SAST (Static Application Security Testing) | SonarQube, SpotBugs + FindSecBugs, Semgrep | Уязвимости в исходном коде: SQL injection, XSS, небезопасная десериализация | При каждом коммите |
| SCA (Software Composition Analysis) | OWASP Dependency Check, Snyk | Уязвимости в сторонних зависимостях (библиотеках) | При сборке |
| Secret Scan | Trivy, GitLeaks, TruffleHog | Случайно добавленные секреты: пароли, ключи API, токены в коде и Git-истории | При каждом коммите |
| Image Scan | Trivy, Snyk, Grype | Уязвимости в Docker-образе: пакеты ОС и зависимости приложения | После сборки образа |
| IaC Scan | Trivy config, Checkov, kubesec | Мисконфигурации: Kubernetes-манифесты, Dockerfile, Terraform | При каждом коммите |
| DAST (Dynamic Application Security Testing) | OWASP ZAP | Уязвимости работающего приложения: проверка HTTP-эндпоинтов, заголовков, аутентификации | После деплоя в тестовую среду |
| Lint | hadolint, yamllint | Стилистические и безопасностные проблемы в Dockerfile и YAML | При каждом коммите |
Ключевой принцип: пайплайн должен блокировать продвижение артефакта при обнаружении критических уязвимостей. Для банковских систем ни одна CRITICAL-уязвимость не должна попасть в production. Параметр --exit-code 1 в сканерах обеспечивает это: при обнаружении уязвимости указанного severity процесс завершается с ненулевым кодом, и CI/CD-система считает этап провалившимся.