Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як сканувати Docker образи на вразливості?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Бери Trivy, Grype або Docker Scout** у CI для сканування image на відомі CVE. Завали білд на HIGH/CRITICAL severity. Крути періодичні re-scan, бо нові CVE розкриваються проти тих самих image-байтів щоденно. ```bash trivy image --severity HIGH,CRITICAL --exit-code 1 myorg/api:1.0 grype myorg/api:1.0 docker scout cves myorg/api:1.0 ``` **Головне:** scan при build-time (gate pipeline), при admission (gate deploy) і continuously (ловити нові CVE у вже задеплоєних image). Розрізняй OS-level CVE (фікс через base-image bump) і app-level CVE (фікс через dependency-update).Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Vulnerability-сканування** відповідає на «чи мої image містять відомі security-діри?» Сучасний tooling робить це near-zero-effort кроком у CI. Скіпання означає шиппінг відомих CVE у прод. ## Теорія ### TL;DR - Scanner інспектує встановлені пакети image (OS + мова) і матчить їх проти CVE-баз (NVD, vendor-advisory, GitHub Security Advisories). - **Три популярні tools:** Trivy (Aqua), Grype (Anchore), Docker Scout (Docker Inc). - Severities: LOW, MEDIUM, HIGH, CRITICAL. Більшість команд gate на HIGH+ або CRITICAL. - **Три моменти сканування:** build-time (CI gate), admission-time (cluster gate), continuously (ловити нові CVE у вже-running image). - **Дві CVE-категорії:** OS-level (фікс через base-image update) і app-level (фікс через package-update). - Завжди парь з **`.trivyignore`** або еквівалентом для задокументованих винятків. ### Trivy (найпопулярніший) ```bash # Scan локального image trivy image myorg/api:1.0 # Завали на HIGH або CRITICAL trivy image --severity HIGH,CRITICAL --exit-code 1 myorg/api:1.0 # Ігнорувати unfixed CVE (немає patch, чекаємо upstream) trivy image --ignore-unfixed myorg/api:1.0 # Формати output trivy image --format json myorg/api:1.0 trivy image --format sarif --output report.sarif myorg/api:1.0 ``` Trivy opinionated і швидкий. Вбудовані CI-флаги. Дефолт severity це усе; прод-CI зазвичай фільтрує на HIGH+. ### Grype ```bash grype myorg/api:1.0 grype --fail-on high myorg/api:1.0 ``` Від Anchore. Трохи інша DB; іноді ловить, що Trivy пропускає (і навпаки). Хороший для second-opinion-сканів. ### Docker Scout ```bash docker scout cves myorg/api:1.0 docker scout recommendations myorg/api:1.0 # пропонує base-image-upgrade для фіксу CVE ``` Scanner Docker Inc. Тісно інтегровано з Docker CLI і Docker Hub. Команда `recommendations` унікальна і корисна: каже точно, який base-image bump фіксить які CVE. ### CI-інтеграція ```yaml # GitHub Actions з Trivy - name: Build image run: docker build -t myorg/api:${{ github.sha }} . - name: Scan with Trivy uses: aquasecurity/trivy-action@master with: image-ref: myorg/api:${{ github.sha }} severity: 'HIGH,CRITICAL' exit-code: '1' ignore-unfixed: true - name: Upload SARIF to GitHub if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' ``` Заливає у GitHub Code Scanning-tab, вразливості з'являються поряд з рештою code-quality сигналів. ### Multi-stage scan під час білду ```dockerfile # syntax=docker/dockerfile:1.7 FROM golang:1.23-alpine AS build # ... build app ... FROM aquasec/trivy:0.59.0 AS scan COPY --from=build / /scan-target RUN trivy filesystem --severity HIGH,CRITICAL --exit-code 1 /scan-target FROM alpine:3.21 COPY --from=build /out/server /server ENTRYPOINT ["/server"] ``` Стейдж `scan` крутиться як частина білду. Білд падає, якщо HIGH/CRITICAL CVE знайдено. Не може бути скіпнуто careless CI-config. ### Admission-time-сканування Kubernetes admission-controllers можуть відмовити деплою unscanned або vulnerable image: - **OPA Gatekeeper / Kyverno** з політиками, що перевіряють Cosign-attestation scan-результатів. - **Trivy Operator** для K8s — continuously сканує running-image. - **Cosign + sigstore-policy-controller** — верифікує, що image було підписано *і* attested як scanned. Pipeline: CI підписує attestation («це image було scanned at time X, found N HIGH CVE»); admission верифікує attestation і перевіряє policy. ### Continuous re-scanning CVE, розкритий сьогодні, впливає на image, що ти зібрав минулого тижня. Байти ті самі; threat-landscape змінився. ```bash # Cron-driven re-scan у проді 0 6 * * * trivy image --severity HIGH,CRITICAL myorg/api:current 2>&1 | mail security@example.com ``` Або continuous-scanner (Trivy Operator, Snyk Container, Sysdig Secure), що стежить за running-image і re-scan'ить періодично. ### OS vs app CVE Результати зазвичай падають у дві групи: **OS-level** (`alpine:3.20` → CVE у `libssl`): - Фікс: bump base-image (`alpine:3.21`), rebuild. - Або чекай, поки OS-vendor опублікує patched-version. **App-level** (`requests==2.30.0` → CVE у requests): - Фікс: update залежності у твоєму `requirements.txt` / `package.json` / `go.mod`. - Часто бере довше, якщо нова версія має breaking changes. Sканери виявляють обидва. Шлях фіксу різний. ### Типові помилки **Сканування без gating** ```yaml - run: trivy image myorg/api:${{ github.sha }} || true # ← завжди проходить ``` Результати показано, але не валять білд. Деви ігнорують. Через місяці у тебе сотні HIGH CVE у проді. **Завжди постав `--exit-code 1` на реальний severity-поріг.** **Сканування лише при build-time** HIGH CVE, розкритий минулої ночі, зараз у твоєму прод-image. Твій CI пройшов учора; реальність змінилася за ніч. Додай nightly re-scan running-image. **Не підтримувати `.trivyignore`** Реальні CVE іноді не мають patch («won't fix»). Або толеруєш їх, або замінюєш пакет. Документуй рішення у `.trivyignore`: ``` # .trivyignore CVE-2023-12345 # Нема patch, mitigated через network-policy. Re-evaluate quarterly. ``` **Довіряти одному scanner** Різні scanner мають різні DB. CVE у твоїй image-базі може бути відсутній в одного scanner. Для high-stakes роботи крути два scanner і union результати. **Ігнорувати SBOM** Scanner-результат без SBOM це просто «ці CVE існують». З SBOM (Software Bill of Materials) у тебе точний список того, що в image, queryable пізніше: «які image містять log4j?». Генеруй SBOM під час білду (`docker buildx build --sbom=true`), зберігай з image. ### Реальний setup #### Мінімум - Trivy у CI, gate на HIGH/CRITICAL. - `.trivyignore` для задокументованих винятків. - Готово. #### Зрілий - Trivy у CI + scan-стейдж у Dockerfile. - SBOM-генерація + зберігання. - Cosign-attestation scan-результатів. - Admission-controller верифікує attestation. - Nightly re-scan running-image. - Trivy Operator на K8s. - Findings → Slack-канал + ticket-створення. ### Питання для поглиблення **Q:** Яка різниця між CVE і vulnerability? **A:** CVE (Common Vulnerabilities and Exposures) це публічний ID для відомого security-issue. Vulnerability це лежачий баг. Більшість сучасних scanner репортять по CVE-ID, з severity, scored через CVSS. **Q:** Чи scanner може знайти zero-day? **A:** Ні, вони знають лише про *публічно розкриті* вразливості. Zero-day за визначенням ще не відомі. Сканування це один шар; доповни runtime-monitoring, sandbox-testing і code-review. **Q:** Що таке SBOM і навіщо генерувати? **A:** Software Bill of Materials — список кожного пакета і версії у твоєму image. SPDX або CycloneDX формат. З SBOM можеш пізніше відповісти «які з моїх image містять $vulnerable_package» без re-pull і re-scan. **Q:** Як обробляти CVE без доступного фіксу? **A:** Документуй у `.trivyignore` з обґрунтуванням і re-evaluation-датою. Mitigate через runtime-control (network-policy, capability-drops). Re-check quarterly. **Q:** (Senior) Як побудувати scanning-pipeline, що запобігає регресіям? **A:** Три gate: (1) CI-build падає на новий HIGH/CRITICAL — навіть fixing-an-unrelated-bug PR не може merge, якщо вводить новий CVE. (2) Admission-control відкидає image без свіжого підписаного scan-attestation. (3) Прод runtime-scanner alert'ить на нові CVE проти running-image і тригерить remediation-workflow (auto-PR до bump base-image, ticket до dev-команди). Результат: будь-який CVE, що дістав прод, був або свідомо проігнорований (у `.trivyignore` з sign-off), або розкритий після deploy з tracked-response-window. ## Приклади ### Trivy у GitHub Actions ```yaml name: ci on: [push] jobs: build-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: docker build -t myorg/api:${{ github.sha }} . - name: Trivy scan (gate) uses: aquasecurity/trivy-action@master with: image-ref: myorg/api:${{ github.sha }} severity: 'HIGH,CRITICAL' ignore-unfixed: true exit-code: '1' - name: Trivy scan (full report, ніколи не падає) if: always() uses: aquasecurity/trivy-action@master with: image-ref: myorg/api:${{ github.sha }} format: 'sarif' output: 'trivy-results.sarif' severity: 'LOW,MEDIUM,HIGH,CRITICAL' - name: Upload SARIF if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' ``` Два проходи: gating на HIGH+, повний звіт залито для видимості. ### Multi-stage з вбудованим scan ```dockerfile # syntax=docker/dockerfile:1.7 FROM golang:1.23-alpine AS build WORKDIR /src COPY . . RUN CGO_ENABLED=0 go build -o /out/server ./cmd/server FROM aquasec/trivy:0.59.0 AS scan COPY --from=build /out /scan RUN trivy filesystem --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed /scan FROM scratch COPY --from=build /out/server /server USER 65532:65532 ENTRYPOINT ["/server"] ``` `docker build` падає, якщо HIGH/CRITICAL CVE знайдено. Можна обійти через `--target` для runtime-стейджу при потребі, але дефолтний build-шлях enforce'ить gate. ### Порівняти два scanner ```bash #!/bin/bash IMG=$1 echo "=== Trivy ===" trivy image --severity HIGH,CRITICAL $IMG --no-progress --quiet echo "=== Grype ===" grype $IMG --output table --quiet ``` Різні scanner flag'ують різні речі. Для high-stakes image union це твоя true risk-картина.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.