
Язык и библиотека для задания политик фильтрации системных вызовов.
Kafel — это язык и библиотека для задания политик фильтрации системных вызовов. Политики компилируются в BPF-код, который может использоваться с seccomp-filter.
Это НЕ официальный продукт Google.
struct sock_fprog prog;
kafel_ctxt_t ctxt = kafel_ctxt_create();
kafel_set_input_string(ctxt, seccomp_policy);
if (kafel_compile(ctxt, &prog)) {
fprintf(stderr, "policy compilation failed: %s", kafel_error_msg(ctxt));
kafel_ctxt_destroy(&ctxt);
exit(-1);
}
kafel_ctxt_destroy(&ctxt);
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
free(prog.filter);
struct sock_fprog prog;
if (kafel_compile_string(seccomp_policy, &prog)) {
fputs("policy compilation failed", stderr);
exit(-1);
}
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
free(prog.filter);
Для определения политик используется простой язык.
Файл политики состоит из утверждений.
Утверждением может быть:
Утверждения определения политики, размещенные на уровне файла, будут добавлены в неявную политику верхнего уровня. Эта политика верхнего уровня будет скомпилирована.
DEFAULT the_action
Указывает, что действие the_action должно быть выполнено, если ни одно правило не совпало.
Действие по умолчанию должно быть указано только один раз.
Если файл политики не указывает действий по умолчанию, действием по умолчанию будет KILL
Kafel поддерживает следующие форматы чисел:
420xfa107770b10101Вы можете определять числовые константы для улучшения читаемости политик. Определения констант могут быть размещены практически в любом месте файла политики. Определение константы не может быть размещено внутри определения политики. Определенные константы затем можно использовать везде, где ожидается число.
#define MYCONST 123
Определение политики — это список блоков действий и утверждений USE, разделенных запятыми.
samples/ содержит несколько примеров политик, демонстрирующих поддерживаемые функции.
USE someOtherPolicy ведет себя так, как если бы тело someOtherPolicy было вставлено на его
место. Можно использовать только политики, определенные до утверждения USE.
С помощью утверждений USE можно создавать осмысленные группы правил фильтрации, которые являются строительными блоками более крупных политик.
Блок действий состоит из цели и списка правил сопоставления системных вызовов, разделенных запятыми.
Цель первого совпавшего правила является решением политики.
В следующей таблице перечислены цели Kafel и соответствующие им возвращаемые значения seccomp-filter.
Правило состоит из имени системного вызова и необязательного списка логических выражений.
Список логических выражений разделяется запятыми.
Запятая семантически эквивалентна ||, но имеет самый низкий приоритет,
поэтому ее может быть проще читать.
Обычно системные вызовы задаются по их именам, как определено в ядре Linux.
Однако вы также можете фильтровать пользовательские системные вызовы, которых нет в стандартном
списке системных вызовов.
Вы можете либо определить константу и использовать ее вместо имени системного вызова, либо
воспользоваться ключевым словом SYSCALL.
#define mysyscall -1
POLICY my_const {
ALLOW {
mysyscall
}
}
POLICY my_literal {
ALLOW {
SYSCALL[-1]
}
}
Вы можете ограничить правило системного вызова подмножеством архитектур с помощью защиты ON.
Защита принимает либо одно имя архитектуры, либо список, разделенный запятыми, заключенный в фигурные скобки. Имена соответствуют целям, распознаваемым
kafel_set_target_archs (например, x86_64, x86, arm, aarch64,
mips64, riscv64, m68k).
ALLOW {
io_uring_setup ON x86_64,
arm_fadvise64_64 ON arm
}
При компиляции для архитектуры, не входящей в защиту, правило системного вызова игнорируется, поэтому межархитектурным политикам больше не нужно дублировать целые политики только для учета различий в доступности системных вызовов.
Логические выражения используются для фильтрации системных вызовов на основе их аргументов. Выражение напоминает синтаксис языка C, за исключением того, что в нем нет арифметических операторов.
some_syscall(first_arg, my_arg_name) { first_arg == 42 && my_arg_name != 42 }
Побитовое И (&) и ИЛИ ('|') могут использоваться для проверки флагов.
mmap { (prot & PROT_EXEC) == 0 },
open { flags == O_RDONLY|O_CLOEXEC }
Вам не нужно объявлять аргументы для хорошо известных системных вызовов, можно просто использовать
их обычные имена, как указано в ядре Linux и страницах man.
write { fd == 1 }
Для упрощения повторного использования и компоновки политик kafel предоставляет поддержку include.
#include "some_other_file.policy"
Kafel ищет включаемые файлы только в каталогах, явно добавленных в пути поиска.
kafel_add_include_search_path(ctxt, "includes/path");
Добавляет includes/path в пути поиска — тогда приведенный выше пример директивы include будет ссылаться на includes/path/some_other_file.policy.
Директива include завершается символом новой строки или точкой с запятой. В одной директиве можно указать несколько файлов, разделенных пробелами.
#include "first.policy" "second.policy"; #include "third.policy"
При использовании с nsjail следующая команда позволяет создать довольно ограниченное окружение для вашей оболочки
$ ./nsjail --chroot / --seccomp_string 'POLICY a { ALLOW { write, execve, brk, access, mmap, open, newfstat, close, read, mprotect, arch_prctl, munmap, getuid, getgid, getpid, rt_sigaction, geteuid, getppid, getcwd, getegid, ioctl, fcntl, newstat, clone, wait4, rt_sigreturn, exit_group } } USE a DEFAULT KILL' -- /bin/sh -i
[2017-01-15T21:53:08+0100] Mode: STANDALONE_ONCE
[2017-01-15T21:53:08+0100] Jail parameters: hostname:'NSJAIL', chroot:'/', process:'/bin/sh', bind:[::]:0, max_conns_per_ip:0, uid:(ns:1000, global:1000), gid:(ns:1000, global:1000), time_limit:0, personality:0, daemonize:false, clone_newnet:true, clone_newuser:true, clone_newns:true, clone_newpid:true, clone_newipc:true, clonew_newuts:true, clone_newcgroup:false, keep_caps:false, tmpfs_size:4194304, disable_no_new_privs:false, pivot_root_only:false
[2017-01-15T21:53:08+0100] Mount point: src:'/' dst:'/' type:'' flags:0x5001 options:''
[2017-01-15T21:53:08+0100] Mount point: src:'(null)' dst:'/proc' type:'proc' flags:0x0 options:''
[2017-01-15T21:53:08+0100] PID: 18873 about to execute '/bin/sh' for [STANDALONE_MODE]
/bin/sh: 0: can't access tty; job control turned off
$ set
IFS='
'
OPTIND='1'
PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
PPID='0'
PS1='$ '
PS2='> '
PS4='+ '
PWD='/'
$ id
Bad system call
$ exit
[2017-01-15T21:53:17+0100] PID: 18873 exited with status: 159, (PIDs left: 0)
| Kafel | seccomp-filter |
|---|
ALLOW | SECCOMP_RET_ALLOW |
LOG | SECCOMP_RET_LOG |
KILL, KILL_THREAD, DENY | SECCOMP_RET_KILL |
KILL_PROCESS | SECCOMP_RET_KILL_PROCESS |
USER_NOTIF | SECCOMP_RET_USER_NOTIF |
ERRNO(number) | SECCOMP_RET_ERRNO+number |
TRAP(number) | SECCOMP_RET_TRAP+number |
TRACE(number) | SECCOMP_RET_TRACE+number |