Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Security context: ограничения безопасности пода

Security context в Kubernetes: runAsNonRoot, capabilities и seccomp

Security context пода и контейнера: runAsNonRoot, readOnlyRootFilesystem, capabilities, fsGroup, seccomp и уровни Pod Security Admission.

Прямой ответ

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 — см. статью про изоляцию подов.

Рекомендации:

  1. Вводите уровень restricted поэтапно. Сначала warn — метки без блокировки, чтобы собрать список нарушителей, затем audit, затем enforce.
  2. Исключения оформляйте отдельным namespace. Смешивание строгих и привилегированных нагрузок в одном namespace делает стандарт неприменимым.
  3. Проверяйте фактический UID процесса. kubectl exec <pod> -- id показывает реальный идентификатор; значение в манифесте может переопределяться образом.
  4. Не отключайте allowPrivilegeEscalation без проверки. Некоторые рантаймы используют setuid-хелперы и перестают работать; это видно в логах при старте.
  5. Фиксируйте требования в CI. Проверка манифестов на наличие privileged и отсутствие runAsNonRoot дешевле, чем разбор инцидента.

Практический сценарий: включение restricted сломало половину namespace

Контекст: в namespace добавили метку pod-security.kubernetes.io/enforce: restricted. Через час несколько сервисов перестали разворачиваться.

  1. Диагностика. kubectl get events показал отказы admission-контроллера: создание пода блокировалось с указанием конкретного нарушенного требования.
  2. Причины по сервисам. (а) Два образа запускались от root, поскольку USER в Dockerfile не был задан; (б) три сервиса писали в /var/cache, что несовместимо с readOnlyRootFilesystem; (в) один sidecar требовал NET_ADMIN для настройки маршрутов.
  3. Действия. (а) Метку enforce временно заменили на warn, чтобы восстановить деплой; (б) в образах добавили USER 10001 и пересобрали; (в) для каталогов записи добавили emptyDir с явными точками монтирования; (г) для sidecar с NET_ADMIN создали отдельный namespace с уровнем baseline и задокументировали причину; (д) добавили шаг проверки манифестов в CI с перечнем запрещённых полей.
  4. Повторное включение. Через неделю вернули enforce: restricted. Все поды прошли проверку; исключение осталось в отдельном namespace.
  5. Результат. Поверхность атаки сократилась: процессы работают от непривилегированного пользователя, корневая ФС доступна только для чтения, лишние возможности ядра сняты.

Вывод: стандарт 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) — ссылки в шапке.