Kafel 是一种用于指定系统调用过滤策略的语言和库。 这些策略被编译成 BPF 代码,可与 seccomp-filter 一起使用。
这不是谷歌的官方产品。
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 "some_other_file.policy"
Kafel 仅在显式添加到搜索路径的目录中查找包含的文件。
kafel_add_include_search_path(ctxt, "includes/path");
将 includes/path 添加到搜索路径——上面的包含指令将引用 includes/path/some_other_file.policy。
包含指令以换行符或分号结束。可以在一条指令中指定多个文件,文件之间用空白分隔。
#include "first.policy" "second.policy"; #include "third.policy"
与 nsjail 一起使用时,以下命令可以为你的 shell 创建一个相当受限的环境:
$ ./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 |