Зачем
capabilities делят root-привилегии. Но даже простой пользовательский процесс может вызвать ~350 разных syscall'ов, и в любом потенциально есть бага → уязвимость. Лучшая защита - отнять у процесса возможность делать syscall'ы, которые ему не нужны.
Например, web-сервер не должен звать mount(), reboot(), kexec_load(),
ptrace(). Если их можно ОТКЛЮЧИТЬ - целая поверхность атак отрезана.
Два режима
- SECCOMP_MODE_STRICT (старый, 2005) - оставляет только
read,write,_exit,sigreturn. Слишком жёстко, никем не используется. - SECCOMP_MODE_FILTER (BPF, 2012) - программа на BPF фильтрует syscall'ы по номеру и аргументам. Это и есть «seccomp» сегодня.
Программа BPF получает на вход номер syscall'а и аргументы, возвращает одно из:
| Action | Что делает |
|---|---|
SECCOMP_RET_ALLOW | пропустить |
SECCOMP_RET_ERRNO(n) | заблокировать с возвратом ошибки n (типично EPERM) |
SECCOMP_RET_KILL_PROCESS | убить процесс целиком |
SECCOMP_RET_KILL_THREAD | убить только этот thread |
SECCOMP_RET_TRAP | SIGSYS → можно обработать |
SECCOMP_RET_LOG | пропустить + записать в audit |
SECCOMP_RET_USER_NOTIF | передать в userspace для решения (новое; для контейнеров) |
Как программа включает seccomp
Через системный вызов prctl() или seccomp(). Обычно используют
библиотеку libseccomp чтобы не писать BPF руками:
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ERRNO(EPERM)); // default = блок
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_load(ctx);
// теперь любой syscall кроме разрешённых = EPERM
Filter - необратимый: можно только сужать, нельзя расширять.
Docker default profile
Docker применяет seccomp profile
по умолчанию. Заблокирован ~50 syscall'ов: clone3 (CVE-mitigation,
раньше), kexec_load, keyctl, reboot, mount (требует CAP_SYS_ADMIN
отдельно), часть ptrace-вариантов и т.д.
docker run --security-opt seccomp=unconfined ubuntu # ВЫКЛЮЧИТЬ - для дебага
docker run --security-opt seccomp=/path/profile.json # свой профиль
В Kubernetes:
securityContext:
seccompProfile:
type: RuntimeDefault # docker-style профиль
# или
type: Localhost
localhostProfile: profiles/audit.json
Связь с другими механизмами
- AppArmor / SELinux - фильтр на уровне MAC (file/path/network)
- capabilities - что разрешено root-привилегий
- seccomp - что разрешено по syscall'ам
Это разные слои; на проде стоит включать все три + namespace-изоляцию. Это и есть подход «defense in depth».
Дебаг seccomp-нарушений
Когда процесс падает с EPERM «непонятно от чего», подозрение на seccomp:
# 1. Посмотреть фильтры процесса
cat /proc/<pid>/status | grep ^Seccomp
# Seccomp: 2 ← 0=disabled, 1=strict, 2=filter
# 2. strace - увидим запрещённый syscall
strace -p <pid> # error: Operation not permitted на конкретном syscall
# 3. dmesg - если профиль настроен на LOG
sudo dmesg | grep audit
Для разработки своего профиля Docker запускают приложение с режимом
RET_LOG (всё пропускать но логировать), собирают список реально
использованных syscall'ов, потом строят минимальный whitelist.
Системные тулзы
scmp_sys_resolver <number>- расшифровать номер syscall'а в имяseccomp-tools(третья сторона) - дамп BPF из работающего процессаfalco- observability + audit с seccomp