google/kafel
GitHub: google/kafel
Kafel 是一种用于定义和编译系统调用过滤策略的语言与库,输出 BPF 代码以供 seccomp 使用。
Stars: 354 | Forks: 58
# 是什么
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);
```
# 策略语言
使用一种简单的语言来定义策略。
策略文件由多条语句组成。
一条语句可以是:
* 常量定义
* 策略定义
* 策略定义语句
* 默认动作语句
位于文件作用域的策略定义语句会被添加到隐式的
顶层策略中。
这个顶层策略将被编译。
## 默认动作语句
指定在没有规则匹配时应采取的动作 `the_action`。
默认动作必须只指定一次。
如果策略文件未指定默认动作,则默认动作为
KILL
## 数字
Kafel 支持以下数字表示法:
* 十进制 `42`
* 十六进制 `0xfa1`
* 八进制 `0777`
* 二进制 `0b10101`
## 常量定义
可以定义数值常量以使策略更具可读性。
常量定义几乎可以放在策略文件中的任何位置。
常量定义不能放在策略定义内部。
定义后的常量可以在任何需要数字的地方使用。
```
#define MYCONST 123
```
## 策略定义
策略定义是由动作块和使用语句组成的列表,
以逗号分隔。
__samples/__ 包含一些示例策略,展示了所支持的功能。
### 使用语句
`USE someOtherPolicy` 的行为类似于将 `someOtherPolicy` 的内容粘贴到其位置。
你只能使用在此语句之前定义好的策略。
通过使用语句,可以创建有意义的规则组,这些组是构建更大策略的基石。
### 动作块
动作块由一个目标和一组以逗号分隔的系统调用匹配规则组成。
第一条匹配规则的目标即为策略决策。
下表列出了 Kafel 目标及其对应的 seccomp-filter 返回值。
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`
### 系统调用匹配规则
一条规则由系统调用名称和可选的布尔表达式列表组成。
多个布尔表达式之间用逗号分隔。
逗号在语义上等价于 `||`,但优先级最低,
因此这样写更容易阅读。
#### 系统调用命名
通常系统调用使用 Linux 内核中定义的名称进行指定。
不过,你也可以过滤 __custom syscalls__,即不在标准系统调用列表中的调用。
你可以通过定义常量并在系统调用名称处使用它,或者
使用 `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](https://github.com/google/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)
```
标签:Google开源, Kafel语言, seccomp, Streamlit, 云安全监控, 内核级安全, 子域名枚举, 安全策略, 客户端加密, 提示词设计, 沙箱, 目录遍历, 策略语言, 系统安全, 系统调用过滤, 编译器, 访问控制, 静态分析