Перейти до вмісту

Модуль 5.1: Безпека образів

Складність: [СЕРЕДНЯ] - Основні знання

Час на виконання: 25-30 хвилин

Передумови: Модуль 4.4: Загрози ланцюга постачання


Що ви зможете робити

Розділ «Що ви зможете робити»

Після завершення цього модуля ви зможете:

  • Оцінити безпеку образів контейнерів протягом життєвого циклу збірка-зберігання-розгортання-виконання
  • Оцінити практики зміцнення образів: мінімальні базові образи, користувач не-root, багатоетапні збірки
  • Визначити шаблони вразливих образів: теги latest, несканований реєстр, вбудовані секрети
  • Пояснити як сканування образів, підпис та admission control формують конвеєр захисту у глибину

Чому цей модуль важливий

Розділ «Чому цей модуль важливий»

Образи контейнерів є форматом пакування для всіх навантажень Kubernetes. Кожна вразливість, неправильна конфігурація чи шкідливий код в образі стає частиною вашого середовища виконання. Захист образів протягом їхнього життєвого циклу — збірка, зберігання, розгортання, виконання — є фундаментом безпеки Kubernetes.

KCSA перевіряє ваші знання практик безпеки образів та управління вразливостями.


Життєвий цикл безпеки образів

Розділ «Життєвий цикл безпеки образів»
┌─────────────────────────────────────────────────────────────┐
│ ЖИТТЄВИЙ ЦИКЛ БЕЗПЕКИ ОБРАЗІВ │
├─────────────────────────────────────────────────────────────┤
│ │
│ ЗБІРКА │
│ ├── Обрати безпечний базовий образ │
│ ├── Мінімізувати встановлені пакети │
│ ├── Не включати секрети │
│ ├── Використовувати багатоетапні збірки │
│ └── Сканувати на вразливості │
│ │
│ ЗБЕРІГАННЯ │
│ ├── Використовувати приватний реєстр │
│ ├── Підписувати образи │
│ ├── Увімкнути сканування вразливостей │
│ ├── Використовувати незмінні теги │
│ └── Впровадити контроль доступу │
│ │
│ РОЗГОРТАННЯ │
│ ├── Перевіряти підписи │
│ ├── Застосовувати дозволені реєстри │
│ ├── Блокувати вразливі образи │
│ ├── Завантажувати за дайджестом │
│ └── Використовувати image pull secrets │
│ │
│ ВИКОНАННЯ │
│ ├── Безперервний моніторинг вразливостей │
│ ├── Виявлення загроз часу виконання │
│ ├── Файлова система тільки для читання │
│ └── Виконання без root-привілеїв │
│ │
└─────────────────────────────────────────────────────────────┘

Побудова безпечних образів

Розділ «Побудова безпечних образів»

Вибір базового образу

Розділ «Вибір базового образу»
┌─────────────────────────────────────────────────────────────┐
│ ПОРІВНЯННЯ БАЗОВИХ ОБРАЗІВ │
├─────────────────────────────────────────────────────────────┤
│ │
│ ТИП ОБРАЗУ РОЗМІР CVE ВИКОРИСТАННЯ │
│ ─────────────────────────────────────────────────────── │
│ ubuntu:22.04 ~77MB 100+ Розробка │
│ debian:bookworm ~50MB 50+ Загального призн. │
│ alpine:3.19 ~7MB 10-20 Легкі застосунки │
│ distroless/static ~2MB 0-5 Статичні бінарники │
│ scratch 0MB 0 Бінарники Go/Rust │
│ │
│ РЕКОМЕНДАЦІЇ: │
│ ├── Продакшн: Distroless або Alpine │
│ ├── Статичні бінарники: Scratch або distroless/static │
│ ├── Потрібна оболонка: Alpine (менший) або Debian │
│ └── Уникати: Повні ОС-образи (Ubuntu, CentOS) у │
│ продакшні │
│ │
│ ПЕРЕВАГИ DISTROLESS: │
│ • Без оболонки (складніше для зловмисників) │
│ • Без менеджера пакетів │
│ • Мінімальна поверхня атаки │
│ • Менший розмір = швидше завантаження │
│ │
└─────────────────────────────────────────────────────────────┘

Багатоетапні збірки

Розділ «Багатоетапні збірки»
# Етап збірки — має всі інструменти збірки
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/myapp
# Етап виконання — мінімальний образ
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/myapp /myapp
USER nonroot:nonroot
ENTRYPOINT ["/myapp"]
┌─────────────────────────────────────────────────────────────┐
│ ПЕРЕВАГИ БАГАТОЕТАПНОЇ ЗБІРКИ │
├─────────────────────────────────────────────────────────────┤
│ │
│ БЕЗ БАГАТОЕТАПНОЇ ЗБІРКИ: │
│ Кінцевий образ включає: │
│ • Інструменти збірки (gcc, make тощо) │
│ • Вихідний код │
│ • Проміжні артефакти │
│ • Тестові залежності │
│ Результат: Великий образ із зайвою поверхнею атаки │
│ │
│ З БАГАТОЕТАПНОЮ ЗБІРКОЮ: │
│ Кінцевий образ включає: │
│ • Лише скомпільований бінарник │
│ • Мінімальні залежності часу виконання │
│ Результат: Малий образ із мінімальною поверхнею атаки │
│ │
│ ПОРІВНЯННЯ РОЗМІРІВ: │
│ Застосунок Go з інструментами збірки: ~800MB │
│ Застосунок Go багатоетапний: ~20MB │
│ Застосунок Go на scratch: ~10MB │
│ │
└─────────────────────────────────────────────────────────────┘

Найкращі практики Dockerfile

Розділ «Найкращі практики Dockerfile»
┌─────────────────────────────────────────────────────────────┐
│ ПРАКТИКИ БЕЗПЕКИ DOCKERFILE │
├─────────────────────────────────────────────────────────────┤
│ │
│ РОБІТЬ: │
│ ✓ Прив'язуйте базовий образ до дайджесту │
│ FROM alpine@sha256:abc123... │
│ │
│ ✓ Запускайте як non-root користувач │
│ USER nonroot:nonroot │
│ │
│ ✓ Використовуйте COPY замість ADD │
│ COPY --chown=nonroot:nonroot app /app │
│ │
│ ✓ Мінімізуйте шари та очищуйте │
│ RUN apt-get update && apt-get install -y pkg \ │
│ && rm -rf /var/lib/apt/lists/* │
│ │
│ НЕ РОБІТЬ: │
│ ✗ Включати секрети в образ │
│ ENV API_KEY=secret123 # ПОГАНО │
│ │
│ ✗ Використовувати тег latest │
│ FROM nginx:latest # ПОГАНО │
│ │
│ ✗ Запускати від root │
│ USER root # ПОГАНО для продакшну │
│ │
│ ✗ Встановлювати непотрібні пакети │
│ RUN apt-get install vim curl wget # Уникати │
│ │
└─────────────────────────────────────────────────────────────┘

Сканування образів

Розділ «Сканування образів»

Сканування вразливостей

Розділ «Сканування вразливостей»
┌─────────────────────────────────────────────────────────────┐
│ ІНСТРУМЕНТИ СКАНУВАННЯ ОБРАЗІВ │
├─────────────────────────────────────────────────────────────┤
│ │
│ TRIVY (Aqua Security) │
│ ├── Пакети ОС та залежності мов програмування │
│ ├── Неправильні конфігурації │
│ ├── Виявлення секретів │
│ └── Генерація SBOM │
│ │
│ GRYPE (Anchore) │
│ ├── Швидке сканування вразливостей │
│ ├── Кілька джерел баз даних │
│ └── Інтеграція з CI/CD │
│ │
│ CLAIR (CoreOS/Red Hat) │
│ ├── Сканування на основі API │
│ ├── Інтеграція з реєстром │
│ └── Безперервний моніторинг │
│ │
│ КОЛИ СКАНУВАТИ: │
│ • Під час збірки: Зупиняти збірки з критичними CVE │
│ • У реєстрі: Безперервне сканування збережених образів │
│ • Під час виконання: Сповіщення про нові CVE │
│ │
└─────────────────────────────────────────────────────────────┘
Terminal window
# Сканування образу на вразливості
trivy image nginx:1.25
# Сканування з фільтром серйозності
trivy image --severity HIGH,CRITICAL nginx:1.25
# Зупинити, якщо знайдено критичні CVE (для CI)
trivy image --exit-code 1 --severity CRITICAL nginx:1.25
# Генерація SBOM
trivy image --format spdx-json -o sbom.json nginx:1.25
# Сканування Dockerfile на неправильні конфігурації
trivy config Dockerfile

Конфігурація приватного реєстру

Розділ «Конфігурація приватного реєстру»
┌─────────────────────────────────────────────────────────────┐
│ БЕЗПЕКА РЕЄСТРУ │
├─────────────────────────────────────────────────────────────┤
│ │
│ КОНТРОЛЬ ДОСТУПУ │
│ ├── Автентифікація потрібна для всіх операцій │
│ ├── Доступ на основі ролей (push проти pull) │
│ ├── Дозволи на рівні репозиторіїв │
│ └── Інтеграція з ServiceAccount │
│ │
│ ЦІЛІСНІСТЬ ОБРАЗІВ │
│ ├── Довіра до контенту (підписування) │
│ ├── Незмінні теги │
│ ├── Інтеграція сканування вразливостей │
│ └── Процеси просування образів │
│ │
│ МЕРЕЖЕВА БЕЗПЕКА │
│ ├── Обов'язкове шифрування TLS │
│ ├── Приватні точки доступу (без публічного доступу) │
│ ├── Ізоляція VPC/мережі │
│ └── Журналювання аудиту │
│ │
│ ХМАРНІ РЕЄСТРИ: │
│ • AWS: ECR (Elastic Container Registry) │
│ • GCP: Artifact Registry │
│ • Azure: Container Registry │
│ • Самохостовані: Harbor, Quay │
│ │
└─────────────────────────────────────────────────────────────┘
# Створення секрету реєстру
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
namespace: production
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
# Використання у поді
apiVersion: v1
kind: Pod
metadata:
name: private-app
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: app
image: myregistry.io/myapp:v1.0
# Або прив'язка до ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
imagePullSecrets:
- name: registry-credentials

Admission Control для образів

Розділ «Admission Control для образів»

Приклад політики Kyverno

Розділ «Приклад політики Kyverno»
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-trusted-registry
spec:
validationFailureAction: Enforce
rules:
- name: validate-registry
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Образи мають бути з довірених реєстрів"
pattern:
spec:
containers:
- image: "gcr.io/my-project/* | myregistry.io/*"

  • Середній образ контейнера має 300+ пакетів та 100+ відомих вразливостей. Мінімальні базові образи можуть зменшити це на 90%.

  • Distroless образи не мають оболонки — якщо зловмисник отримає виконання коду, він не зможе легко запускати команди. Це захист у глибину.

  • Distroless образи Google збираються з нуля щодня і включають лише те, що потрібно для запуску конкретного середовища виконання мови.

  • Шари образів кешуються та спільно використовуються. Вразливість у базовому шарі впливає на всі образи, побудовані на ньому.


ПомилкаЧому це шкодитьРішення
Використання :latestНепередбачуваний, зміннийПрив’язати до дайджесту
Великі базові образиВелика поверхня атакиВикористовувати мінімальні/distroless
Запуск від rootВищі привілеї при експлуатаціїrunAsNonRoot: true
Без скануванняНевідомі вразливостіСканувати в CI та реєстрі
Файлова система для записуМожливе закріпленняreadOnlyRootFilesystem

  1. Чому distroless образи безпечніші за Alpine?

    Відповідь Distroless образи містять лише застосунок та його залежності часу виконання — без оболонки, без менеджера пакетів, без утиліт. Це означає, що навіть якщо зловмисник отримає виконання коду, він не зможе легко запускати команди або встановлювати інструменти. Alpine включає оболонку та менеджер пакетів, які можуть бути використані зловмисниками.
  2. Яка перевага багатоетапних збірок Docker?

    Відповідь Багатоетапні збірки розділяють середовище збірки та середовище виконання. Кінцевий образ містить лише скомпільований застосунок та залежності часу виконання, а не інструменти збірки, вихідний код або тестові залежності. Це значно зменшує розмір образу та поверхню атаки.
  3. Чому слід завантажувати образи за дайджестом, а не за тегом?

    Відповідь Теги є змінними — їх можна перезаписати для вказівки на інші образи. Дайджести є адресованими за контентом та незмінними. Завантаження за дайджестом гарантує, що ви завжди отримуєте точно той самий образ, запобігаючи атакам, де теги замінюються шкідливими версіями.
  4. Коли повинно відбуватися сканування образів?

    Відповідь У кількох точках: під час збірки (зупиняти збірки з критичними CVE), у реєстрі (безперервне сканування збережених образів) та під час виконання (сповіщення про нові CVE, що впливають на запущені контейнери). Це забезпечує захист у глибину протягом життєвого циклу образу.
  5. Чому readOnlyRootFilesystem запобігає?

    Відповідь Він запобігає запису контейнера до його кореневої файлової системи. Це блокує зловмисників від зміни бінарників, додавання інструментів або створення механізмів закріплення всередині контейнера. Застосунки, яким потрібне записуване сховище, використовують окремі томи (emptyDir, PVC).

Безпека образів охоплює весь життєвий цикл:

ФазаКлючові контролі
ЗбіркаМінімальна база, багатоетапна, без секретів, сканування
ЗберіганняПриватний реєстр, підписування, незмінні теги
РозгортанняПеревірка підписів, дозволені реєстри, дайджест
ВиконанняFS тільки для читання, non-root, безперервне сканування

Ключові принципи:

  • Менші образи = менша поверхня атаки
  • Прив’язуйте до дайджесту, а не до тегу
  • Скануйте на кожній фазі
  • Підписуйте та верифікуйте образи
  • Запускайте з мінімальними привілеями

Модуль 5.2: Спостережуваність безпеки — Моніторинг та виявлення загроз безпеки у Kubernetes.