Seccomp
Профили Seccomp для подов и контейнеров Kubernetes, размещение профилей на узлах через NCI и безопасное внедрение ограничений системных вызовов.
Общая информация
Seccomp (Secure computing mode) — механизм безопасности ядра Linux, который ограничивает системные вызовы из пользовательского пространства в ядро. В Kubernetes профили Seccomp применяются к подам и контейнерам, чтобы уменьшить поверхность атаки: процесс внутри контейнера получает только те возможности ядра, которые разрешены выбранным профилем.
Профиль Seccomp — это JSON-файл в формате, совместимом с OCI runtime specification. В нём задаются действие по умолчанию, списки системных вызовов и, при необходимости, условия по аргументам системных вызовов.
Основные действия профиля:
- SCMP_ACT_ALLOW — разрешить вызов (белый список).
- SCMP_ACT_ERRNO — вернуть процессу ошибку для заблокированного вызова.
- SCMP_ACT_LOG — разрешить вызов, но записывать его в лог (аудит).
- SCMP_ACT_KILL_PROCESS — завершить процесс.
- SCMP_ACT_KILL_THREAD или SCMP_ACT_KILL — завершить поток.
- SCMP_ACT_TRAP — отправить процессу сигнал
SIGSYS. - SCMP_ACT_TRACE — уведомить трассирующий процесс.
Для универсальных профилей обычно используют SCMP_ACT_LOG, SCMP_ACT_ERRNO и SCMP_ACT_ALLOW.
Параметр defaultAction определяет, что делать с системными вызовами, которые не попали ни в одно правило. Например:
SCMP_ACT_LOGподходит для аудита: вызовы выполняются, но фиксируются в журнале.SCMP_ACT_ERRNOподходит для жёсткого профиля: неразрешённые вызовы блокируются.
Логи блокировок и аудита смотрят на узле, где запущен Pod, в /var/log/messages или /var/log/syslog по ключевому слову SECCOMP либо по имени процесса.
Как Kubernetes применяет Seccomp
Профиль указывается в securityContext.seccompProfile. Его можно задать:
- для всего Pod:
spec.securityContext.seccompProfile; - для отдельного контейнера:
spec.containers[*].securityContext.seccompProfile; - для init-контейнера:
spec.initContainers[*].securityContext.seccompProfile; - для ephemeral-контейнера:
spec.ephemeralContainers[*].securityContext.seccompProfile.
Профиль на уровне контейнера имеет приоритет над профилем на уровне Pod. Если контейнер не задаёт свой seccompProfile, он наследует профиль Pod.
Доступные значения seccompProfile.type:
| Значение | Поведение |
|---|---|
RuntimeDefault |
Применяется профиль по умолчанию, который предоставляет container runtime. Такие профили рассчитаны на безопасные настройки без потери базовой работоспособности, но могут отличаться между CRI-O, containerd и разными версиями runtime. |
Localhost |
Применяется локальный JSON-профиль с узла. На Linux профили размещаются относительно каталога /var/lib/kubelet/seccomp. Наличие файла проверяет container runtime при создании контейнера. Если файла нет, контейнер не создаётся и получает ошибку CreateContainerError. |
Unconfined |
Нагрузка запускается без ограничений Seccomp. |
Нельзя применить профиль Seccomp к Pod или контейнеру с privileged: true: privileged-контейнеры всегда запускаются как Unconfined.
В новых манифестах используйте поля securityContext.seccompProfile. Аннотации Seccomp, применявшиеся в старых версиях Kubernetes, не нужны для актуальной конфигурации.
Базовый вариант: RuntimeDefault
Для большинства нагрузок сначала стоит проверить профиль RuntimeDefault. Он включает ограничения, заданные container runtime, и не требует размещения отдельного JSON-файла на узлах.
apiVersion: v1
kind: Pod
metadata:
name: runtime-default-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: hashicorp/http-echo:1.0
args:
- "-text=hello"
securityContext:
allowPrivilegeEscalation: false
Если seccompDefault включён в конфигурации kubelet или kubelet запущен с флагом --seccomp-default, Pod без явно заданного профиля получает RuntimeDefault. Если это поведение не включено, Pod без seccompProfile запускается как Unconfined.
Для внедрения RuntimeDefault по умолчанию включайте настройку поэтапно: сначала на части узлов, затем проверяйте запуск и основные сценарии работы приложений. Некоторые нагрузки могут требовать менее строгих ограничений. Для таких случаев используйте один из вариантов: явно указать Unconfined, подготовить отдельный Localhost-профиль или запускать нагрузку на узлах без seccompDefault.
Проверить фактически применённый профиль можно средствами container runtime, например через crictl inspect на узле.
Создание локального профиля через NCI
Для кастомного профиля создайте JSON-файл и разместите его на Linux-узлах. В платформе «Штурвал» это выполняется с помощью ресурса NCI (NodeConfigItem). Подробнее о создании и применении NCI — на странице Конфигурация узлов.
Создать NCI можно через импорт манифеста или в интерфейсе: Администрирование → Конфигурация узлов. При создании объекта выберите в селекторе узлов kubernetes.io/os: linux, в настраиваемых разделах выберите Файлы и директории, создайте директорию и разместите в ней JSON-файл профиля.
Ниже пример NCI, который создаёт каталог /var/lib/kubelet/seccomp/ и файл /var/lib/kubelet/seccomp/myprofile.json.
apiVersion: node.shturval.tech/v1beta2
kind: NodeConfigItem
metadata:
name: seccomp-profile
spec:
files:
- path: /var/lib/kubelet/seccomp/
type: directory
owner: root
group: root
mode: "0755"
- path: /var/lib/kubelet/seccomp/myprofile.json
type: file
owner: root
group: root
mode: "0644"
content: |-
{
"defaultAction": "SCMP_ACT_LOG",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
"syscalls": [
{
"names": ["arch_prctl", "sched_yield", "futex", "write", "mmap", "exit_group", "madvise", "rt_sigprocmask", "getpid", "gettid", "tgkill", "rt_sigaction", "read", "getpgrp"],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["add_key", "keyctl", "ptrace"],
"action": "SCMP_ACT_ERRNO"
}
]
}
nodeconfigselector:
kubernetes.io/os: linux
priority: 100
После применения NCI проверьте, что профиль появился на нужных узлах:
kubectl get nci seccomp-profile -o yaml
kubectl get nodes --selector kubernetes.io/os=linux
При необходимости проверьте файл непосредственно на узле средствами администрирования ОС. Kubernetes не хранит содержимое Localhost-профиля в Pod: при создании контейнера runtime ищет файл на том узле, куда был назначен Pod.
Запуск нагрузок с профилем Seccomp
Чтобы применить профиль, укажите type: Localhost и имя файла в localhostProfile. Значение localhostProfile задаётся относительно /var/lib/kubelet/seccomp.
Если файл расположен как /var/lib/kubelet/seccomp/myprofile.json, укажите:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
Если файл расположен во вложенном каталоге /var/lib/kubelet/seccomp/profiles/myprofile.json, укажите localhostProfile: profiles/myprofile.json.
Пример Pod с локальным профилем на уровне контейнера:
apiVersion: v1
kind: Pod
metadata:
name: nettool
namespace: namespacename
labels:
run: nettool
spec:
containers:
- name: nettool
image: r.shturval.tech/network-multitool:alpine-extra
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
Пример Pod, где общий профиль задан на уровне Pod, а отдельный контейнер переопределяет его:
apiVersion: v1
kind: Pod
metadata:
name: seccomp-priority
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: hashicorp/http-echo:1.0
args:
- "-text=hello"
- name: custom
image: hashicorp/http-echo:1.0
args:
- "-text=custom"
securityContext:
seccompProfile:
type: Localhost
localhostProfile: myprofile.json
В интерфейсе при создании или редактировании Pod или Deployment профиль настраивается в блоке Security context на уровне Pod или контейнера. Для локального профиля выберите использование настроенного профиля и укажите имя файла.
Создание профиля по результатам аудита
Для прикладных контейнеров безопаснее двигаться от наблюдения к ограничению:
- Создайте аудит-профиль с
defaultAction: SCMP_ACT_LOG. - Запустите нагрузку с этим профилем.
- Выполните основные пользовательские и служебные сценарии приложения.
- Соберите системные вызовы из журналов узла.
- Создайте профиль с
defaultAction: SCMP_ACT_ERRNOи разрешите только необходимые вызовы черезSCMP_ACT_ALLOW. - Проверьте запуск, readiness/liveness-пробы, сетевые обращения, работу с файлами, graceful shutdown и обновление версии приложения.
Минимальный аудит-профиль:
{
"defaultAction": "SCMP_ACT_LOG"
}
Пример строгого профиля, который по умолчанию блокирует вызовы и разрешает только перечисленные:
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4",
"bind",
"brk",
"close",
"epoll_ctl",
"epoll_wait",
"exit",
"exit_group",
"futex",
"getpid",
"gettid",
"mmap",
"munmap",
"openat",
"read",
"rt_sigaction",
"rt_sigprocmask",
"socket",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Такой подход требует тестирования на конкретном приложении. Логи аудита не гарантируют, что за один тестовый прогон будут собраны все системные вызовы, которые приложение может выполнить в редких сценариях.
Диагностика
| Симптом | Возможная причина | Что проверить |
|---|---|---|
Pod создан, но контейнер не стартует с CreateContainerError |
Для Localhost указан несуществующий файл профиля |
Проверьте localhostProfile, наличие файла на узле и путь относительно /var/lib/kubelet/seccomp. |
Контейнер уходит в CrashLoopBackOff |
Профиль блокирует системный вызов, необходимый процессу | Посмотрите события Pod и журналы узла, временно включите аудит через SCMP_ACT_LOG, расширьте список разрешённых вызовов. |
| Профиль не ограничивает контейнер | Контейнер запущен с privileged: true или указан Unconfined |
Проверьте securityContext.privileged и фактически применённый профиль через инструменты runtime. |
| Разные результаты на разных узлах | На узлах отличаются файлы профиля, container runtime или версии runtime | Проверьте NCI, размещение файла на каждом целевом узле и версию runtime. |
Команды для первичной проверки:
kubectl describe pod <pod-name> -n <namespace>
kubectl get pod <pod-name> -n <namespace> -o yaml
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>
Для локальных профилей дополнительно проверьте, на какой узел назначен Pod:
kubectl get pod <pod-name> -n <namespace> -o wide
Рекомендации
- Начинайте с
RuntimeDefault. Это базовое усиление безопасности без сопровождения собственного JSON-профиля. - Используйте
Localhostдля критичных и хорошо изученных нагрузок. Кастомный профиль полезен, когда требуется явно ограничить набор системных вызовов конкретного приложения. - Идите от аудита к блокировке. Сначала соберите системные вызовы с
SCMP_ACT_LOG, затем переходите кSCMP_ACT_ERRNOиSCMP_ACT_ALLOW. - Задавайте профиль на уровне Pod, если он общий для всех контейнеров. Переопределяйте профиль на уровне контейнера только для исключений.
- Не используйте
Unconfinedбез причины. Этот режим отключает ограничения Seccomp для нагрузки. - Не рассчитывайте на Seccomp для privileged-контейнеров. Контейнер с
privileged: trueвсегда работает какUnconfined. - Тестируйте профиль на полном сценарии работы приложения. Слишком жёсткий профиль может заблокировать легитимные вызовы уже после успешного старта контейнера.
- Контролируйте одинаковость профилей на узлах. Для
Localhostфайл должен быть доступен на каждом узле, куда может попасть Pod. - Учитывайте различия runtime. Профиль
RuntimeDefaultи поддержка отдельных действий Seccomp могут отличаться между container runtime и их версиями.