Seccomp

简要概述

Seccomp(Secure Computing)是自 2.6.12 版本后内核的一个特性,它背后思想是限制进程可以使用的系统调用。

Linux 内核有几百个系统调用,但大多数进程并不需要它们,然而,如果一个进程受到入侵并被使用本身功能之外的其他系统调用,可能导致安全漏洞,从而危及整个系统的安全性。通过限制可以调用哪些系统调用,它是构建应用程序沙盒的关键组件。

目前存在两种主要的模式,分别是严格模式(Strict Mode)和过滤器模式(Filter Mode):

  1. 严格模式

除了最基本的 read ,write ,_exit ,_sigreturn 四个之外,一旦出现其他的系统调用,进程会被立刻终止 (SIGKILL)。

  1. 过滤器模式

允许应用程序定义一组自定义的系统调用过滤规则,过滤器模式使用BPF(Berkeley Packet Filter)语言来编写,适用于需要更精细控制的场景。

在应用中使用

严格模式

  • 未使用 seccomp 编译执行
cat << EOF >> /tmp/test1.c
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/socket.h>

int main(int argc, char* argv[]) {
  printf("Install seccomp\n");

  printf("Creating socket\n");
  int sock = socket(AF_INET, SOCK_STREAM, 0);

  return 0;
}
EOF
$ gcc -o test1 test1.c
$ ./test1
Install seccomp
Creating socket
$

此时编译执行成功。

  • 添加 seccomp 代码使用严格模式
cat << EOF >> /tmp/test2.c
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/socket.h>

#include <linux/seccomp.h>

int main(int argc, char* argv[]) {
  printf("Install seccomp\n");

  // 使用 Seccomp 严格模式
  prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);

  printf("Creating socket\n");
  int sock = socket(AF_INET, SOCK_STREAM, 0);

  return 0;
}
EOF
$ gcc -o test2 test2.c
$ ./test2
Install seccomp
Creating socket
Killed
$

此时编译成功,执行应用在调用创建 socket 时马上被 KILL。

过滤器模式

cat << EOF >> /tmp/test3.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <stddef.h>

#include <linux/filter.h>
#include <linux/seccomp.h>

int main() {
  printf("Install seccomp\n");

  // 使用 Seccomp 过滤器模式
  prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER);

  // 定义允许的系统调用规则
  struct sock_filter filter[] = {
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),  // 载入系统调用号
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),  // 允许socket系统调用
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),   // 其他系统调用将导致程序终止
  };

  struct sock_fprog prog = {
    .len = sizeof(filter) / sizeof(filter[0]),
    .filter = filter,
  };

  // 设置 NO_NEW_PRIVS 标志
  prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);

  // 加载 Seccomp 规则
  prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);

  printf("Creating socket\n");
  int sock = socket(AF_INET, SOCK_STREAM, 0);

  if (sock == -1) {
    perror("Socket creation failed");
    exit(EXIT_FAILURE);
  }

  printf("Socket created successfully\n");

  return 0;
}
EOF
$ gcc -o test3 test3.c
$ ./test3
Install seccomp
Creating socket
Socket created successfully
$

此时编译执行成功。

在 Kubernetes 上使用

配置 Pod securityContext 启用

其中可配置的参数参考 Pod SeccompProfile

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    resources: {}
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
    securityContext:
      allowPrivilegeEscalation: false
  securityContext:
    runAsNonRoot: false
    runAsUser: 99
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/audit.json

这里使用了宿主 “profiles/audit.json” 上的 seccomp 配置,这里的路径是在 kubelet 工作目录下,如:"/var/lib/kubelet"。

启用容器默认 seccomp 策略

在 kubelet 添加以下启动参数,同时 feature 未被禁用:

--seccomp-default=true

启用 RuntimeDefault 作为所有工作负载的默认 seccomp 配置文件。

检查当前 seccomp 启用的容器

crictl inspect 2b6a707db3c26 | jq .info.runtimeSpec.linux.seccomp

如果结果为 “null” 则说明该容器未使用到 seccomp 技术。

null

特别的当启用特权模式的 Pod 是无法使用 seccomp 的,既然:

    securityContext:
      privileged: true



最后修改 2023.12.06: chore: update seccomp (26cd6cd)