Прямой ответ
Security context задаёт параметры безопасности пода и контейнера: от какого пользователя запускается процесс, какие привилегии ему доступны, можно ли менять файловую систему и какие системные вызовы разрешены. Контекст задаётся на двух уровнях: spec.securityContext для пода и spec.containers[].securityContext для конкретного контейнера; настройки контейнера переопределяют настройки пода.
Практический набор для production:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry/app:2026.10.21-1
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Что делает каждое поле:
| Поле | Эффект |
|---|---|
runAsNonRoot: true |
Запрещает запуск от root; проверка выполняется до старта контейнера |
runAsUser / runAsGroup |
Явный идентификатор пользователя и группы процесса |
fsGroup |
Владелец подключённых томов, чтобы процесс мог писать |
allowPrivilegeEscalation: false |
Запрещает повышение привилегий через setuid-бинарники |
readOnlyRootFilesystem: true |
Корневая ФС доступна только для чтения |
capabilities.drop: ["ALL"] |
Снимает все возможности ядра, кроме явно добавленных |
seccompProfile |
Фильтр системных вызовов; RuntimeDefault — профиль рантайма |
Поле privileged: true отключает почти все ограничения и даёт контейнеру доступ к устройствам узла. В рабочем кластере его быть не должно: скомпрометированный под в таком режиме выходит за пределы контейнера.
Дополнительный уровень — Pod Security Admission, который проверяет поды на соответствие стандартам: privileged (без ограничений), baseline (запрещает известные способы выхода из изоляции), restricted (максимально строгий, требует всех перечисленных выше полей). Уровень задаётся метками namespace:
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Практика: переход на ограниченный профиль
1. Начните с runAsNonRoot. Большинство официальных образов поддерживает запуск от непривилегированного пользователя. Если образ требует root, проблема в самом образе, а не в Kubernetes — см. статью про hardening образов.
2. readOnlyRootFilesystem выявляет записи в образ. Приложения, которые пишут кэш или временные файлы в корневую ФС, перестают работать. Решение — явные emptyDir для /tmp и каталогов кэша.
3. fsGroup нужен для томов, а не для процесса. Без него подключённый том может принадлежать root, и приложение с runAsUser: 10001 получит ошибку записи.
4. capabilities.drop: ["ALL"] — безопасное начало. Нужные возможности добавляют точечно, например NET_BIND_SERVICE для привязки к порту ниже 1024. Проще слушать порт выше 1024 и не добавлять capability вовсе.
5. Seccomp в режиме RuntimeDefault. Профиль по умолчанию блокирует десятки системных вызовов, используемых для выхода из контейнера. Профиль Unconfined оправдан только при доказанной необходимости.
6. Минимальный образ уменьшает поверхность атаки. Отсутствие оболочки и пакетного менеджера лишает атакующего инструментов; о выборе базы — в статье про минимальные образы.
7. Security context дополняет, но не заменяет сетевую изоляцию. Ограничения процесса не мешают исходящим соединениям; для этого нужны NetworkPolicy — см. статью про изоляцию подов.
Рекомендации:
- Вводите уровень
restrictedпоэтапно. Сначалаwarn— метки без блокировки, чтобы собрать список нарушителей, затемaudit, затемenforce. - Исключения оформляйте отдельным namespace. Смешивание строгих и привилегированных нагрузок в одном namespace делает стандарт неприменимым.
- Проверяйте фактический UID процесса.
kubectl exec <pod> -- idпоказывает реальный идентификатор; значение в манифесте может переопределяться образом. - Не отключайте
allowPrivilegeEscalationбез проверки. Некоторые рантаймы используют setuid-хелперы и перестают работать; это видно в логах при старте. - Фиксируйте требования в CI. Проверка манифестов на наличие
privilegedи отсутствиеrunAsNonRootдешевле, чем разбор инцидента.
Практический сценарий: включение restricted сломало половину namespace
Контекст: в namespace добавили метку pod-security.kubernetes.io/enforce: restricted. Через час несколько сервисов перестали разворачиваться.
- Диагностика.
kubectl get eventsпоказал отказы admission-контроллера: создание пода блокировалось с указанием конкретного нарушенного требования. - Причины по сервисам. (а) Два образа запускались от root, поскольку
USERв Dockerfile не был задан; (б) три сервиса писали в/var/cache, что несовместимо сreadOnlyRootFilesystem; (в) один sidecar требовалNET_ADMINдля настройки маршрутов. - Действия. (а) Метку
enforceвременно заменили наwarn, чтобы восстановить деплой; (б) в образах добавилиUSER 10001и пересобрали; (в) для каталогов записи добавилиemptyDirс явными точками монтирования; (г) для sidecar сNET_ADMINсоздали отдельный namespace с уровнемbaselineи задокументировали причину; (д) добавили шаг проверки манифестов в CI с перечнем запрещённых полей. - Повторное включение. Через неделю вернули
enforce: restricted. Все поды прошли проверку; исключение осталось в отдельном namespace. - Результат. Поверхность атаки сократилась: процессы работают от непривилегированного пользователя, корневая ФС доступна только для чтения, лишние возможности ядра сняты.
Вывод: стандарт restricted выявляет накопленные допущения в образах и манифестах. Включать его нужно через warn, иначе деплой останавливается в неподходящий момент, а исправления делаются в спешке.
FAQ
Обязателен ли security context? Нет, без него контейнер запускается с настройками образа, часто от root. Но в кластере с несколькими командами отсутствие ограничений означает, что любой компрометируемый под получает широкие права на узле.
Что произойдёт при runAsNonRoot: true, если образ запускается от root?
Pod не запустится: kubelet отклонит контейнер до старта с ошибкой в событиях. Это ожидаемое поведение — оно выявляет проблему до запуска, а не после компрометации.
Нужно ли задавать securityContext и в поде, и в контейнере? Уровень пода задаёт значения по умолчанию для всех контейнеров, включая init-контейнеры. Уровень контейнера нужен, когда требования различаются, например для sidecar.
Влияет ли security context на производительность? Нет. Seccomp-фильтр и снятие capabilities не дают измеримых накладных расходов; влияние на производительность имеют ограничения ресурсов, а не безопасности.
Источники: Kubernetes docs (security context, pod security standards, pod security admission) — ссылки в шапке.