Модуль 4.4: SecurityContexts
Складність:
[MEDIUM]— Важливо для безпеки, численні налаштуванняЧас на виконання: 40–50 хвилин
Передумови: Базові концепції користувачів/груп Linux, Модуль 1.1 (Поди)
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Налаштувати SecurityContexts на рівні Пода та контейнера, включно з
runAsUser,runAsNonRootтаreadOnlyRootFilesystem - Пояснити різницю між налаштуваннями безпеки на рівні Пода та контейнера та їх пріоритетність
- Оцінити чи відповідає Під вимогам безпеки, перевіривши його конфігурацію SecurityContext
- Діагностувати помилки “permission denied”, спричинені обмеженнями SecurityContext на доступ до файлів або capabilities
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»SecurityContexts визначають параметри привілеїв та контролю доступу для подів і контейнерів. Вони контролюють, від якого користувача працює контейнер, які можливості він має та до чого може отримати доступ на хості.
На іспиті CKAD перевіряють:
- Встановлення ідентифікаторів користувача та групи
- Запуск від імені не-root
- Керування можливостями (capabilities) Linux
- Права доступу до файлової системи
Аналогія з охороною будівлі
SecurityContext — це як політики безпеки будівлі. Ви контролюєте, хто може увійти (runAsUser), до яких зон вони мають доступ (capabilities), чи можуть вони щось змінювати (readOnlyRootFilesystem) і чи мають вони майстер-ключі (privileged). Різні орендарі (контейнери) в одній будівлі (поді) можуть мати різні рівні доступу.
Рівні SecurityContext
Розділ «Рівні SecurityContext»Рівень пода проти рівня контейнера
Розділ «Рівень пода проти рівня контейнера»apiVersion: v1kind: Podmetadata: name: security-demospec: securityContext: # Рівень пода (застосовується до всіх контейнерів) runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 containers: - name: app image: nginx securityContext: # Рівень контейнера (перевизначає рівень пода) runAsUser: 2000 allowPrivilegeEscalation: falseПріоритет: Налаштування рівня контейнера перевизначають налаштування рівня пода.
Загальні налаштування
Розділ «Загальні налаштування»Запуск від користувача/групи
Розділ «Запуск від користувача/групи»securityContext: runAsUser: 1000 # UID, від якого запускати runAsGroup: 3000 # GID для процесу fsGroup: 2000 # GID для змонтованих томівЗапуск від не-root
Розділ «Запуск від не-root»securityContext: runAsNonRoot: true # Помилка, якщо образ намагається запуститися від rootЕскалація привілеїв
Розділ «Ескалація привілеїв»securityContext: allowPrivilegeEscalation: false # Заборонити отримання додаткових привілеївФайлова система лише для читання
Розділ «Файлова система лише для читання»securityContext: readOnlyRootFilesystem: true # Контейнер не може писати у файлову системуПривілейований контейнер
Розділ «Привілейований контейнер»securityContext: privileged: true # Повний доступ до хоста (НЕБЕЗПЕЧНО — рідко потрібно)Можливості (capabilities) Linux
Розділ «Можливості (capabilities) Linux»Можливості надають конкретні привілеї root без повного root-доступу:
securityContext: capabilities: add: - NET_ADMIN # Конфігурація мережі - SYS_TIME # Системний годинник drop: - ALL # Спочатку видалити всі можливостіПоширені можливості
Розділ «Поширені можливості»| Можливість | Призначення |
|---|---|
NET_ADMIN | Конфігурація мережі |
NET_BIND_SERVICE | Прив’язка до портів < 1024 |
SYS_TIME | Зміна системного годинника |
SYS_PTRACE | Відлагодження інших процесів |
CHOWN | Зміна власника файлів |
Найкраща практика: видалити всі, додати конкретні
Розділ «Найкраща практика: видалити всі, додати конкретні»securityContext: capabilities: drop: - ALL add: - NET_BIND_SERVICEПовні приклади
Розділ «Повні приклади»Безпечний під (не-root)
Розділ «Безпечний під (не-root)»apiVersion: v1kind: Podmetadata: name: secure-podspec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 containers: - name: app image: nginx securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: tmp mountPath: /tmp - name: cache mountPath: /var/cache/nginx - name: run mountPath: /var/run volumes: - name: tmp emptyDir: {} - name: cache emptyDir: {} - name: run emptyDir: {}Під з правами доступу до томів
Розділ «Під з правами доступу до томів»apiVersion: v1kind: Podmetadata: name: volume-permsspec: securityContext: runAsUser: 1000 fsGroup: 2000 # Файли тому належать цій групі containers: - name: app image: busybox command: ['sh', '-c', 'ls -la /data && sleep 3600'] volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}Візуалізація
Розділ «Візуалізація»┌─────────────────────────────────────────────────────────────┐│ Ієрархія SecurityContext │├─────────────────────────────────────────────────────────────┤│ ││ SecurityContext пода ││ ┌─────────────────────────────────────────────────┐ ││ │ runAsUser: 1000 │ ││ │ runAsGroup: 3000 │ ││ │ fsGroup: 2000 (для томів) │ ││ │ │ ││ │ Контейнер A Контейнер B │ ││ │ ┌────────────────┐ ┌────────────────┐ │ ││ │ │ (успадковує │ │ runAsUser: 2000│ │ ││ │ │ від пода) │ │ (перевизначає │ │ ││ │ │ runAsUser:1000 │ │ під) │ │ ││ │ │ │ │ │ │ ││ │ │ capabilities: │ │ readOnly: true │ │ ││ │ │ drop: [ALL] │ │ │ │ ││ │ └────────────────┘ └────────────────┘ │ ││ └─────────────────────────────────────────────────┘ ││ │└─────────────────────────────────────────────────────────────┘Перевірка параметрів безпеки
Розділ «Перевірка параметрів безпеки»# Перевірити, від якого користувача працює процесk exec my-pod -- id# uid=1000 gid=3000 groups=2000
# Перевірити власника файлів у томіk exec my-pod -- ls -la /data# drwxrwsrwx 2 root 2000 ...
# Перевірити можливостіk exec my-pod -- cat /proc/1/status | grep CapШвидка довідка
Розділ «Швидка довідка»# Рівень подаspec: securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 runAsNonRoot: true
# Рівень контейнераcontainers:- name: app securityContext: runAsUser: 1000 allowPrivilegeEscalation: false readOnlyRootFilesystem: true privileged: false capabilities: drop: ["ALL"] add: ["NET_BIND_SERVICE"]Чи знали ви?
Розділ «Чи знали ви?»-
fsGroupвпливає лише на монтування томів. Файли, створені в emptyDir або PVC, отримують цю групу-власника. Звичайна файлова система контейнера не зачіпається. -
runAsNonRoot: true— це перевірка під час виконання. Якщо стандартний користувач образу контейнера — root (UID 0), контейнер не запуститься. -
Можливості (capabilities) специфічні для Linux. На контейнерах Windows налаштування capabilities ігноруються.
-
readOnlyRootFilesystemламає багато застосунків, яким потрібно записувати тимчасові файли. Використовуйте томи emptyDir для /tmp та подібних шляхів.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це шкодить | Рішення |
|---|---|---|
runAsNonRoot з root-образом | Контейнер не запуститься | Використовуйте runAsUser: 1000 явно |
readOnlyRootFilesystem без tmpfs | Застосунок не може писати тимчасові файли | Змонтуйте emptyDir до /tmp |
| Не видаляти capabilities | Більша поверхня атаки | Завжди drop: [ALL], додавайте конкретні |
| Плутати контекст пода/контейнера | Застосовуються неправильні налаштування | Контейнер перевизначає під |
privileged: true без необхідності | Ризик безпеки | Лише для конкретних системних інструментів |
Тест
Розділ «Тест»-
Яка різниця між securityContext на рівні пода та на рівні контейнера?
Відповідь
Налаштування на рівні пода застосовуються до всіх контейнерів у поді. Налаштування на рівні контейнера перевизначають налаштування рівня пода для цього конкретного контейнера. -
Що контролює
fsGroup?Відповідь
`fsGroup` встановлює групу-власника файлів у змонтованих томах. Процеси запускаються з цією групою як додатковою. -
Що відбувається, якщо встановити
runAsNonRoot: true, але образ за замовчуванням використовує root?Відповідь
Контейнер не запуститься. Потрібно також встановити `runAsUser` з ненульовим UID. -
Як дозволити контейнеру прив’язуватися до порту 80 без запуску від root?
Відповідь
Додайте можливість `NET_BIND_SERVICE`: ```yaml capabilities: add: ["NET_BIND_SERVICE"] ```
Практична вправа
Розділ «Практична вправа»Завдання: Налаштувати параметри безпеки для подів.
Частина 1: Запуск від не-root
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: nonroot-podspec: securityContext: runAsUser: 1000 runAsGroup: 3000 containers: - name: app image: busybox command: ['sh', '-c', 'id && sleep 3600']EOF
k logs nonroot-pod# Має показати: uid=1000 gid=3000 groups=3000Частина 2: Демонстрація fsGroup
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: fsgroup-podspec: securityContext: runAsUser: 1000 fsGroup: 2000 containers: - name: app image: busybox command: ['sh', '-c', 'ls -la /data && id && sleep 3600'] volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}EOF
k logs fsgroup-pod# Файли в /data належать групі 2000Частина 3: Файлова система лише для читання
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: readonly-podspec: containers: - name: app image: busybox command: ['sh', '-c', 'touch /test 2>&1 || echo "Cannot write!"; sleep 3600'] securityContext: readOnlyRootFilesystem: trueEOF
k logs readonly-pod# Має показати: Cannot write!Очищення:
k delete pod nonroot-pod fsgroup-pod readonly-podПрактичні вправи
Розділ «Практичні вправи»Вправа 1: Запуск від користувача (Ціль: 2 хвилини)
Розділ «Вправа 1: Запуск від користувача (Ціль: 2 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill1spec: securityContext: runAsUser: 1000 containers: - name: app image: busybox command: ['sh', '-c', 'id && sleep 3600']EOF
k logs drill1k delete pod drill1Вправа 2: Примусовий запуск від не-root (Ціль: 2 хвилини)
Розділ «Вправа 2: Примусовий запуск від не-root (Ціль: 2 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill2spec: securityContext: runAsNonRoot: true runAsUser: 1000 containers: - name: app image: busybox command: ['sleep', '3600']EOF
k get pod drill2k delete pod drill2Вправа 3: Можливості (Ціль: 3 хвилини)
Розділ «Вправа 3: Можливості (Ціль: 3 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill3spec: containers: - name: app image: busybox command: ['sleep', '3600'] securityContext: capabilities: drop: - ALL add: - NET_BIND_SERVICEEOF
k exec drill3 -- cat /proc/1/status | grep Capk delete pod drill3Вправа 4: Лише для читання з тимчасовою директорією (Ціль: 3 хвилини)
Розділ «Вправа 4: Лише для читання з тимчасовою директорією (Ціль: 3 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill4spec: containers: - name: app image: busybox command: ['sh', '-c', 'touch /tmp/test && echo "Wrote to /tmp" && sleep 3600'] securityContext: readOnlyRootFilesystem: true volumeMounts: - name: tmp mountPath: /tmp volumes: - name: tmp emptyDir: {}EOF
k logs drill4k delete pod drill4Вправа 5: Перевірка fsGroup (Ціль: 3 хвилини)
Розділ «Вправа 5: Перевірка fsGroup (Ціль: 3 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill5spec: securityContext: fsGroup: 2000 containers: - name: app image: busybox command: ['sh', '-c', 'touch /data/file && ls -la /data && sleep 3600'] volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}EOF
k logs drill5# Файл має належати групі 2000k delete pod drill5Вправа 6: Повністю безпечний під (Ціль: 5 хвилин)
Розділ «Вправа 6: Повністю безпечний під (Ціль: 5 хвилин)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill6spec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 2000 containers: - name: app image: busybox command: ['sh', '-c', 'id && ls -la /data && sleep 3600'] securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}EOF
k logs drill6k get pod drill6 -o yaml | grep -A20 securityContextk delete pod drill6Наступний модуль
Розділ «Наступний модуль»Модуль 4.5: Сервісні акаунти — Налаштування ідентифікації подів для доступу до API.