Сканування вразливостей Docker: чому довіряти, а що — шум

Було корисно?

Ви запустили скан контейнера і він кричить: «327 вразливостей, 14 критичних.»
Тим часом сервіс — це крихітний бінарний Go, без shell, без пакетного менеджера і він навіть не відкриває вхідний порт.
Сканер усе одно панікує. Ваша команда безпеки вимагає тікет. Ви на чергуванні хочете спати.

Сканування вразливостей контейнерів корисне — якщо його сприймати як панель приладів, а не як пророцтво.
Ось як відокремити сигнал від шуму, вирішити, що виправляти, і побудувати робочий процес, що витримає контакт з продакшеном.

Що сканери насправді знають (а чого не знають)

Більшість сканерів вразливостей Docker працюють шляхом інспекції вмісту образу та зіставлення знайденого з базами вразливостей.
Це звучить просто, поки ви не згадаєте, що таке контейнерний образ: купа файлів плюс метадані шарів, збудована в екосистемі, яка любить абстракції.
Сканери не «розуміють ваш додаток». Вони виводять висновки, співставляють і припускають.

Що сканери роблять добре

  • Виявлення відомих вразливих версій пакетів в менеджерах пакетів ОС (dpkg, rpm, apk) та у поширених мовних екосистемах.
  • Виділення застарілих базових образів, коли upstream випустив виправлення, а ви не перестроїли образ.
  • Інвентаризація компонентів (поведінка, схожа на SBOM), щоб припинити суперечки про те, що «в образі».
  • Пошук очевидних фатальних помилок: вбудовані приватні ключі, файли з правами запису для всіх або несподіваний SSH-сервер — залежно від інструмента.

Де сканери рутинно вводять в оману (або принаймні mislead)

  • Досяжність: вразливість у бібліотеці, яка присутня, але ніколи не завантажується, все одно буде виявлена.
  • Бекпорти: enterprise-дистрибутиви патчать вразливості без зміни версій у спосіб, якого очікує наївне зіставляння.
  • Неоднозначність «виправлено в»: сканер каже «виправлено в 1.2.3», але ваш дистро постачає «1.2.2-ubuntu0.4» з бекпортованим патчем.
  • Оціночна серйозність без контексту: CVSS припускає загальне розгортання. Ваш контейнер може бути non-root, без мережі, з rootfs лише для читання, із seccomp. CVSS це не бачить.
  • Примарні пакети: залежності під час збірки, що залишилися у фінальному шарі, бо хтось забув про багатоступінчасті збірки.

Сканування вразливостей — необхідний генератор сигналів, а не калькулятор ризику. Розрахунок ризику — ваша робота.
А точніше: ваша задача — створити систему, яка обчислює ризик послідовно, щоб люди не імпровізували о 2-й ночі.

Одна цитата, яку я тримаю в голові, від John Allspaw: «Автоматизація має бути примножувачем сил, а не джерелом таємниць.»

Факти та історичний контекст (чому цей безлад існує)

Якщо сканування вразливостей відчувається як суперечка з дуже впевненою електронною таблицею — це тому, що це так. Трохи контексту допоможе передбачити режими відмов.

  1. CVE почалося в 1999 як система найменувань, щоб вендори могли говорити про одну й ту саму помилку без плутанини.
  2. CVSS v2 (2005) і v3 (2015) стандартизували оцінку серйозності, але це все ще абстрактна модель, яка не бачить ваших рантайм-запобіжників.
  3. Контейнери популяризували «незмінну інфраструктуру», але багато команд іще поводяться з образами як з мутованими віртуалками: патчать всередині, відправляють, забувають.
  4. Дистрибутиви бекпортять виправлення як політику (Debian, Ubuntu, RHEL), що ламає просту логіку «версія < виправлена_версія».
  5. Екосистема musl libc у Alpine змінила ландшафт залежностей; менші образи, але інша сумісність і іноді інша історія вразливостей.
  6. Heartbleed (2014) навчив усіх: «просто оновити OpenSSL» — не стратегія, коли ви навіть не знаєте, де OpenSSL присутній.
  7. Log4Shell (2021) запровадив SBOM у корпоративний мейнстрім, бо люди почали сканувати все, щоб знайти, де лежить JAR.
  8. Executive Order 14028 (2021) прискорив вимоги до SBOM і очікування «доведіть, що всередині» у закупівельних процесах.
  9. З’явився VEX, бо SBOM без контексту «чи експлуатується це?» створює втомлення від алертів у промислових масштабах.

Факт №10, неофіційний але болісно реальний: продавці інструментів безпеки виявили, що «більше знаходжень» часто продається краще, ніж «більш точні знаходження».
Це ринкова реальність, не змова.

Практична модель довіри: три рівні істини

Коли ви тріажуєте знахідки, потрібна ієрархія. Не для філософії — щоб перестати витрачати час на дрібниці.

Рівень 1: Що в образі (істина інвентарю)

Це те, що сканери зазвичай роблять добре: перераховують пакети, бібліотеки й іноді файли.
Перше питання не «чи це критично?» — перше питання «чи це насправді там?»

Сигнали довіри:

  • Результати, отримані з метаданих менеджера пакетів ОС (dpkg/rpm/apk), а не здогадками за іменами файлів.
  • Генерація SBOM з контрольними сумами та ідентифікаторами пакетів (purl, CPE, SPDX).
  • Відтворювані збірки або принаймні детерміністичні Dockerfile, щоб ви могли підтвердити: скан — це саме той артефакт, що ви думаєте.

Рівень 2: Чи вразливе це в цьому дистро/екосистемі (істина advisory)

CVE — це не нотатка про патч. Це ярлик.
«Вразливе» залежить від статусу патчів дистрибутива, бекпортів, прапорів компіляції, вимкнених функцій і рішень вендора.

Сигнали довіри:

  • Трекери безпеки дистро та вендорні advisories, на які посилається сканер, а не лише NVD.
  • Урахування бекпортів: зіставлення рядків релізів пакетів дистро, а не лише upstream semver.
  • Інструменти, що розрізняють «не виправлено», «виправлено», «вразливе, але пом’якшено» та «не зачіпає».

Рівень 3: Чи експлуатується це у вашому рантаймі (істина реальності)

Експлуатованість — це те, де сканування зупиняється, а починається операційна діяльність:
мережеве експонування, привілеї, доступність файлової системи для запису, секрети у змінних середовища, загартування ядра, контролі egress та фактичні шляхи виконання коду.

Сигнали довіри:

  • Відома конфігурація рантайму: користувацькі ID, Linux capabilities, seccomp/apparmor, read-only rootfs, no-new-privileges.
  • Відоме експонування: які порти досяжні, правила ingress, політика сервісної сітки, обмеження egress.
  • Аналіз досяжності на основі доказів (SCA з графами викликів) для мовних залежностей, де це можливо.

Якщо ваш пайплайн трактує Рівень 1 як Рівень 3, ви потонете в алертах і все одно отримаєте влучення чимось нудним.

Поширені категорії шуму (і коли це перестає бути шумом)

1) «Невиправлені» CVE в пакетах базового образу, які ви не використовуєте

Приклад: ваш образ містить libxml2 як транзитивну залежність, сканер позначає CVE, але ваш додаток ніколи не парсить XML.
Це часто «прийнятний ризик», а не «ігнорувати назавжди».

Коли це стає реальним:

  • Вразлива бібліотека використовується системними компонентами, які ви експонуєте (nginx, curl, git, виклики пакетного менеджера).
  • Контейнер запускається з привілеями або ділиться неймспейсами хоста.
  • Бібліотека часто використовуються через несподівані шляхи (наприклад, бібліотеки обробки зображень через функцію завантаження користувачем).

2) Псевдопозитиви через бекпорти

Debian та Ubuntu часто постачають патчі без підвищення номера версії до upstream-версії виправлення.
Сканер, що порівнює лише upstream-версії, стверджуватиме, що ви вразливі, коли це не так.

Коли це стає реальним:

  • Ви на дистро без культури бекпортів або використовуєте upstream-бінарі.
  • Вендор пакету явно позначає його як уражений і не виправлений.

3) CVE в мовних залежностях без досяжних шляхів виконання

JavaScript, Java, Python — екосистеми, де граф залежностей може бути невеликим лісовим вогнем.
Сканери відмічають бібліотеки, присутні в node_modules, навіть якщо код ніколи не імпортується під час виконання.

Коли це стає реальним:

  • Ви відправляєте повне дерево залежностей у продакшен (поширено в Node-образах).
  • Є динамічне завантаження/рефлексія, що ускладнює аналіз досяжності.
  • У вас є вхідні дані, контрольовані користувачем, які можуть потрапити в рідкісні парсери.

4) CVE, що вимагають локального доступу користувача, у контейнері, що працює як non-root

Багато «локальних ескалацій привілеїв» все ще релевантні, якщо нападник може виконувати код у вашому контейнері.
Якщо вони вже мають виконання коду — у вас уже поганий день.
Але LPE важливий, бо може стати шляхом до втечі з контейнера або бокового переміщення.

5) CVE ядра, що повідомляються щодо образів

Контейнери ділять ядро хоста. Якщо сканер повідомляє про CVE ядра «в образі», ставтесь до цього принаймні інформаційно.
Виправляти потрібно на вузлах хоста, а не в Dockerfile.

Жарт №1: Оцінки CVSS як прогноз погоди — корисні, але якщо ви плануєте операцію, спираючись лише на них, у вас буде багато паперів.

Фреймворк тріажу, що працює під тиском

Ось робочий процес, який я рекомендую командам. Він не ідеальний. Він послідовний, а послідовність краща за героїзм.

Крок A: Підтвердіть, що ви сканували правильний артефакт

Ви б подумали, що це очевидно. Ні.
Люди сканують :latest, або сканують локальну збірку, тоді як у продакшені працює старіший digest.
Завжди прив’язуйте знахідки до незмінного digest.

Крок B: Зведіть до «експлуатується + експоновано + не пом’якшено»

Серйозність — це не рішення; це підказка.
Ваше рішення має ґрунтуватися на:

  • Експонована поверхня: чи досяжний уразливий компонент ззовні межі довіри?
  • Зрілість експлойту: є публічний експлойт? озброєний? лише теоретичний?
  • Привілеї: чи може це призвести до root в контейнері? втечі? доступу до даних?
  • Міри пом’якшення: конфігурація вимикає функцію, мережеві політики, ізоляція, WAF, read-only rootfs.
  • Час на виправлення: чи можете ви перестроїти базовий образ сьогодні, або це займе місяць?

Крок C: Вирішіть — перестроїти, запатчити чи прийняти

  • Перестроїти, коли виправлення є в upstream-пакетах і вам потрібен лише новий базовий образ.
  • Запатчити, коли ви контролюєте код і вразливість у вашому дереві залежностей.
  • Прийняти (з фіксацією), коли це не експлуатується, не експоновано або пом’якшено — і ви можете це довести (VEX допомагає).

Крок D: Запобігти повторенню через гігієну збірки

Більшість «шума сканера» — самонанесена рана: величезні образи, інструменти збірки в runtime-шарах, старі базові образи, недисципліноване управління залежностями.
Виправте пайплайн і шум зменшиться.

Практичні завдання: команди, виводи та рішення (12+)

Це практичні завдання, які ви можете виконати на робочій станції або CI-ранері з доступним Docker.
Кожне містить, що означає вивід і яке рішення прийняти.

Завдання 1: Визначити точний digest образу, який ви скануєте

cr0x@server:~$ docker image inspect --format '{{.RepoTags}} {{.Id}}' myapp:prod
[myapp:prod] sha256:9d2c7c0a0d4e2fd0f2c8a7f0b6b1a1f2a3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8

Значення: ID образу (digest) незмінний. Скануйте цього, а не тег, що може рухатись.

Рішення: Якщо ваш сканер повідомляє знахідки без посилання на digest, вважайте звіт «можливо неправильний артефакт» доти, поки не буде доказів.

Завдання 2: Підтвердьте, що в продакшені запущено саме цей digest (не тег)

cr0x@server:~$ docker ps --no-trunc --format 'table {{.Names}}\t{{.Image}}\t{{.ID}}'
NAMES          IMAGE                                                                 ID
myapp-prod     myregistry.local/myapp@sha256:2b1d...e9c3                             61c9f1c1f9d9b0a3b2c1d7b0e3a1c8f9d2e4b5a6c7d8e9f0a1b2c3d4e5f6a7b8

Значення: Запущений контейнер посилається на digest з вашого реєстру. Якщо digest відрізняється від того, що ви сканували, скан нерелевантний.

Рішення: Перескануйте запущений digest або оновіть деплоймент, щоб відповідати просканованому digest, перед створенням тікетів на виправлення.

Завдання 3: Швидка перевірка—яка у нас ОС?

cr0x@server:~$ docker run --rm myapp:prod cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"

Значення: Контекст дистрибутива важливий. Debian бекпорти? Так, іноді. Alpine? Інша база даних і цикл патчів.

Рішення: Налаштуйте сканери на використання фідів, специфічних для дистро; не покладайтеся лише на загальні upstream-дані «виправлено в».

Завдання 4: Порахувати пакети в runtime-образі (прогнозатор шуму)

cr0x@server:~$ docker run --rm myapp:prod dpkg-query -W | wc -l
143

Значення: Більше пакетів зазвичай означає більше CVE. Runtime-образ з 143 пакетами може бути нормальним, але це підказка.

Рішення: Якщо це число — сотні чи тисячі для простого сервісу, плануйте багатоступінчасту збірку або перехід на distroless; ви платите «податок вразливостей» за зручність.

Завдання 5: Знайти найбільші шари (часто там потрапили інструменти збірки)

cr0x@server:~$ docker history --no-trunc myapp:prod | head
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
sha256:9d2c…   3 days ago     /bin/sh -c apt-get update && apt-get install…   312MB
sha256:1aa3…   3 days ago     /bin/sh -c useradd -r -u 10001 appuser          1.2MB
sha256:2bb4…   3 days ago     /bin/sh -c #(nop)  COPY file:app /app           24MB

Значення: Той шар 312MB пахне компіляторами, заголовками та всім іншим, що хтось встановив «тимчасово».

Рішення: Розділіть збірку і рантайм; видаліть кеші пакетного менеджера; мінімізуйте runtime, щоб зменшити поверхню CVE.

Завдання 6: Запустити Trivy зі корисними налаштуваннями (і читати правильно)

cr0x@server:~$ trivy image --severity HIGH,CRITICAL --ignore-unfixed myapp:prod
2026-01-03T10:12:41Z	INFO	Detected OS: debian
2026-01-03T10:12:41Z	INFO	Number of language-specific files: 1
myapp:prod (debian 12)
=================================
Total: 3 (HIGH: 2, CRITICAL: 1)

libssl3 3.0.11-1~deb12u2   (CRITICAL)  CVE-202X-YYYY  fixed in 3.0.11-1~deb12u3
zlib1g  1:1.2.13.dfsg-1    (HIGH)      CVE-202X-ZZZZ  fixed in 1:1.2.13.dfsg-2

Значення: --ignore-unfixed видаляє елементи «патч відсутній», що часто зменшує шум у щоденних операціях.
Залишені знахідки дієві, бо для них є фіксована версія в каналі дистро.

Рішення: Якщо існують фіксовані версії — перестройте з оновленими базовими пакетами. Якщо ні — оцініть експлуатованість і компенсуючі контролі; не блокуйте релізи за «невиправленими» за замовчуванням.

Завдання 7: Згенерувати SBOM і зберегти його як артефакт

cr0x@server:~$ syft myapp:prod -o spdx-json > myapp.spdx.json
cr0x@server:~$ jq -r '.packages[0].name, .packages[0].versionInfo' myapp.spdx.json
base-files
12.4+deb12u5

Значення: Тепер у вас є машинночитний інвентар, прив’язаний до збірки образу.

Рішення: Зберігайте SBOM поруч з образами (в сховищі артефактів). Використовуйте їх для порівняння збірок і щоб відповідати на запит «що всередині?» за хвилини, а не збори.

Завдання 8: Скарелювати знахідки сканера зі статусом встановленого пакета

cr0x@server:~$ docker run --rm myapp:prod dpkg-query -W libssl3
libssl3	3.0.11-1~deb12u2

Значення: Підтверджує, що встановлено, а не те, що сканер вгадав.

Рішення: Якщо встановлена версія вже містить бекпортований патч (вендор каже «виправлено»), позначте запис сканера як помилковий позитив і документуйте це (краще як VEX).

Завдання 9: Перевірити, чи контейнер працює як root (множник ризику)

cr0x@server:~$ docker inspect --format '{{.Config.User}}' myapp:prod

Значення: Порожнє означає, що за замовчуванням воно root. Багато експлойтів з «набридливих» перетворюються на «катастрофічні», коли ви даєте їм root в контейнері.

Рішення: Якщо працює як root — виправте, якщо немає вагомої причини. Додайте USER 10001 (або подібне) і налаштуйте права файлів коректно.

Завдання 10: Валідовати Linux capabilities у рантаймі

cr0x@server:~$ docker inspect --format '{{json .HostConfig.CapAdd}} {{json .HostConfig.CapDrop}}' myapp-prod
null ["ALL"]

Значення: CapDrop ["ALL"] — сильний захід жорсткості. Якщо ви бачите додані capabilities (NET_ADMIN, SYS_ADMIN), радіус ураження зростає.

Рішення: Якщо capabilities додаються «на всякий випадок», приберіть їх і протестуйте. Capabilities треба видавати економно, як паролі root.

Завдання 11: Підтвердити конфігурацію read-only файлової системи (доказ пом’якшення)

cr0x@server:~$ docker inspect --format '{{.HostConfig.ReadonlyRootfs}}' myapp-prod
true

Значення: Read-only rootfs зменшує можливість зберігання експлойту і блокує багато ланцюгів атаки «запис-і-напрацювання».

Рішення: Якщо false — розгляньте можливість зробити true і монтувати лише необхідні записувані шляхи (/tmp, кеш додатку) як tmpfs/volumes.

Завдання 12: Визначити, які порти справді експоновані/слухаються

cr0x@server:~$ docker exec myapp-prod ss -lntp
State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      4096   0.0.0.0:8080       0.0.0.0:*       users:(("myapp",pid=1,fd=7))

Значення: Один слухач на 8080. Це ваша основна віддалена поверхня атаки.

Рішення: Якщо знахідки сканера стосуються компонентів, що не залучені в цей шлях (наприклад, SSH, FTP бібліотеки), знизьте пріоритет, якщо нема внутрішнього ланцюга експлуатації.

Завдання 13: Виявити наявність shell та інших інструментів для зручності (поверхня атаки)

cr0x@server:~$ docker run --rm myapp:prod sh -lc 'command -v bash; command -v curl; command -v gcc; true'
/usr/bin/bash
/usr/bin/curl
/usr/bin/gcc

Значення: Багато інструментів для runtime-контейнера. Добре для дебагу. Також добре для нападників.

Рішення: Перенесіть інструменти до debug-образу або епhemeral toolbox-контейнера. Тримайте продакшен-образи нудними.

Завдання 14: Порівняти два образи, щоб переконатися, що зниження CVE реальне

cr0x@server:~$ trivy image --severity HIGH,CRITICAL myapp:prod | grep -E 'Total:|CRITICAL|HIGH' | head
Total: 17 (HIGH: 13, CRITICAL: 4)
cr0x@server:~$ trivy image --severity HIGH,CRITICAL myapp:prod-slim | grep -E 'Total:|CRITICAL|HIGH' | head
Total: 4 (HIGH: 3, CRITICAL: 1)

Значення: Slim-образ зменшив кількість знахідок суттєво. Тепер підтвердіть функціональність і операційні вимоги (логування, CA-серти, файли часових поясів).

Рішення: Якщо slim-образ проходить інтеграційні тести і підтримує спостережуваність — підніміть його вгору. Якщо ні — ви з’ясували, які залежності дійсно потрібні.

Завдання 15: Виявити ризик «сталої» збірки (base image не оновлювався)

cr0x@server:~$ docker pull debian:12-slim
12-slim: Pulling from library/debian
Digest: sha256:7a8b...cdef
Status: Image is up to date for debian:12-slim

Значення: Ви підтвердили, що локальний базовий образ відповідає поточному стану реєстру. У CI завжди слід робити pull, щоб уникнути збірки на застарілих шарах.

Рішення: Якщо CI кешує базові образи без оновлення — виправте це. Усунення вразливостей часто — «перестройте щотижня», а не «патч в контейнері вручну».

Завдання 16: Перевірити, чи не відправили ви секрети збірки в шари

cr0x@server:~$ docker run --rm myapp:prod sh -lc 'grep -R --line-number -E "BEGIN (RSA|OPENSSH) PRIVATE KEY" / 2>/dev/null | head -n 3 || true'

Значення: Відсутність виводу — добре. Сканери іноді знаходять секрети; можна також зробити самоперевірку.

Рішення: Якщо знайдете щось — обробляйте як інцидент: ротируйте ключі, перестройте образи і виправте Dockerfile/контекст збірки негайно.

Жарт №2: Єдине, що ще більш стійке, ніж вразливість — це кеш шарів з поганими рішеннями.

Три міні-історії з корпоративного життя

Міні-історія 1: Інцидент, викликаний неправильною підставою

Середній SaaS компанія запускала нічні скани контейнерів і мала просту політику:
«Якщо критично — блокувати деплой». Намір був добрий. Реалізація — груба.

Одної п’ятниці з’явився новий критичний CVE у поширеній бібліотеці стиснення.
Їхній сканер позначив її у десятках образів — API, воркери, cron job-и та навіть одноразовий інструмент міграції.
Припущення: «Критично означає експлуатується з інтернету». Тому команда безпеки зупинила всі релізи.

Тим часом реальна операційна проблема була інша: потрібно було відправити міграцію схеми бази даних, щоб зупинити повільне погіршення продуктивності.
З заблокованими деплойментами регресія стала видимою для клієнтів. Зросла латентність. Багато ретраїв. Зросли витрати.

Після важкого вікенду вони виявили, що «критична» бібліотека була лише в шарі інструментів збірки для кількох образів, а не в runtime-стадії.
Для інших вона була встановлена, але недосяжна з їхніх експонованих сервісів. Правильним виправленням була б запланована перестройка і таргетований виняток з обґрунтуванням.

Урок не в «ігнорувати критичні CVE». Урок — «не плутайте ярлики серйозності з експлуатованістю в архітектурі».
Вони замінили правило блокування на правило тріажу: блокувати лише коли (1) існує фіксована версія, (2) компонент у runtime-образі, (3) досяжний з експонованого шляху, або (4) CVE відомий як активно експлуатований.

Міні-історія 2: Оптимізація, що призвела до зворотного ефекту

Команда внутрішньої платформи в ентерпрайзі втомилася від довгих збірок і росту реєстру.
Вони ввели агресивне кешування шарів і закріпили базові образи за digest у Dockerfile, щоб «гарантувати відтворюваність».
Збірки прискорилися. Результати сканів стали стабільними. Усі аплодували.

Через три місяці прийшли нові сповіщення від зовнішніх аудиторів, а не від їхнього CI.
Аудитори сканували образи в реєстрі і знайшли старі, вразливі пакети, що давно були виправлені upstream.
Сканери платформи їх не помічали, бо вони не перестроювалися, а пайплайн сканування запускався лише для «змінених образів».

Оптимізація створила пастку: прив’язка базових образів до digest зробила збірки відтворюваними, але й постійно застарілими, якщо хтось не оновлював pin.
Їхнє кешування полегшило забуття, що патчі безпеки приходять через перестройку.

Виправлення не було складним, але операційно дратувало:
вони додали заплановані перестройки (щотижня для сервісів, видимих з інтернету; щомісяця для внутрішніх інструментів) і автоматизованого бота PR, що підвищує digest базового образу і проганяє інтеграційні тести.
Кеш лишився — але перестав бути виправданням для ніколи не оновлення.

Урок: оптимізації, що зменшують тертя, також зменшують зворотний зв’язок. Якщо ви прибираєте «біль» перестройки, додайте явний графік перестрок.

Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію

Фінтех-компанія мала звичку, про яку ніхто не хвалився: кожен образ, збудований у CI, виробляв три артефакти —
digest образу, SBOM і підписану атестацію вхідних даних збірки (digest базового образу, git SHA, build args).
Це не було захопливо. Це була документація, автоматизована.

Потім вийшла велика вразливість у бібліотеці, що використовувалася їхнім gateway для клієнтів.
Дашборд сканера загорівся, як це буває. Команда безпеки поставила звичне запитання: «Чи ми постраждали?»
Зазвичай це запускає тижні полювання по репозиторіях і суперечок, який сервіс що використовує.

Цього разу SRE на чергуванні витягнув SBOM для digest працюючого gateway, знайшов компонент і отримав відповідь за хвилини:
так, вразлива бібліотека була присутня, і так, відповідна функція була увімкнена в конфігуруванні.
Вони також використали атестації, щоб підтвердити, які збірки це принесли і в яких середовищах ті digests запущені.

Вони перестроїли базовий образ, поступово розгорнули оновлення і використали мапінг digest, щоб переконатися, що продакшен справді оновлений.
Без здогадок. Без «ми ж деплойнули, правда?» діалогів у Slack. Просто контрольоване виправлення.

Урок: різниця між панікою і прогресом — це докази.
SBOM і атестації — не театральність безпеки, коли вони інтегровані в операції й прив’язані до незмінних digest.

План швидкої діагностики

Коли у вас є звіт сканера і обмежений час, зробіть це по порядку. Мета — швидко знайти вузьке місце у прийнятті рішень.

1) Підтвердіть ідентичність артефакту

  • Чи є у нас digest образу?
  • Чи працює в продакшені цей digest?
  • Чи час сканування — після збірки образу?

Чому першим: Половина драм зі сканами — це невідповідні артефакти. Виправте це і багато «термінових» питань зникнуть або стануть точнішими.

2) Визначте наявність виправлення

  • Чи є фіксована версія пакета в репозиторії дистро?
  • Чи є upstream-реліз для мовної залежності?
  • Чи помічено як «невиправлено», бо патчу не існує?

Чому другим: «Немає фіксу» змінює розмову з «пофіксувати зараз» на «пом’якшити й моніторити».

3) Перевірте експозицію рантайму і привілеї

  • Які порти слухаються? Що досяжне ззовні?
  • Чи працює як root? Додаткові capabilities? Привілейований контейнер?
  • Read-only filesystem? seccomp profile? no-new-privileges?

Чому третім: Якщо рантайм жорстко захищений і вразливий компонент не експонований, у вас є час на коректний rebuild замість поспішного ризикованого хотфіксу.

4) Усуньте залежності збірки з runtime-образу

  • Чи CVE в компіляторах, заголовках, пакетних менеджерах, shell?
  • Чи потрібні вони в продакшені?

Чому: Багатоступінчасті збірки можуть прибрати цілі категорії знахідок без «патчування» нічого.

5) Вирішіть: перестройка, патч чи прийняття з доказами

  • Перестройте, коли фікс є в пакетах бази.
  • Запатчуйте, коли фікс в залежностях додатку.
  • Приймайте лише з документованою мотивацією і датою повторної перевірки; краще виробити VEX-ствердження.

Поширені помилки: симптом → корінна причина → виправлення

1) Симптом: «Ми виправили, але сканер все ще показує»

Корінна причина: Ви оновили код або пакети, але не перестроїли образ з чистої бази; або перестройка була, але деплой усе ще запускає старий digest.

Виправлення: Pull-ніть базовий образ у CI, перестройте, запуште і задеплойте за digest. Перевірте docker ps / еквівалент оркестратора, що digest змінився.

2) Симптом: «Сканер каже вразливе, вендор каже не зачіпає»

Корінна причина: Бекпортовані патчі або невідповідні джерела advisory (NVD проти трекера дистро).

Виправлення: Віддавайте перевагу дистро-орієнтованим джерелам сканування; зафіксуйте виняток з доказами (рядок релізу пакета + статус вендора). Розгляньте VEX для кодування «не зачіпає».

3) Симптом: «У кожного образу сотні CVE, ми все ігноруємо»

Корінна причина: Надуті runtime-образи, старі бази і відсутність графіку перестрок. Втома від алертів стає політикою.

Виправлення: Багатоступінчасті збірки, мінімальні бази, заплановані перестройки і блокування тільки на дієвих категоріях (фіксовано+в runtime+експоновано+високий вплив).

4) Симптом: «Критичний CVE в пакеті ядра всередині контейнера»

Корінна причина: Неправильна інтерпретація: образи не привозять своє ядро; сканери іноді повідомляють про пакети чи заголовки ядра.

Виправлення: Патчіть вузли хоста. Для образу — видаліть заголовки/інструменти ядра, якщо вони не потрібні. Ставтесь до «CVE ядра в образі» як до інформаційного, якщо ви не постачаєте інструменти, пов’язані з ядром.

5) Симптом: «Ми оновили базовий образ і зламали TLS/перевірку CA»

Корінна причина: Перехід на slim/distroless без явного включення сертифікатів CA чи даних часових поясів, яких очікує додаток.

Виправлення: Забезпечте наявність ca-certificates (або еквіваленту); явно копіюйте бандли сертифікатів для distroless. Додайте інтеграційні тести для вихідного TLS.

6) Симптом: «Скан показує CVE в пакетах, яких у нас немає»

Корінна причина: Сканер визначає за сигнатурами файлів або вгадує мовні залежності з маніфестів, що не відповідають runtime-вмісту.

Виправлення: Підтвердіть через запити до бази пакетів всередині образу; згенеруйте SBOM; налаштуйте сканер віддавати перевагу метаданим пакетного менеджера.

7) Симптом: «Ми не можемо запатчити, бо фіксованої версії ще немає»

Корінна причина: Upstream не випустив патч, або ваш дистро ще не його доставив, або ви зафіксували репозиторії.

Виправлення: Пом’якшіть: вимкніть вразливу функцію, обмежте експозицію, знизьте привілеї, додайте правила WAF, ізолюйте мережу. Слідкуйте і перестройте, коли з’явиться фікс.

Контрольні списки / покроковий план

Щоденний операційний чекліст (не дає шуму стати культурою)

  1. Скануйте образи за digest, а не тегом.
  2. Зберігайте результати сканування, прив’язані до digest і ID збірки.
  3. Відстежуйте «дієві» знахідки: фіксована доступна версія + в runtime + експоновано/релевантно.
  4. Створюйте тікети з точним ім’ям пакета/версією і шляхом виправлення (перестройка vs зміна коду).
  5. Вимагайте обґрунтування для винятків, включно з доказами (стан вендора, метрики рантайму).

Щотижневий план гігієни безпеки (нудно, але ефективно)

  1. Перестройте інтернет-орієнтовані образи щонайменше щотижня з чистих баз.
  2. Перезапустіть скани на перестроєних образах і порівнюйте дельти; досліджуйте зростання.
  3. Замініть застарілі базові образи; припиніть використання дистро в кінці життєвого циклу.
  4. Видаліть інструменти збірки з runtime (багатоступінчасті збірки).
  5. Перевіряйте жорсткість рантайму: non-root, drop capabilities, read-only rootfs де можливо.

Покроковий план тріажу для «Критичної» знахідки

  1. Перевірка артефакту: підтвердьте, що робочий digest відповідає просканованому.
  2. Перевірка компонента: упевніться, що пакет/бібліотека встановлені в runtime-шарі.
  3. Наявність виправлення: чи є патчений пакет або реліз?
  4. Експозиція: чи компонент у шляхах запитів або досяжний через вхід користувача?
  5. Привілеї: root? додані capabilities? записувана файловa система?
  6. Міри пом’якшення: конфігурація вимикає функцію? мережеві політики? sandboxing?
  7. Рішення: перестройка зараз, патч коду чи прийняття з доказами і датою перегляду.
  8. Перевірка: деплой за digest; перескан; підтвердження, що продакшен працює на новому digest.

Поширені питання (FAQ)

1) Чи блокувати деплое за результатами сканування вразливостей?

Так, але лише за знахідками, що дієві та значущі: існує фіксована версія, компонент у runtime-образі і ризик релевантний (експоновано/високий вплив).
Блокування за замовчуванням через «невиправлене» зазвичай тренує людей обходити систему.

2) Якому сканеру довіряти: Trivy, Docker Scout, Grype чи іншому?

Довіра — це не бренд; це робочий процес. Використовуйте принаймні один сканер, що знає про дистро, і той, що може згенерувати SBOM.
Найкращий сканер — той, який ви тримаєте в актуальному стані і вивід якого ви можете пояснити аудитору без танців інтерпретації.

3) Чому в мого distroless-образу все одно є CVE?

Distroless зменшує кількість пакетів, але не усуває вразливості. Ви все одно можете відправити вразливі бібліотеки, бандли CA або рантайм-модулі.
Також сканери можуть повідомляти CVE для вбудованих компонентів у вашому бінарнику або рантаймі.

4) Чи безпечно ігнорувати «невиправлені» вразливості?

Не безпечно ігнорувати. Можна інше трактування.
Якщо патчу немає, варіанти — пом’якшення (зменшити експозицію), заміна компоненту або прийняття з доказами і тригером перегляду.

5) Чому ми бачимо CVE у пакетах, які ми ніколи не використовуємо?

Тому що пакети встановлені, а не використані. Сканери повідомляють про присутність.
Ваше завдання — зменшити присутність (менші образи) і оцінити досяжність/експозицію для того, що лишається.

6) Як обробляти бекпорт-псевдопозитиви без створення шпарини?

Формалізуйте винятки: прив’яжіть їх до digest образу, рядка релізу пакета, статусу вендора («виправлено через бекпорт» / «не зачіпає») і встановіть дату пере-валідації.
Надавайте перевагу VEX-стейтментам замість хаотичних сторінок в вікі.

7) Чи варто обирати Alpine через менше CVE?

Оберіть базовий образ насамперед за операційною сумісністю (glibc vs musl — реальні проблеми), потім за гігієною безпеки.
Менше повідомлених CVE ≠ менше ризику; це може бути також різницею в покритті звітності.

8) Як часто потрібно перестроювати образи?

Сервіси, видимі в інтернеті: щотижня — хороший стандарт. Внутрішні батч-джоби: щомісяця може бути прийнятно.
Якщо ви перестроюєте «лише коли змінюється код», ви навмисно вирішуєте пропускати патчі безпеки, доставлені через оновлення базового образу.

9) Чи означають рантайм-міри, що можна ігнорувати вразливості образу?

Міри пом’якшення зменшують експлуатованість; вони не знищують вразливості.
Використовуйте їх, щоб виграти час і зменшити радіус ураження, але все одно перестройте/запатчте, коли з’являються виправлення — особливо для широко експлуатованих CVE.

10) Який найпростіший спосіб швидко зменшити шум сканера?

Багатоступінчасті збірки і видалення інструментів збірки з runtime-образів. Потім додайте заплановані перестройки з чистих баз.
Ці два кроки зазвичай значно скорочують знахідки без втрати функціональності.

Висновок: кроки, що дійсно знижують ризик

Сканування вразливостей — це ліхтар, а не вирок суду. Сприймайте його як телеметрію: корелюйте, підтверджуйте і дійте на основі того, що важливо.
Якщо ви хочете менше шуму і більше безпеки, вам не потрібна нова панель. Потрібні докази і регулярність.

  1. Скануйте за digest і доведіть, що продакшен запускає те, що ви сканували.
  2. Генеруйте і зберігайте SBOM для кожної збірки; використовуйте їх, щоб миттєво відповісти «що всередині».
  3. Перестройте регулярно з чистих баз; оновлення безпеки часто приходять через перестройку, а не через зміну коду.
  4. Зменште рантайм-поверхню багатоступінчастими збірками і мінімальними базами.
  5. Загартуйте налаштування рантайму: non-root, drop capabilities, read-only rootfs де можливо.
  6. Впровадьте структуровані винятки (ідеально — VEX), щоб «не зачіпає» не стало «ігноруємо назавжди».

Зробіть це — і ваш сканер перестане бути генератором паніки. Він стане тим, чим мав бути спочатку: інструментом, що допомагає вам відправляти більш безпечні системи і водночас зберігати вихідні вихідні вихідні дні.

← Попередня
MySQL проти PostgreSQL: «випадкові таймаути» — мережа, DNS і пулінг як винуватці
Наступна →
Відключення Windows IKEv2: поширені помилки та надійні виправлення

Залишити коментар