В манифестах подов можно задавать ряд параметров, влияющих на безопасность контейнеров.

Большинство таких параметров настраивается в блоках securityContext и containers[].securityContext.

На данной странице рассмотрены основные параметры безопасности подов и контейнеров:

В Deckhouse Kubernetes Platform (DKP) допустимые значения указанных параметров безопасности контролируются модулем admission-policy-engine.

runAsUser

Параметр runAsUser задаёт цифровой идентификатор пользователя (UID), от имени которого выполняются все процессы внутри контейнера.

Он напрямую управляет правами доступа процессов в Linux. С помощью этого параметра можно принудительно лишить приложение прав суперпользователя (root) на уровне ядра, даже если сам Docker-образ запускается с UID 0.

В ядре Linux безопасность файловой системы и процессов строится на цифровых идентификаторах (UID), а не на текстовых именах. Суперпользователь root всегда имеет UID 0. По умолчанию, если в Dockerfile не указана инструкция USER, контейнерный рантайм запускает приложение с UID 0.

Например, если в контейнере работает веб-сервер от имени root и злоумышленник находит уязвимость (например, Remote Code Execution), он получает полный контроль над изолированной средой с UID 0. Это значительно облегчает проведение атак на хостовую операционную систему.

При использовании runAsUser Kubernetes передаёт указанный UID контейнерному рантайму при создании процесса. При этом:

  • Игнорируются настройки образа. Текстовые или цифровые пользователи, прописанные разработчиком в Dockerfile (через инструкцию USER), полностью переопределяются значением из манифеста.
  • Меняются права процессов. Все создаваемые контейнером процессы получают указанный UID (например, 10001). С этого момента они подчиняются стандартным правилам разграничения доступа Linux для обычных пользователей.

Запуск процессов с некорневым UID (любое число, отличное от 0) является фундаментальным правилом безопасности (принцип наименьших привилегий). Если приложение скомпрометировано, злоумышленник с UID 10001 не сможет изменить системные файлы внутри контейнера, установить вредоносные пакеты через apt/apk или выполнить чувствительные системные вызовы к ядру хоста.

Если вы указываете случайный UID (например, 2000), у этого пользователя внутри контейнера может не быть прав на чтение файлов самого приложения, если они были скопированы в образ с правами root:root. Разработчикам необходимо заранее подготавливать Docker-образы (выполнять CHOWN для рабочих директорий), чтобы приложение могло успешно запуститься под некорневым пользователем.

Расположение параметра

Параметр может задаваться как для всего пода (настройки унаследуют все контейнеры), так и индивидуально для конкретного контейнера. Значение, заданное для контейнера, имеет более высокий приоритет.

Параметр можно указать в одном из следующих полей:

  • spec.securityContext.runAsUser — для всего пода;
  • spec.containers[].securityContext.runAsUser — для конкретного контейнера.

Доступные значения параметра

Параметр принимает целое положительное число (integer), представляющее собой UID в Linux. Доступны следующие варианты:

  • 0 — запуск от имени root (категорически не рекомендуется);
  • 1000 и выше (до 65535 или 4294967295 в зависимости от архитектуры) — запуск от имени обычного пользователя (рекомендуется выбирать случайные высокие ID, например 10001, чтобы исключить совпадения с системными UID на хосте).

Пример конфигурации

Принудительный запуск контейнера от имени некорневого пользователя с UID 10001:

spec:
  containers:
  - name: secure-web-app
    image: my-app:latest
    securityContext:
      runAsUser: 10001

runAsNonRoot

Параметр runAsNonRoot определяет, обязан ли контейнер запускаться исключительно от имени некорневого пользователя (UID отличный от 0).

Он включает встроенный механизм валидации на стороне агента kubelet. С помощью этого параметра Kubernetes проверяет манифест и сам Docker-образ перед стартом, полностью блокируя запуск контейнера, если итоговый UID процесса окажется 0.

Обычно Kubernetes доверяет метаданным Docker-образа. Если разработчик забыл указать некорневого пользователя в Dockerfile, контейнер запустится с правами root (UID 0). Параметр runAsNonRoot заставляет kubelet провести строгую инспекцию перед запуском контейнера.

Например, если в манифесте пода выставлен флаг runAsNonRoot: true, kubelet запрашивает у контейнерного рантайма информацию о пользователе из образа. Если там настроен USER root или вовсе нет этой инструкции (что означает UID 0), Kubernetes прерывает запуск и переводит под в состояние ошибки.

При использовании runAsNonRoot: true агент kubelet анализирует итоговый UID, с которым должен запуститься процесс. При этом:

  • Проверяется связка параметров. Если в манифесте указан runAsUser: 0, Kubernetes сразу заблокирует под;
  • Анализируется манифест образа. Если в securityContext не указан конкретный runAsUser, kubelet проверяет UID внутри самого Docker-образа. Если там обнаруживается 0 (или текстовое имя root), под не запустится.
  • Выводится ошибка запуска. Вместо работающего небезопасного контейнера вы получите статус CreateContainerConfigError или ContainerCannotRun со следующим описанием: Container has runAsNonRoot and image will run as root.

Этот параметр служит «подушкой безопасности» и главным рубежом защиты кластера от человеческого фактора. Он гарантирует, что в production-окружение физически не сможет попасть контейнер, работающий с правами суперпользователя. Это критически важно, так как компрометация root-контейнера открывает злоумышленнику прямой путь к эксплуатации уязвимостей ядра хоста и потенциальному «побегу из контейнера» (container escape).

Некоторые Docker-образы используют текстовые имена пользователей вместо цифровых UID (например, USER nginx). Если в образе не настроена таблица соответствия имён и ID (/etc/passwd), kubelet не сможет определить числовой UID на этапе подготовки и заблокирует контейнер, даже если этот пользователь не является root. Чтобы избежать этой проблемы, используйте runAsNonRoot: true совместно с явным указанием цифрового ID через runAsUser.

Расположение параметра

Параметр может задаваться как для всего пода (настройки унаследуют все контейнеры), так и индивидуально для конкретного контейнера:

  • spec.securityContext.runAsNonRoot — для всего пода;
  • spec.containers[].securityContext.runAsNonRoot — для конкретного контейнера.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — проверка на некорневого пользователя отключена (значение по умолчанию);
  • true — запуск от имени root запрещён (рекомендуется для всех приложений).

Пример конфигурации

Пример конфигурации, которая блокирует запуск от имени root и принудительно назначает безопасный UID:

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10001
  containers:
  - name: protected-app
    image: my-app:latest

runAsGroup

Параметр runAsGroup определяет цифровой идентификатор основной группы (GID), под которым будут запускаться все процессы внутри контейнера.

Он дополняет параметр runAsUser и управляет групповыми правами доступа процессов к файлам и устройствам Linux. С его помощью можно гибко разграничить права общего доступа для нескольких контейнеров или настроить безопасное взаимодействие с локальными дисками.

В Linux доступ к файлам распределяется на трёх уровнях: для владельца (User), для членов его основной группы (Group) и для всех остальных (Others). Группы идентифицируются цифровыми GID, где 0 — это группа суперпользователя root. По умолчанию, если параметр не задан, контейнерный рантайм присваивает процессам GID 0 или ту группу, которая жёстко прописана для пользователя в файле /etc/passwd внутри Docker-образа.

Например, если приложение создаёт логи или временные файлы, они по умолчанию записываются с группой root (GID 0). Если к этой же директории нужно дать доступ соседнему контейнеру (например, агенту сбора логов), инженерам приходится давать файлам избыточные права chmod 666 (чтение и запись для всех в системе), что нарушает правила безопасности.

При использовании runAsGroup Kubernetes передаёт указанный GID контейнерному рантайму, переопределяя стандартное поведение операционной системы. При этом:

  • Назначается основная группа. Первичному процессу контейнера и всем его дочерним процессам принудительно присваивается указанный GID (например, 20002).
  • Маркируются новые файлы. Все новые файлы, директории или сокеты, которые контейнер создаст во время своей работы, автоматически получат этот GID в качестве группы-владельца.

Фиксация некорневого GID (отличного от 0) предотвращает случайный или намеренный доступ процессов контейнера к защищённым системным файлам группы root. Кроме того, это позволяет безопасно организовать совместную работу нескольких контейнеров с общими данными (через emptyDir или PersistentVolume) без выдачи опасных глобальных прав на чтение и запись для посторонних процессов.

Так же, как и в случае с runAsUser, указание случайного GID может привести к ошибкам вида Permission denied при старте, если бинарные файлы приложения внутри Docker-образа принадлежат исключительно группе root и закрыты для чтения остальным. Образ должен проектироваться с учётом того, что приложение будет работать под нестандартной группой.

Расположение параметра

Параметр может задаваться как для всего пода (настройки унаследуют все контейнеры), так и индивидуально для конкретного контейнера:

  • spec.securityContext.runAsGroup — для всего пода;
  • spec.containers[].securityContext.runAsGroup — для конкретного контейнера.

Доступные значения параметра

Параметр принимает целое положительное число (integer), представляющее собой GID в Linux. Доступны следующие варианты:

  • 0 — назначение основной группы root (не рекомендуется);
  • 1000 и выше (до 4294967295) — назначение некорневой группы (рекомендуется выбирать высокие значения, например 10002, согласованные с вашей политикой разграничения прав).

Пример конфигурации

Пример конфигурации пода, при которой процессы работают под некорневым пользователем и входят в выделенную безопасную группу:

spec:
  securityContext:
    runAsUser: 10001
    runAsGroup: 10002
  containers:
  - name: shared-data-app
    image: my-app:latest

readOnlyRootFilesystem

Параметр readOnlyRootFilesystem определяет, разрешено ли процессам внутри контейнера изменять или создавать файлы на его собственном базовом диске.

Он переводит неизменяемость контейнеров на аппаратный уровень ядра Linux. С помощью этого параметра можно заблокировать любые попытки записи в файловую систему контейнера, превращая его образ в статичную и защищенную среду.

Контейнеры создаются на основе слоев Docker-образа, поверх которых рантайм накладывает один верхний тонкий слой с правами на запись (Read-Write layer). По умолчанию приложение может свободно создавать файлы в /tmp, устанавливать утилиты или перезаписывать свои же конфигурационные файлы.

Например, если злоумышленник взламывает веб-приложение, его стандартный первый шаг — скачать вредоносный скрипт или вредоносную программу во временную директорию (например, /tmp/malware.sh), выдать ему права на исполнение и запустить. Если файловая система открыта на запись, ядро Linux позволит это сделать.

При использовании readOnlyRootFilesystem: true Kubernetes даёт команду контейнерному рантайму смонтировать корневой слой контейнера (/) в режиме ro (read-only). При этом:

  • Блокируется запись в корень. Любая системная попытка создать, изменить или удалить файл в любой директории контейнера (если это не специально смонтированный внешний диск) будет пресечена ядром.
  • Вызывается системная ошибка. Приложение, попытавшееся записать лог или временный файл на базовый диск, мгновенно получит отказ операционной системы с ошибкой Read-only file system.

Этот параметр реализует концепцию неизменяемой инфраструктуры (Immutable Infrastructure) и сводит к нулю риски долгосрочного закрепления злоумышленника в контейнере. Даже если в приложении найдена критическая уязвимость (RCE), взломщик физически не сможет сохранить вредоносные файлы, подменить исполняемые файлы или модифицировать код. Это одна из самых эффективных практик защиты от современных автоматизированных атак.

Большинство современных приложений (а также системные библиотеки внутри образа) не могут запуститься, если им полностью запрещено писать временные данные (например, PID-файлы или логи). Чтобы приложение не падало с ошибкой при старте, все необходимые для записи пути (такие как /tmp, /var/run, /cache) необходимо точечно смонтировать как временные RAM-диски с помощью механизмов emptyDir прямо в манифесте Kubernetes.

Расположение параметра

Параметр задаётся исключительно на уровне конкретного контейнера в поле spec.containers[].securityContext.readOnlyRootFilesystem.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — файловая система контейнера доступна на запись (значение по умолчанию);
  • true — корневая файловая система заблокирована на запись (рекомендуется для максимальной безопасности).

Пример конфигурации

Пример конфигурации с переводом контейнера в режим read-only и безопасным выделением временной папки /tmp для легитимной записи:

spec:
  containers:
  - name: immutable-api
    image: my-app:latest
    securityContext:
      readOnlyRootFilesystem: true
    volumeMounts:
    - mountPath: /tmp
      name: temp-storage
  volumes:
  - name: temp-storage
    emptyDir: {}

fsGroup

Параметр fsGroup определяет цифровой идентификатор группы (GID) Linux, которой будут принудительно принадлежать все смонтированные к поду постоянные тома (PersistentVolumes).

Он управляет правами доступа к внешним дискам на уровне файловой системы хоста и контейнера. С его помощью Kubernetes автоматически решает проблему совместимости прав, позволяя некорневым процессам беспрепятственно читать и писать данные на смонтированные хранилища без использования избыточных прав администратора.

Когда к поду монтируется постоянный диск (PersistentVolume), файлы на нём сохраняют те UID и GID, с которыми они были изначально записаны (часто это 0:0, то есть root). Если сам контейнер запущен от имени безопасного некорневого пользователя (например, runAsUser: 10001), ядро Linux заблокирует доступ к диску.

Например, база данных PostgreSQL запускается в контейнере под пользователем postgres (UID 999). При монтировании сетевого диска процесс пытается инициализировать файлы в каталоге данных, но получает ошибку Permission denied, так как каталог на диске принадлежит root. Инженерам приходится вручную менять права на диске, что неудобно и небезопасно.

При использовании fsGroup при старте пода Kubernetes выполняет специальную процедуру подготовки томов перед тем, как запустить основные контейнеры. При этом:

  • Меняется владелец на диске. Kubernetes автоматически выполняет операцию, аналогичную chown, делая указанный fsGroup (например, 30003) группой-владельцем всех файлов и директорий на смонтированном диске.
  • Устанавливается SGID-флаг. На корневой каталог тома накладывается специальный флаг set-group-ID. Благодаря этому все новые файлы, которые контейнер создаст на этом диске в процессе работы, автоматически унаследуют GID из fsGroup, а не основную группу контейнера.
  • Добавляются права процессам. Идентификатор fsGroup добавляется в список дополнительных (supplemental) групп для каждого контейнера в поде, предоставляя процессам легитимный доступ к файлам тома.

Параметр устраняет необходимость запускать контейнеры от имени root только ради того, чтобы они имели доступ к своим дискам. Он также избавляет от опасной практики выставления прав chmod 777 на сетевых хранилищах, изолируя данные конкретного приложения на уровне выделенной группы Linux.

По умолчанию при каждом перезапуске пода Kubernetes рекурсивно обходит все файлы на смонтированном диске, чтобы проверить и изменить их GID. Если на диске базы данных хранится множество мелких файлов, этот процесс может занять долгое время, из-за чего под будет зависать в статусе ContainerCreating. Чтобы исправить это поведение, используйте fsGroup в паре с параметром fsGroupChangePolicy: OnRootMismatch.

Расположение параметра

Настройка задаётся в поле spec.securityContext.fsGroup исключительно на уровне всего пода, так как тома монтируются к поду целиком.

Доступные значения параметра

Параметр принимает целое положительное число (integer), представляющее собой GID в Linux. Доступны следующие варианты:

  • 0 — использование группы root для томов (не рекомендуется);
  • 1000 и выше (до 4294967295) — выделенный идентификатор группы для совместного доступа к дискам (рекомендуется).

Пример конфигурации

Пример конфигурации пода, при которой некорневой контейнер получает автоматический безопасный доступ к смонтированному постоянному тому:

spec:
  securityContext:
    runAsUser: 10001
    fsGroup: 30003
  containers:
  - name: db-app
    image: my-db:latest
    volumeMounts:
    - mountPath: /var/lib/data
      name: storage
  volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: db-pvc

supplementalGroups

Параметр supplementalGroups определяет список дополнительных цифровых идентификаторов групп (GID) Linux, которые будут принудительно добавлены к процессу контейнера в дополнение к его основной группе.

В Linux каждый процесс всегда имеет одну основную группу (задаваемую через runAsGroup) и список дополнительных (supplemental) групп. Механизм дополнительных групп используется ядром для предоставления пользователю прав доступа к различным разделяемым системным ресурсам, общим каталогам или физическим устройствам хоста.

Список дополнительных групп формируется путем перечисления числовых идентификаторов. При запуске пода ядро Linux объединяет основную группу процесса и этот список, расширяя права доступа контейнера.

Пример конфигурации процесса внутри контейнера:

Параметр Значение
Основной пользователь, UID (runAsUser) 10001
Основная группа, GID (runAsGroup) 10002
Дополнительные группы, GID (supplementalGroups) [40001, 40002]

Если на смонтированном диске или внутри контейнера есть файлы, принадлежащие группе 40001, процесс с такой конфигурацией сможет беспрепятственно прочитать или изменить их (в зависимости от стандартных UNIX-прав файлов), поскольку он легитимно входит в состав этой группы.

Расположение параметра

Настройка задаётся исключительно на уровне всего пода в поле spec.securityContext.supplementalGroups, поскольку список дополнительных групп применяется ко всем контейнерам (включая init-контейнеры) внутри пода.

Доступные значения параметра

Параметр принимает список целых положительных чисел (array из integer), представляющих собой системные GID в Linux. Доступны следующие варианты:

  • 0 — добавление группы root в качестве дополнительной (категорически не рекомендуется);
  • 1–999 — системные группы Linux (например, группы для доступа к аудиоустройствам или логированию). Используйте с осторожностью, чтобы случайно не выдать лишние права на узле;
  • 1000 и выше (до 4294967295) — пользовательские группы безопасности для совместного разграничения прав доступа к файловым хранилищам (рекомендуемый вариант).

Параметр supplementalGroups позволяет реализовать точечное разделение прав доступа к общим данным (например, к файлам в emptyDir или PersistentVolume) между разными подами без повышения привилегий до уровня root. Вместо выдачи избыточных прав на чтение и запись для всех пользователей системы (chmod 777), инженеры могут объединить несколько независимых сервисов в одну общую безопасную группу.

Обратите внимание:

  • Взаимосвязь с fsGroup. Параметр fsGroup также добавляет указанный GID в список дополнительных групп процесса. Разница в том, что fsGroup дополнительно меняет владельца файлов на монтируемом диске и устанавливает SGID-флаг, а supplementalGroups просто дополняет права самого процесса, никак не модифицируя файловую систему томов при старте.
  • Игнорирование текстовых имен. Kubernetes принимает только цифровые форматы ID групп. Настройки текстовых групп из файла /etc/group внутри Docker-образа полностью игнорируются.

Пример конфигурации

Пример конфигурации пода, процесс которого запускается под некорневым пользователем и получает доступ к двум разным разделяемым хранилищам через дополнительные группы 40001 и 40002:

apiVersion: v1
kind: Pod
metadata:
  name: shared-groups-pod
spec:
  securityContext:
    runAsUser: 10001
    runAsGroup: 10002
    supplementalGroups:
      - 40001 # Группа для доступа к общему хранилищу логов.
      - 40002 # Группа для работы с медиа-файлами.
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - mountPath: /var/log/shared
          name: log-volume
  volumes:
    - name: log-volume
      persistentVolumeClaim:
        claimName: shared-logs-pvc

fsGroupChangePolicy

Параметр fsGroupChangePolicy определяет, каким образом Kubernetes будет проверять и изменять права владения (GID) для файлов на смонтированных дисках при запуске пода.

Он напрямую управляет поведением агента kubelet на этапе инициализации хранилищ. С помощью этого параметра можно оптимизировать время старта приложений, работающих с большими объемами данных, предотвращая длительные простои кластера.

Когда для пода настроен параметр fsGroup, Kubernetes должен гарантировать, что все файлы на подключенном томе принадлежат этой группе. По умолчанию kubelet при каждом запуске или перезапуске пода выполняет полную рекурсивную проверку и смену владельца (аналог команды chown -R) для каждого файла на диске.

Например, если вы запускаете базу данных (такую как PostgreSQL или Elasticsearch) с диском объемом в несколько терабайт, на котором лежит множество мелких файлов, системный обход диска агентом kubelet может занять 15–30 минут. Всё это время под будет находиться в состоянии ContainerCreating, блокируя работу приложения и нарушая метрики доступности (uptime).

При использовании fsGroupChangePolicy Kubernetes меняет алгоритм подготовки тома перед стартом контейнеров. При этом:

  • включается стандартный обход (Always) — kubelet всегда рекурсивно проходит по всем файлам и меняет GID на ходу, независимо от того, правильные там права или нет;
  • включается умная проверка (OnRootMismatch) — kubelet проверяет права только у самого корневого каталога смонтированного диска. Если его GID уже совпадает с указанным в fsGroup, рекурсивный обход терабайтов данных полностью пропускается, и контейнеры запускаются мгновенно. Если права не совпадают (например, диск подключен впервые), то права обновляются для всех вложенных файлов.

Параметр устраняет критическую проблему «зависания» инфраструктуры при масштабировании, обновлении подов или аварийном переключении узлов (failover). При этом сохраняется строгая изоляция данных: права доступа гарантированно будут скорректированы, если диск переехал из другого окружения или был создан со стандартными правами root.

Политика OnRootMismatch поддерживается не всеми типами хранилищ (хотя отлично работает со стандартными PersistentVolumeClaims на базе блочных и сетевых устройств, таких как AWS EBS, Ceph RBD или локальные диски). Кроме того, если файлы внутри тома были созданы сторонним процессом с разными GID вручную, умная проверка по корневой папке может не заметить скрытых расхождений в правах более глубоких подкаталогов.

Расположение параметра

Настройка задаётся в поле spec.securityContext.fsGroupChangePolicy исключительно на уровне всего пода, так как тома монтируются к поду целиком.

Доступные значения параметра

Параметр имеет строковый тип (string). Доступны следующие значения:

  • Always — всегда рекурсивно изменять права на все файлы при каждом старте (значение по умолчанию);
  • OnRootMismatch — изменять права рекурсивно только в том случае, если права корневой директории тома не совпадают с параметром fsGroup (рекомендуется для баз данных и больших хранилищ).

Пример конфигурации

Пример оптимизированной конфигурации для быстрого запуска пода с базой данных и терабайтным диском:

spec:
  securityContext:
    runAsUser: 10001
    fsGroup: 30003
    fsGroupChangePolicy: OnRootMismatch # Пропускать проверку, если корень диска уже настроен.
  containers:
  - name: heavy-db
    image: my-db:latest
    volumeMounts:
    - mountPath: /data
      name: big-volume
  volumes:
  - name: big-volume
    persistentVolumeClaim:
      claimName: heavy-db-pvc

appArmorProfile

Параметр appArmorProfile определяет, какой профиль принудительного контроля доступа (Mandatory Access Control, MAC) будет наложен на контейнер на уровне ядра Linux.

Он управляет поведением системного модуля безопасности AppArmor, работающего на хост-узлах кластера. С помощью этого параметра можно заблокировать конкретные действия внутри контейнера (например, чтение конфигурационных файлов хоста, выполнение бинарных файлов или запись в системные папки), даже если процесс запущен под пользователем root.

AppArmor — это подсистема безопасности Linux, которая позволяет связать программу с профилем безопасности, определяющим ее полномочия. В отличие от стандартных прав Linux, ограничения AppArmor накладываются сверху, и их не может обойти даже суперпользователь root.

Список разрешенных действий формируется с помощью создания специального файла — профиля AppArmor на хост-узле в директории /etc/apparmor.d.

Профиль представляет собой текстовую структуру, в которой задаются детальные правила доступа к файловой системе, сети и системным вызовам. Пример:

profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
  include <abstractions/base>
  
  # Разрешение на чтение всего.
  /** r,
  
  # Разрешение на запись везде, кроме /etc.
  /** w,
  audit deny /etc/** w,
}

Профили бывают трёх видов:

  • встроенный профиль рантайма (RuntimeDefault) — содержит базовый набор ограничений контейнеризации, оптимальный для большинства задач;
  • профиль без ограничений (Unconfined) — полностью отключает защиту AppArmor для контейнера;
  • пользовательские профили (Localhost) — кастомные профили, которые администратор должен заранее загрузить в ядро Linux на каждом узле в директорию /etc/apparmor.d.

AppArmor обеспечивает мощный дополнительный уровень изоляции и защиты от уязвимостей нулевого дня. Если злоумышленник получил выполнение кода в контейнере и даже повысил свои права до root, строго настроенный профиль AppArmor не позволит ему изменить конфигурационные файлы приложения, прочитать секреты хоста или выполнить опасные системные утилиты.

Расположение параметра

Параметр задаётся в следующих полях:

  • spec.securityContext.appArmorProfile — на уровне пода;
  • spec.containers[].securityContext.appArmorProfile — на уровне контейнера.

Доступные значения параметра

Доступны следующие значения:

  • RuntimeDefault — профиль контейнерного рантайма по умолчанию (рекомендуемый вариант);
  • Unconfined — AppArmor без ограничений (менее безопасно);
  • Localhost — пользовательский профиль, который должен быть предварительно загружен в ядро операционной системы на каждом узле кластера.

    Для данного параметра также указывается строковый параметр localhostProfile — точное имя профиля, зарегистрированное в операционной системе хоста.

До появления полноценного поля в API профили AppArmor настраивались через текстовые аннотации в метаданных пода. Начиная с версии Kubernetes 1.30, этот синтаксис объявлен устаревшим, а в версии 1.36 поддержка аннотаций была полностью удалена из кода Kubernetes.

Синтаксис требовал конструирования составного ключа, содержащего точное имя целевого контейнера:

  • расположение — metadata.annotations["container.apparmor.security.beta.kubernetes.io/<имя_контейнера>"];
  • значения — runtime/default (в настоящий момент RuntimeDefault), localhost/<имя_профиля> (в настоящий момент Localhost) или unconfined (в настоящий момент Unconfined).

Критический недостаток устаревшего формата заключался в том, что API-сервер не валидировал опечатки в имени контейнера внутри аннотации. Если в названии допускалась ошибка, Kubernetes запускал контейнер без какой-либо защиты AppArmor. При обновлении кластеров до версии 1.30+ данный формат необходимо переписать на новый securityContext.appArmorProfile.

Пример конфигурации

Пример конфигурации пода, использующей кастомный профиль AppArmor с именем k8s-apparmor-example-deny-write, который был предварительно загружен в ОС на узлах кластера:

apiVersion: v1
kind: Pod
metadata:
  name: secure-apparmor-pod
spec:
  containers:
    - name: app
      image: nginx
      securityContext:
        appArmorProfile:
          type: Localhost
          localhostProfile: k8s-apparmor-example-deny-write # Точное имя профиля в ядре ОС узла.

seccompProfile

Параметр seccompProfile ограничивает список системных вызовов, доступных процессам в контейнере с помощью специального профиля.

Системный вызов (syscall) — это обращение программы из пользовательского пространства к ядру Linux за «привилегированным» действием. Процесс не может напрямую управлять устройствами, памятью, сетью и другими системными ресурсами. Он просит ядро сделать это через syscall.

Список разрешенных системных вызовов формируется с помощью создания специального файла с профилем seccomp. Профиль представляет собой JSON-структуру, в которой задаются разрешения. Пример:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "read",
                "write",
                "exit",
                "exit_group"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

Профили бывают трёх видов:

  • встроенный профиль рантайма (RuntimeDefault) — содержит базовый набор разрешённых системных вызовов, оптимальный для большинства контейнеров;
  • профиль без ограничений (Unconfined) — позволяет выполнить любое действие в контейнере;
  • пользовательские профили.

Чем меньше разрешённых системных вызовов, тем меньше «поверхность атаки». Даже если злоумышленник смог выполнить код в контейнере, запрет критичных вызовов может ограничить развитие атаки.

Расположение параметра

Параметр задаётся в следующих полях:

  • spec.securityContext.seccompProfile — на уровне пода;
  • spec.containers[].securityContext.seccompProfile — на уровне контейнера.

Доступные значения параметра

Доступны следующие значения:

  • RuntimeDefault — профиль контейнерного рантайма по умолчанию (рекомендуемый вариант);
  • Unconfined — без seccomp-ограничений (менее безопасно);
  • Localhost — пользовательский профиль, размещённый на каждом узле в директории /var/lib/kubelet/seccomp.

    Для данного параметра также указывается параметр localhostProfile — относительный путь к профилю внутри директории /var/lib/kubelet/seccomp.

Пример конфигурации

Пример конфигурации пода, в которой используется пользовательский профиль из файла /var/lib/kubelet/seccomp/my-profiles/secure.json:

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: my-profiles/secure.json  # Путь относительно /var/lib/kubelet/seccomp/.
  containers:
    - name: app
      image: nginx

seLinuxOptions

Параметр seLinuxOptions ограничивает возможности процессов в контейнере с помощью меток принудительного контроля доступа (Mandatory Access Control, MAC) на уровне ядра Linux.

SELinux (Security-Enhanced Linux) — это подсистема безопасности Linux, которая управляет доступом на основе политик и контекстов безопасности (лейблов), присваиваемых процессам, файлам, портам и устройствам. В отличие от стандартных прав Linux, ограничения SELinux накладываются сверху, благодаря чему процессы изолируются друг от друга, даже если они запущены от имени суперпользователя root. Этот механизм по умолчанию активен и является стандартом в дистрибутивах Red Hat, CentOS, Fedora, Rocky Linux и AlmaLinux.

Контекст безопасности SELinux представляет собой строку, разделённую двоеточиями, которая состоит из четырёх основных компонентов: user:role:type:level.

В Kubernetes эти компоненты передаются контейнерному рантайму через специальную структуру параметров. Пример структуры компонентов:

User (Пользователь):  system_u     # Системный пользователь SELinux.
Role (Роль):          system_r     # Системная роль для процессов.
Type (Тип/Домен):     container_t  # Домен, определяющий правила доступа контейнера.
Level (Уровень/MCS):  s0:c123,c456 # Категории для многоуровневой изоляции (Multi-Category Security).

В большинстве случаев контейнерные рантаймы (containerd, CRI-O) автоматически генерируют случайные уникальные категории (level) для каждого пода. Благодаря этому два контейнера с одинаковым типом container_t физически не могут получить доступ к файлам друг друга на хост-узле. Параметр seLinuxOptions используется тогда, когда этот стандартный механизм нужно переопределить.

SELinux обеспечивает строгую изоляцию на уровне файловой системы хоста и межпроцессного взаимодействия. Если злоумышленник скомпрометирует приложение внутри контейнера, SELinux заблокирует любые попытки прочитать чужие файлы на узле (например, в директории /var/lib/kubelet/pods/), получить доступ к сокетам хоста или устройствам в /dev, предотвращая «побег из контейнера» (container escape).

Расположение параметра

Параметр задаётся в следующих полях:

  • spec.securityContext.seLinuxOptions — на уровне пода;
  • spec.containers[].securityContext.seLinuxOptions — на уровне контейнера.

Доступные значения параметра

Параметр принимает объект, состоящий из четырех опциональных строковых полей, которые соответствуют компонентам контекста SELinux:

  • user — пользователь SELinux (например, system_u);
  • role — роль SELinux (например, system_r);
  • type — тип или домен безопасности SELinux (например, container_t или специализированный домен вида spc_t для супер-привилегированных контейнеров);
  • level — уровень чувствительности и категории MCS (например, s0:c100,c200). Применяется для явного разделения доступа к общим дискам.

По умолчанию Kubernetes автоматически проставляет лейблы (выполняет relabeling) на все смонтированные постоянные тома (PersistentVolumes), присваивая файлам на диске тот же MCS-уровень (level), с которым запускается под. Если вам нужно отключить этот автоматический процесс (например, диск монтируется одновременно в несколько подов в режиме ReadWriteMany и автоматическая смена лейблов ломает доступ), в настройках тома (spec.volumes[].persistentVolumeClaim) используется специальная точка монтирования с флагом mountOptions: ["context=xyz"].

Пример конфигурации

Пример конфигурации пода, запускаемого со строго зафиксированным контекстом SELinux для работы со специализированным типом данных и разделяемой директорией:

apiVersion: v1
kind: Pod
metadata:
  name: secure-selinux-pod
spec:
  securityContext:
    seLinuxOptions:
      user: "system_u"
      role: "system_r"
      type: "container_t"
      level: "s0:c123,c456" # Явная фиксация MCS-категорий для доступа к общим файлам.
  containers:
    - name: app
      image: nginx

capabilities

Параметр capabilities отвечает за управление привилегиями ядра Linux (Linux Capabilities) для процессов внутри контейнера.

Linux Capabilities — это механизм ядра Linux, который разбивает полномочия суперпользователя (root) на десятки независимых привилегий. Каждая привилегия имеет собственный текстовый идентификатор. Всего ядро Linux имеет около 40 различных capabilities. Вот самые частые из них:

  • NET_BIND_SERVICE — позволяет процессу (даже не от имени root) слушать системные порты (например, 80 или 443);
  • SYS_TIME — разрешает изменять системное время на узле;
  • NET_ADMIN — позволяет настраивать сетевые интерфейсы, правила маршрутизации и файрвол внутри сети контейнера (часто используется в service mesh — например, Istio);
  • CHOWN — разрешает произвольно менять владельца файлов.

С помощью capabilities реализуется принцип наименьших привилегий. С его помощью можно дать пользователю одну конкретную привилегию ядра без выдачи полного root-доступа, либо отобрать некоторые привилегии у пользователя root.

Расположение параметра

Параметр состоит из двух частей:

  • Drop — список привилегий, которые будут запрещены контейнеру;
  • Add — список привилегий, которые будут разрешены контейнеру.

Параметр задаётся на уровне контейнера в полях spec.containers[].securityContext.capabilities.add и spec.containers[].securityContext.capabilities.drop.

Итоговый расчёт разрешенных привилегий производится по формуле (Default + Add) - Drop, где Default — это стандартный набор контейнерного рантайма (containerd/CRIO).

Таким образом, приоритет имеют операции запрета привилегий. Если разрешить и запретить одну и ту же привилегию, то в итоге привилегия будет запрещена.

Исключение — обработка при drop: ALL. В этом случае все привилегии сначала удаляются, а затем добавляются только те, что перечислены в поле add.

Доступные значения параметра

Параметры Add и Drop представлены в виде массива строк и не валидируются на уровне Kubernetes. Допустимо указанию любых значений. Полный перечень поддерживаемых привилегий ядра можно узнать в документации Linux.

Рекомендуемый практический подход:

  1. Начинайте с drop: ["ALL"].
  2. Добавляйте в add только минимально необходимые capabilities.

Примеры конфигурации

  1. Одинаковые привилегии в add и drop:

    securityContext:
      capabilities:
        add: ["NET_ADMIN"]
        drop: ["NET_ADMIN"]
    

    В результате привилегия NET_ADMIN будет запрещена (запрет имеет приоритет).

  2. Добавление одной привилегии без drop:

    securityContext:
      capabilities:
        add: ["NET_BIND_SERVICE"]
    

    Итоговый набор capabilities:

    • все capabilities из Default (стандартный набор контейнерного рантайма);
    • а также NET_BIND_SERVICE (если её не было в Default).
  3. Удаление части привилегий:

    securityContext:
      capabilities:
        drop: ["CHOWN", "FOWNER"]
    

    В итоге будут разрешены все capabilities из Default (стандартный набор контейнерного рантайма), кроме CHOWN и FOWNER.

  4. Жёсткое ограничение через drop: ["ALL"]:

    securityContext:
      capabilities:
        drop: ["ALL"]
        add: ["NET_BIND_SERVICE"]
    

    В итоге будет разрешена только capability NET_BIND_SERVICE.

allowPrivilegeEscalation

Параметр allowPrivilegeEscalation определяет, может ли процесс внутри контейнера получить больше прав, чем его родительский процесс.

Он напрямую управляет системным флагом ядра Linux no_new_privs для запускаемого контейнера. Если вы устанавливаете allowPrivilegeEscalation: false, Kubernetes включает этот флаг, блокируя любые механизмы повышения прав.

Главный инструмент повышения прав в Linux — это файлы с флагами SUID (Set User ID) или SGID (Set Group ID). Когда обычный пользователь запускает SUID-файл, процесс временно получает права владельца этого файла (часто это root).

Например, утилиты passwd (для смены пароля) или sudo имеют флаг SUID. Обычный пользователь запускает sudo, и ядро Linux временно повышает его привилегии до уровня суперпользователя, чтобы выполнить системную задачу.

При использовании allowPrivilegeEscalation: false ядро Linux активирует режим no_new_privs. При этом:

  • блокируются SUID/SGID-файлы — при попытке запустить sudo или passwd ядро проигнорирует SUID-флаг. Утилита выполнится с правами текущего пользователя контейнера и выдаст ошибку Permission denied;
  • запрещается активация capabilities — процесс не сможет приобрести новые Linux Capabilities в ходе своей работы (например, через выполнение файлов с предустановленными файловыми мандатами).

Даже если контейнер запущен от имени пользователя root (UID 0), Docker и Kubernetes по умолчанию урезают часть системных прав (capabilities). Однако если в контейнере доступно повышение привилегий, злоумышленник, получивший доступ к приложению, может найти уязвимость в ядре Linux или системной утилите внутри контейнера и использовать SUID-вызовы для «побега из контейнера» (container escape) на хостовый узел с полными правами root. Настройка allowPrivilegeEscalation: false закрывает этот вектор атаки.

Если вы делаете файловую систему контейнера доступной только для чтения (readOnlyRootFilesystem: true), это косвенно мешает злоумышленнику создать свой SUID-файл, но не защищает от использования уже существующих в образе утилит.

Расположение параметра

Параметр задаётся на уровне контейнера в поле spec.containers[].securityContext.allowPrivilegeEscalation.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — повышение привилегий запрещено (рекомендуется для большинства приложений);
  • true — повышение привилегий разрешено.

Пример конфигурации

Полный запрет на повышение прав:

spec:
  containers:
  - name: secure-app
    image: my-app:latest
    securityContext:
      allowPrivilegeEscalation: false 

privileged

Параметр privileged определяет, запускается ли контейнер с полным доступом к хостовой операционной системе на узле на уровне ядра.

При установке параметра privileged: true Kubernetes полностью отключает все стандартные механизмы изоляции контейнера. С точки зрения безопасности, этот контейнер становится обычным процессом, запущенным от имени root прямо на хосте, хотя и запертым в своем сетевом и cgroup-пространстве.

При выставлении параметра privileged: true происходят следующие изменения:

  • выдача доступа ко всем capabilities — контейнер получает все Linux Capabilities (около 40 штук);
  • конфигурации add и drop в securityContext для этого контейнера начинают игнорироваться.
  • доступ к устройствам хоста — внутри контейнера в директории /dev появляются все физические устройства узла (жёсткие диски, NVMe-накопители, графические карты, USB-порты). Контейнер может напрямую читать и писать в них;
  • отключение AppArmor и SELinux — системы принудительного контроля доступа (MAC) на хосте перестают накладывать ограничения на процессы внутри этого контейнера;
  • обход sysfs и procfs — контейнер может свободно изменять параметры ядра хоста через /sys и /proc.

Флаг privileged: true — это самая большая брешь в безопасности, если он выдан недоверенному приложению. Злоумышленник, получивший доступ в такой контейнер, может гарантированно скомпрометировать весь узел за одну команду. Например, он может выполнить команду mount для корневого диска хоста и получить полный контроль над файлами операционной системы узла, совершив «побег из контейнера» (container escape).

Обычным бизнес-приложениям этот флаг категорически противопоказан. Он необходим исключительно для системных компонентов кластера: сетевых плагинов (CNI), драйверов хранилищ (CSI) или низкоуровневых агентов мониторинга.

Расположение параметра

Параметр задаётся на уровне контейнера в поле spec.containers[].securityContext.privileged.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — привилегированный режим отключен (значение по умолчанию, рекомендуется для безопасности);
  • true — привилегированный режим включен.

Пример конфигурации

Пример конфигурации, включающей максимальные права на хосте для системной утилиты:

spec:
  containers:
  - name: admin-tool
    image: ubuntu
    securityContext:
      privileged: true 

procMount

Параметр procMount определяет, как подкаталоги системной файловой системы /proc будут монтироваться внутри контейнера.

Он напрямую управляет уровнем изоляции системной информации ядра Linux. С помощью этого параметра можно отключить стандартные защитные маски Kubernetes, которые скрывают от процессов контейнера критически важные и потенциально опасные системные пути хоста.

Файловая система /proc в Linux является окном в ядро. Через неё можно не только читать системные метрики, но и изменять параметры конфигурации ОС на лету. По умолчанию контейнерные рантаймы (containerd, CRI-O) используют защитные маски (MaskedPaths), которые скрывают или делают доступными только для чтения критические пути (например, /proc/sys, /proc/sysrq-trigger, /proc/scsi).

Например, если обычному контейнеру нужно изменить параметры сетевого стека через sysctl, защитная маска ядра заблокирует запись в /proc/sys/net, выдавая ошибку Read-only file system, чтобы защитить хостовую операционную систему от несанкционированных изменений.

При использовании procMount Kubernetes передаёт инструкцию рантайму изменить тип монтирования. При этом:

  • включается стандартная защита (Default) — применяются все стандартные маски контейнеризации. Опасные системные пути хоста скрываются или блокируются на запись;
  • снимается системная изоляция (Unmasked) — отключаются все защитные маски для файловой системы /proc. Контейнер видит структуру ядра хоста «как есть», без ограничений на чтение и запись.

Установка значения Unmasked открывает прямой доступ к конфигурации ядра хоста. Если злоумышленник скомпрометирует контейнер с отключенным маскированием, он сможет использовать уязвимости ядра или напрямую изменить системные параметры узла (например, через /proc/sysrq-trigger вызвать мгновенную панику ядра или перезагрузку хоста). Этот параметр является критическим вектором для «побега из контейнера» (container escape).

Использование типа Unmasked требуется крайне редко. Основной сценарий его применения — запуск специализированных инструментов внутри контейнеров, таких как инструменты сборки образов (Kaniko, Buildah и др.) или вложенные контейнеры (nested containerization), которым необходим полноценный, немодифицированный доступ к подсистемам /proc для эмуляции процессов.

Расположение параметра

Параметр задаётся на уровне контейнера в поле spec.containers[].securityContext.procMount.

Доступные значения параметра

Параметр имеет строковый тип (string). Доступны следующие значения:

  • Default — стандартное маскирование опасных путей ядра (рекомендуется по умолчанию);
  • Unmasked — отключение защитных масок и предоставление полного доступа к /proc.

Пример конфигурации

Пример конфигурации с отключением защитных масок /proc для специализированного контейнера сборщика:

spec:
  containers:
  - name: image-builder
    image: kaniko-project/executor:latest
    securityContext:
      procMount: Unmasked

sysctls

Параметр sysctls определяет список изменяемых параметров ядра Linux, которые можно безопасно или небезопасно переопределить для неймспейса конкретного пода.

Он напрямую управляет поведением сетевого стека, памяти и виртуальной файловой системы на уровне контейнеров. С помощью этого параметра можно оптимизировать производительность высоконагруженных приложений (например, баз данных или веб-серверов) без необходимости менять глобальные настройки операционной системы на всей хост-узле.

В операционной системе Linux утилита sysctl позволяет изменять конфигурацию ядра во время работы системы через интерфейс /proc/sys/. Параметры ядра делятся на изолированные (на уровне неймспейсов контейнера) и глобальные (влияющие на всю физический узел).

Например, по умолчанию максимальное количество незавершенных соединений в очереди (somaxconn) ограничено небольшим системным значением. Высоконагруженному балансировщику трафика NGINX этого может не хватать, из-за чего он начнет отбрасывать пакеты. С помощью sysctls контейнеру можно индивидуально выделить увеличенный размер очереди.

В кластере DKP ряд параметров sysctls настраивается автоматически при установке. Ознакомиться с полным списком таких параметров можно в разделе «Параметры sysctl, настраиваемые платформой».

При настройке sysctls Kubernetes делит все параметры на две категории, требующие разного уровня доверия. При этом:

  • разрешаются безопасные параметры (Safe sysctls) — параметры, которые полностью изолированы внутри контейнера и их изменение не может навредить соседним подам или стабильности узла. Kubernetes применяет их сразу (например, net.ipv4.ip_local_port_range);
  • блокируются небезопасные параметры (Unsafe sysctls) — параметры, которые могут повлиять на весь узел (вызвать перегрузку памяти, нарушить общую маршрутизацию). По умолчанию Kubernetes заблокирует поды с такими параметрами. Для их работы администратор кластера должен предварительно разрешить их в конфигурации kubelet через флаг --allowed-unsafe-sysctls.

Актуальный список безопасных sysctl-параметров доступен в документации Kubernetes.

Бесконтрольное использование небезопасных sysctls может привести к отказу в обслуживании (DoS) всего сервера. Если злоумышленник получит доступ к контейнеру с правами на изменение глобальных сетевых параметров, он сможет нарушить связность других подов на этом узле или забить системную память, спровоцировав аварийное завершение критических процессов хоста (Out of Memory).

В Kubernetes существует лишь несколько безопасных параметров ядра (в основном это kernel.shm_rmid_forced и часть сетевых параметров net.ipv4.* для локальной сети контейнера). Если вам требуется применить параметр из категории Unsafe, рекомендуется вынести такие приложения на изолированные группы узлов (dedicated node pools) с помощью taints и tolerations, чтобы минимизировать риски для остального кластера.

Расположение параметра

Параметр задаётся исключительно на уровне всего пода в поле spec.securityContext.sysctls.

Доступные значения параметра

Параметр принимает список объектов (array), где каждый элемент состоит из пары ключ-значение (name и value), имеющих строковый тип (string).

Пример конфигурации

Пример настройки параметров локального сетевого стека для высоконагруженного пода:

spec:
  securityContext:
    sysctls:
    - name: net.core.somaxconn
      value: "8192"
    - name: net.ipv4.ip_local_port_range
      value: "1024 65535"
  containers:
  - name: high-load-nginx
    image: nginx:latest

hostNetwork

Параметр hostNetwork определяет, будет ли под использовать сетевой неймспейс узла, на котором он запущен, вместо отдельного изолированного сетевого неймспейса.

По умолчанию каждый под получает собственный сетевой неймспейс (netns) с виртуальным сетевым интерфейсом, отдельным IP-адресом и собственными правилами маршрутизации. Трафик изолирован от хоста и соседних подов сетевым плагином (CNI). Параметр hostNetwork: true отключает эту изоляцию.

При использовании hostNetwork: true Kubernetes даёт команду контейнерному рантайму не создавать отдельный сетевой неймспейс. При этом:

  • разделяется сетевой стек хоста — контейнер использует сетевые интерфейсы узла (eth0, loopback), его IP-адрес и таблицы маршрутизации напрямую;
  • открываются порты на хосте — любой порт, который слушает приложение в контейнере, автоматически открывается на интерфейсах узла и доступен извне кластера;
  • игнорируется CNI — сетевой плагин кластера не назначает поду отдельный IP-адрес и не применяет сетевые политики (NetworkPolicy) к его трафику.

Включение hostNetwork: true разрушает сетевую изоляцию пода. Приложение получает прямой доступ ко всем сетевым интерфейсам узла, может перехватывать чужой трафик (sniffing), вмешиваться в маршрутизацию кластера или конфликтовать портами с системными службами хоста. Сетевые политики Kubernetes перестают действовать, так как трафик пода неотличим от трафика самого узла. Злоумышленник, скомпрометировавший такой под, получает возможность атаковать соседние поды и узлы напрямую, минуя механизмы микросегментации.

hostNetwork часто используется вместе с параметром dnsPolicy: ClusterFirstWithHostNet. Без явного указания этой политики под с hostNetwork: true не сможет корректно разрешать имена внутренних сервисов кластера через CoreDNS, так как по умолчанию будет использовать резолверы хоста.

Расположение параметра

Параметр задаётся в поле spec.hostNetwork исключительно на уровне всего пода, так как сетевой неймспейс создаётся для пода целиком.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — под использует собственный изолированный сетевой неймспейс (значение по умолчанию, рекомендуется);
  • true — под использует сетевой неймспейс узла.

Пример конфигурации

Пример конфигурации пода, использующего сеть узла (например, системный сетевой агент):

apiVersion: v1
kind: Pod
metadata:
  name: host-network-pod
spec:
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet
  containers:
    - name: network-agent
      image: my-agent:latest

hostPID

Параметр hostPID определяет, будет ли под использовать PID-неймспейс узла вместо собственного изолированного.

В Linux идентификаторы процессов (PID) уникальны только в рамках одного неймспейса. По умолчанию каждый под получает собственный PID-неймспейс: процессы внутри контейнера видят только сами себя (PID 1 и его дочерние процессы), а процессы хоста и соседних подов для них невидимы. Параметр hostPID: true отключает эту изоляцию.

При использовании hostPID: true контейнерный рантайм не создаёт отдельный PID-неймспейс для пода. При этом:

  • видны все процессы узла — процессы внутри контейнера видят полный список процессов, запущенных на узле (аналог команды ps aux на хосте);
  • доступно взаимодействие с процессами хоста — при наличии достаточных прав процессы контейнера могут посылать сигналы (kill, SIGTERM) процессам узла или инспектировать их память через /proc.

Параметр hostPID: true открывает процессам контейнера окно в операционную систему узла. Злоумышленник, скомпрометировавший такой под, может анализировать запущенные на узле системные компоненты (включая kubelet, containerd, другие поды), собирать информацию о командных строках и аргументах процессов, а при наличии привилегий — завершать чужие процессы, вызывая отказ в обслуживании. Кроме того, доступ к /proc чужих процессов позволяет читать их переменные окружения, в которых часто находятся секреты и токены.

Возможность отправлять сигналы процессам хоста зависит от привилегий пользователя внутри контейнера. Даже с hostPID: true некорневой контейнер не сможет напрямую управлять процессами root на хосте, однако чтение метаданных процессов через /proc останется доступным.

Расположение параметра

Параметр задаётся исключительно на уровне всего пода в поле spec.hostPID.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — под использует собственный изолированный PID-неймспейс (значение по умолчанию, рекомендуется);
  • true — под использует PID-неймспейс узла.

Пример конфигурации

Пример конфигурации пода с доступом к процессам узла (например, для системного мониторинга):

apiVersion: v1
kind: Pod
metadata:
  name: host-pid-pod
spec:
  hostPID: true
  containers:
    - name: process-inspector
      image: my-tool:latest

hostIPC

Параметр hostIPC определяет, будет ли под использовать IPC-неймспейс узла вместо собственного изолированного.

В Linux IPC-механизмы (очереди сообщений, разделяемая память, семафоры) разделяются по неймспейсам. По умолчанию каждый под получает собственный IPC-неймспейс, изолированный от хоста и соседних подов. Это означает, что процессы контейнера не могут использовать IPC-ресурсы, созданные процессами узла или другими подами. Параметр hostIPC: true отключает эту изоляцию.

При использовании hostIPC: true контейнерный рантайм использует IPC-неймспейс узла. При этом:

  • разделяются IPC-объекты хоста — очереди сообщений (message queues), сегменты разделяемой памяти (shared memory) и семафоры узла становятся доступны процессам контейнера;
  • доступно межпроцессное взаимодействие с хостом — контейнер может читать и модифицировать IPC-объекты, созданные системными процессами узла.

Параметр hostIPC: true создаёт канал обмена данными между контейнером и процессами узла в обход стандартных сетевых и файловых интерфейсов. Злоумышленник, скомпрометировавший такой под, может перехватывать или подменять данные, которыми обмениваются системные службы хоста через IPC, а также внедрять вредоносные payload в разделяемую память, эксплуатируя уязвимости процессов, читающих эти сегменты. На практике параметр применяется крайне редко — в основном для устаревших-приложений, использующих System V IPC для синхронизации с процессами, запущенными непосредственно на узле.

Большинство современных приложений не используют System V IPC, предпочитая сетевые сокеты или стандартные файловые дескрипторы. Поэтому hostIPC почти всегда можно безопасно оставить в значении false без потери функциональности.

Расположение параметра

Параметр задаётся в поле spec.hostIPC исключительно на уровне всего пода.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • false — под использует собственный изолированный IPC-неймспейс (значение по умолчанию, рекомендуется);
  • true — под использует IPC-неймспейс узла.

Пример конфигурации

Пример конфигурации пода с доступом к IPC-ресурсам узла:

apiVersion: v1
kind: Pod
metadata:
  name: host-ipc-pod
spec:
  hostIPC: true
  containers:
    - name: ipc-client
      image: my-app:latest

hostPath

Параметр hostPath определяет возможность монтирования произвольных файлов и директорий файловой системы узла напрямую в контейнер.

Обычно контейнеры работают с данными через изолированные тома Kubernetes (emptyDir, PersistentVolume, configMap), которые не дают прямого доступа к диску узла. Тип тома hostPath обходит эту абстракцию и пробрасывает указанный путь файловой системы хоста внутрь контейнера.

При настройке hostPath Kubernetes монтирует указанный путь с узла в контейнер. При этом:

  • разделяется файловая система хоста — контейнер получает прямой доступ к файлам и директориям узла по указанному пути (path);
  • игнорируется изоляция хранилища — доступ не ограничен PersistentVolume или квотами. Контейнер работает с реальными файлами узла;
  • тип монтирования управляется полем type — доступны значения "" (проверка отключена), Directory, File, Socket, CharDevice, BlockDevice. Они определяют, какой тип объекта должен существовать по пути до монтирования.

Параметр hostPath — один из самых опасных типов томов, так как он открывает контейнеру прямой доступ к файловой системе узла. Злоумышленник, получивший доступ в такой контейнер, может прочитать конфиденциальные файлы хоста (например, /etc/shadow, закрытые ключи, токены kubelet), модифицировать системные исполняемые файлы или подменить конфигурацию критических служб узла. Монтирование корневых или системных путей (/, /var/lib/kubelet, /etc, /proc, /sys) фактически эквивалентно выдаче полного контроля над узлом и является прямым путём к «побегу из контейнера» (container escape).

При использовании hostPath настоятельно рекомендуется монтировать том в режиме только для чтения (readOnly: true), если приложению не требуется запись. Поле type также следует указывать явно, чтобы Kubernetes проверял существование и тип объекта перед монтированием. Это предотвращает непредвиденное создание файлов на хосте.

Расположение параметра

Том hostPath описывается в массиве томов пода и затем монтируется в конкретный контейнер:

  • spec.volumes[].hostPath — описание тома;
  • spec.containers[].volumeMounts — точка монтирования внутри контейнера.

Доступные значения параметра

Объект hostPath содержит следующие поля:

  • path — абсолютный путь в файловой системе узла, который будет смонтирован (строка, обязательное поле);
  • type — тип объекта по указанному пути (строка, опционально). Доступны значения:
    • "" — проверка типа отключена (значение по умолчанию);
    • Directory — директория должна существовать;
    • DirectoryOrCreate — директория будет создана, если отсутствует;
    • File — файл должен существовать;
    • FileOrCreate — файл будет создан, если отсутствует;
    • Socket — UNIX-сокет должен существовать;
    • CharDevice — символьное устройство должно существовать;
    • BlockDevice — блочное устройство должно существовать.

Пример конфигурации

Пример конфигурации с монтированием конфигурационного файла узла в режиме только для чтения:

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
    - name: app
      image: my-app:latest
      volumeMounts:
        - mountPath: /host/etc/app.conf
          name: host-config
          readOnly: true
  volumes:
    - name: host-config
      hostPath:
        path: /etc/app/app.conf
        type: File

automountServiceAccountToken

Параметр automountServiceAccountToken определяет, будет ли Kubernetes автоматически монтировать токен ServiceAccount в контейнер по стандартному пути.

В Kubernetes каждый под по умолчанию ассоциируется с ServiceAccount (если не указан явно — используется default). В целях аутентификации в API-сервере Kubernetes автоматически генерирует для этого ServiceAccount-токен (ServiceAccountToken) и монтирует его в каждый контейнер пода по пути /var/run/secrets/kubernetes.io/serviceaccount. Этот токен позволяет процессам внутри контейнера обращаться к API Kubernetes от имени своего ServiceAccount.

При использовании automountServiceAccountToken: false Kubernetes не монтирует токен ServiceAccount в контейнер. При этом:

  • отсутствует файл токена — по пути /var/run/secrets/kubernetes.io/serviceaccount не создаётся файл token;
  • нет доступа к API от имени ServiceAccount — процессы контейнера не могут автоматически аутентифицироваться в API-сервере Kubernetes с правами своего ServiceAccount;
  • сохраняется возможность ручного монтирования — при необходимости токен можно смонтировать явно через тип тома serviceAccountToken, либо использовать современные ограниченные по времени токены (BoundServiceAccountTokenVolume), появившиеся в Kubernetes 1.21+.

Автоматически смонтированный токен ServiceAccount — это готовый credential, который злоумышленник может использовать для горизонтального перемещения по кластеру. Если приложение не работает с API Kubernetes (например, обычный веб-сервер или база данных), монтирование токена создаёт избыточный риск: при компрометации контейнера злоумышленник получает возможность обращаться к API-серверу с правами ServiceAccount пода, в том числе читать секреты, перечислять ресурсы и, при избыточных правах (RBAC), атаковать другие компоненты кластера. Отключение автоматического монтирования реализует принцип наименьших привилегий и уменьшает поверхность атаки.

Параметр automountServiceAccountToken можно задавать как на уровне ServiceAccount, так и на уровне самого пода (spec.automountServiceAccountToken). Значение, указанное в поде, имеет приоритет над значением в ServiceAccount. Также важно убедиться, что приложение действительно не использует API Kubernetes, прежде чем отключать монтирование токена.

Расположение параметра

Параметр задаётся:

  • в поле spec.automountServiceAccountToken — на уровне всего пода;
  • в поле serviceAccount.automountServiceAccountToken — на уровне ресурса ServiceAccount.

Доступные значения параметра

Параметр имеет булевый тип (boolean). Доступны следующие значения:

  • true — токен ServiceAccount монтируется автоматически (значение по умолчанию, если не указано иное);
  • false — автоматическое монтирование токена отключено (рекомендуется для подов, не взаимодействующих с API Kubernetes).

Пример конфигурации

Пример конфигурации пода с отключённым автоматическим монтированием токена ServiceAccount:

apiVersion: v1
kind: Pod
metadata:
  name: no-sa-token-pod
spec:
  automountServiceAccountToken: false
  containers:
    - name: app
      image: my-app:latest

Дополнительные ресурсы