← [Раздел](README.md) · [Главная](../README.md)

# Безопасность в GitLab CI

## Цель

Настроить типовой **security pipeline** в GitLab CI: сканирование секретов, SAST, проверка зависимостей и контейнерного образа с блокировкой merge при критических находках.

## Предварительно

- Репозиторий в GitLab (или учебный fork).
- Базовое понимание `.gitlab-ci.yml`: `stages`, `script`, `artifacts`.

## Время

~60 минут (чтение + копирование примера в тестовый проект).

## Этапы пайплайна

Рекомендуемый порядок **до** сборки образа:

| Stage | Job | Инструмент |
|-------|-----|------------|
| validate | `secret_detection` | Gitleaks / встроенный GitLab |
| test | `sast` | Semgrep |
| test | `dependency_scan` | Trivy fs / OWASP Dependency-Check |
| build | `docker_build` | docker/kaniko |
| scan | `container_scan` | Trivy image |
| deploy | `deploy_staging` | только если scan passed |

Раннее обнаружение дешевле: секрет в коммите лучше остановить за секунды, чем ротировать ключ в prod.

## Пример `.gitlab-ci.yml`

```yaml
stages:
  - validate
  - test
  - build
  - scan

variables:
  SECURE_LOG_LEVEL: info

secret_detection:
  stage: validate
  image:
    name: zricethezav/gitleaks:latest
    entrypoint: [""]
  script:
    - gitleaks detect --source . --no-git -v --redact
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

semgrep-sast:
  stage: test
  image: returntocorp/semgrep
  script:
    - semgrep scan --config auto --error --json -o semgrep.json .
  artifacts:
    when: always
    paths:
      - semgrep.json
    expire_in: 30 days

trivy-fs:
  stage: test
  image: aquasec/trivy:latest
  script:
    - trivy fs --severity HIGH,CRITICAL --exit-code 1 .
  allow_failure: false

build-image:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

trivy-image:
  stage: scan
  image: aquasec/trivy:latest
  needs: [build-image]
  script:
    - trivy image --severity CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    reports:
      container_scanning: gl-container-scanning-report.json
```

Плейсхолдеры: `$CI_REGISTRY_IMAGE` подставляет GitLab. Реальные токены в файл **не** кладём.

## Встроенные шаблоны GitLab

GitLab Ultimate включает готовые jobs через `include`:

```yaml
include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Secret-Detection.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml
```

На Free tier используйте контейнерные образы инструментов, как в примере выше.

## Merge Request policies

1. **Protected branches** — push только через MR.
2. **Approval rules** — минимум 1 approver для `main`.
3. **Pipeline must succeed** — без зелёного пайплайна merge запрещён.
4. **Security scanning** — отдельный статус для `trivy-image`.

В GitLab 16+ смотрите **Scan Result Policies** (premium): блок MR при Critical в diff.

## Переменные и секреты CI

| Плохо | Хорошо |
|-------|--------|
| `API_KEY=sk-live-xxx` в Variables без маски | Masked + Protected variable |
| Секрет в `.gitlab-ci.yml` | CI/CD Variables или Vault integration |
| Один токен на всё | Project token с минимальными scope |

Интеграция с Vault: job получает short-lived token через JWT (`CI_JOB_JWT` / OIDC).

## Артефакты для аудита

Сохраняйте отчёты 30–90 дней:

- `semgrep.json`
- `gl-container-scanning-report.json`
- лог Gitleaks (без содержимого секретов — флаг `--redact`)

Связь с [audit-evidence.md](../09-compliance/audit-evidence.md).

## Типичные проблемы

| Симптом | Решение |
|---------|---------|
| Gitleaks находит тестовый `password123` | `gitleaks.toml` allowlist для `testdata/` |
| Trivy валит legacy CVE | `.trivyignore` с тикетом и сроком пересмотра |
| Долгий Semgrep | `--include` только `src/` |
| DinD небезопасен | Kaniko или buildkit с rootless |

## Самопроверка

1. В каком stage должен идти Gitleaks и почему?
2. Чем `trivy fs` отличается от `trivy image`?
3. Какие три настройки MR защищают `main` без участия SAST?
4. Где хранить `DATABASE_URL` для deploy job?

## Дальше

[GitHub Actions — security](github-actions-security.md) — те же идеи на другой платформе.
