跳转至

对容器中的系统调用进行白名单过滤

Linux 的 seccomp (seccomp-bpf) 特性,可以帮助我们检查、过滤程序的系统调用,提高容器的安全性。

什么是 seccomp-bpf?

seccomp

如果需要限制程序可以执行的系统调用,传统的方式是使用 ptrace。但是,(在中期报告的时候大家已经知道),这种方式的性能并不高,每次执行系统调用,都会带来很大的额外开销。

seccomp 是 "secure computing mode" 的缩写。在 Linux 2.6.12 中,seccomp 被引入 Linux 内核。在程序进入 seccomp 模式后,它只能执行 _exit(), sigreturn(), read(), write() 这四种系统调用,如果尝试执行其它的系统调用,程序会被 SIGKILL 信号杀死。显然,程序一旦进入 seccomp 模式,就无法恢复到原先的状态了,这就保证了 seccomp 模式下程序的安全。

seccomp-bpf

但是,seccomp 的问题是:它的管理太严格了,且无法自定义。在 seccomp 中的程序,甚至连 malloc()(需要使用 mmap() 等系统调用)都无法执行,这对于一个面向应用程序的容器来说显然是不能接受的。于是,seccomp-bpf 被引入。

BPF ("Berkeley Packet Filter") 是一个在内核态用于过滤网络数据包的机制,内核中的解释器会解析 BPF 指令,在 BPF「虚拟机」上执行。它在这里也被用于与 seccomp 结合,过滤系统调用。

好消息是,这里我们不需要手写 BPF 指令代码。使用 libseccomp 库就可以方便地使用 seccomp-bpf 了。相关的开发文件可以使用 apt install libseccomp-dev 安装,同时在编译链接时需要加上 -lseccomp 参数。

你可能会使用到以下库函数(不限于此):

scmp_filter_ctx seccomp_init(uint32_t def_action);
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...);
int seccomp_load(scmp_filter_ctx ctx);
void seccomp_release(scmp_filter_ctx ctx);

请查询它们的文档,了解使用方法。

我需要过滤哪些系统调用?

与上一节中的「能力」一样,我们使用白名单的方式来限制。你可以参考 Docker 的默认配置 来配置 seccomp-bpf。

建议:

  • 通过第 54 行 "names" 数组中的所有系统调用 - 如果你在编译时遇到了问题,可以放心地从列表中删除 io_uring 开头的三个系统调用,它们是 Linux 5.1 新增的
  • 通过 arch_prctl 系统调用(它出现在第 524 行的数组中,是 x86 平台特有的调用)
  • 通过 modify_ldt 系统调用(它出现在第 539 行的数组中,是 x86 平台特有的调用)
  • 通过第 586 行 "names" 数组中的所有系统调用(这些系统调用是进行一系列系统维护操作需要用到的)

你可以在保证程序正常运行的情况下修改白名单。安全性不是我们考核的核心。