这篇文章发布时间已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

在边缘环境中使用 seccomp 配置文件

安全配置文件操作器 (SPO) 是一个功能丰富的 Kubernetes 操作器,使管理 seccomp、SELinux 和 AppArmor 配置文件比以往任何时候都更加容易。从头开始记录这些配置文件是此操作器的关键功能之一,通常涉及集成到大型 CI/CD 系统中。能够在边缘情况下测试操作器的记录功能是 SPO 最近的开发工作之一,并且可以非常轻松地体验 seccomp 配置文件。

使用 spoc record 记录 seccomp 配置文件

安全配置文件操作器的 v0.8.0 版本发布了一个名为 spoc 的新命令行界面,这是一个用于记录和重放 seccomp 配置文件的小型辅助工具,还有其他一些不在此博客文章范围内的功能。

记录 seccomp 配置文件需要执行一个二进制文件,它可以是一个简单的 golang 应用程序,它只调用 uname(2)

package main

import (
	"syscall"
)

func main() {
	utsname := syscall.Utsname{}
	if err := syscall.Uname(&utsname); err != nil {
		panic(err)
	}
}

可以通过以下方式从该代码构建二进制文件

> go build -o main main.go
> ldd ./main
        not a dynamic executable

现在可以从 GitHub 下载 spoc 的最新二进制文件,并在 Linux 上运行该应用程序

> sudo ./spoc record ./main
10:08:25.591945 Loading bpf module
10:08:25.591958 Using system btf file
libbpf: loading object 'recorder.bpf.o' from buffer
libbpf: prog 'sys_enter': relo #3: patched insn #22 (ALU/ALU64) imm 16 -> 16
10:08:25.610767 Getting bpf program sys_enter
10:08:25.610778 Attaching bpf tracepoint
10:08:25.611574 Getting syscalls map
10:08:25.611582 Getting pid_mntns map
10:08:25.613097 Module successfully loaded
10:08:25.613311 Processing events
10:08:25.613693 Running command with PID: 336007
10:08:25.613835 Received event: pid: 336007, mntns: 4026531841
10:08:25.613951 No container ID found for PID (pid=336007, mntns=4026531841, err=unable to find container ID in cgroup path)
10:08:25.614856 Processing recorded data
10:08:25.614975 Found process mntns 4026531841 in bpf map
10:08:25.615110 Got syscalls: read, close, mmap, rt_sigaction, rt_sigprocmask, madvise, nanosleep, clone, uname, sigaltstack, arch_prctl, gettid, futex, sched_getaffinity, exit_group, openat
10:08:25.615195 Adding base syscalls: access, brk, capget, capset, chdir, chmod, chown, close_range, dup2, dup3, epoll_create1, epoll_ctl, epoll_pwait, execve, faccessat2, fchdir, fchmodat, fchown, fchownat, fcntl, fstat, fstatfs, getdents64, getegid, geteuid, getgid, getpid, getppid, getuid, ioctl, keyctl, lseek, mkdirat, mknodat, mount, mprotect, munmap, newfstatat, openat2, pipe2, pivot_root, prctl, pread64, pselect6, readlink, readlinkat, rt_sigreturn, sched_yield, seccomp, set_robust_list, set_tid_address, setgid, setgroups, sethostname, setns, setresgid, setresuid, setsid, setuid, statfs, statx, symlinkat, tgkill, umask, umount2, unlinkat, unshare, write
10:08:25.616293 Wrote seccomp profile to: /tmp/profile.yaml
10:08:25.616298 Unloading bpf module

我必须以 root 身份执行 spoc,因为它将在内部通过重用安全配置文件操作器本身的代码部分来运行 ebpf 程序。我可以看到 bpf 模块已成功加载,并且 spoc 将所需的跟踪点附加到它。然后,它将通过使用其挂载命名空间来跟踪主应用程序,并处理记录的 syscall 数据。ebpf 程序的性质是它们看到内核的整个上下文,这意味着 spoc 跟踪系统的所有 syscall,但不会干扰它们的执行。

日志表明 spoc 找到了 syscall readclosemmap 等,包括 uname。除了 uname 之外的所有其他 syscall 都来自 golang 运行时及其垃圾收集,这已经给像我们演示中的基本应用程序增加了开销。我还可以从日志行 Adding base syscalls: … 中看到 spoc 向生成的配置文件添加了一堆基本 syscall。这些 syscall 由 OCI 运行时(如 runccrun)使用,以便能够运行容器。这意味着 spoc 可以用于记录 seccomp 配置文件,然后可以直接容器化。可以通过在 spoc 中使用 --no-base-syscalls/-n 来禁用此行为,或者通过 --base-syscalls/-b 命令行标志进行自定义。这在以下情况下很有帮助:使用 crun 和 runc 以外的其他 OCI 运行时,或者如果我只想记录应用程序的 seccomp 配置文件并将其与另一个基本配置文件堆叠。

生成的配置文件现在位于 /tmp/profile.yaml 中,但可以使用 --output-file value/-o 标志更改默认位置

> cat /tmp/profile.yaml
apiVersion: security-profiles-operator.x-k8s.io/v1beta1
kind: SeccompProfile
metadata:
  creationTimestamp: null
  name: main
spec:
  architectures:
    - SCMP_ARCH_X86_64
  defaultAction: SCMP_ACT_ERRNO
  syscalls:
    - action: SCMP_ACT_ALLOW
      names:
        - access
        - arch_prctl
        - brk
        - …
        - uname
        - …
status: {}

seccomp 配置文件自定义资源定义 (CRD) 可以直接与安全配置文件操作器一起使用,以便在 Kubernetes 中进行管理。spoc 还可以通过使用 --type/-t raw-seccomp 标志生成原始 seccomp 配置文件(作为 JSON)。

> sudo ./spoc record --type raw-seccomp ./main
52.628827 Wrote seccomp profile to: /tmp/profile.json
> jq . /tmp/profile.json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["access", "…", "write"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

实用程序 spoc record 允许我们直接从任何能够在内核中运行 ebpf 代码的 Linux 系统中的二进制调用中记录复杂的 seccomp 配置文件。但它可以做更多的事情:如何修改 seccomp 配置文件,然后通过使用 spoc run 进行测试。

使用 spoc run 运行 seccomp 配置文件

spoc 还能够运行已应用 seccomp 配置文件的二进制文件,从而可以轻松测试对其进行的任何修改。要做到这一点,只需运行

> sudo ./spoc run ./main
10:29:58.153263 Reading file /tmp/profile.yaml
10:29:58.153311 Assuming YAML profile
10:29:58.154138 Setting up seccomp
10:29:58.154178 Load seccomp profile
10:29:58.154189 Starting audit log enricher
10:29:58.154224 Enricher reading from file /var/log/audit/audit.log
10:29:58.155356 Running command with PID: 437880
>

看起来应用程序已成功退出,这是预期的,因为我尚未修改先前记录的配置文件。我还可以使用 --profile/-p 标志为配置文件指定自定义位置,但这没有必要,因为我没有修改记录中的默认输出位置。spoc 将自动确定它是基于原始 (JSON) 还是 CRD (YAML) 的 seccomp 配置文件,然后将其应用于进程。

安全配置文件操作器支持一个 日志增强器功能,该功能通过解析审核日志来提供其他 seccomp 相关信息。spoc run 以相同的方式使用增强器,以便在调试 seccomp 配置文件时向最终用户提供更多数据。

现在我必须修改配置文件才能在输出中看到任何有价值的信息。例如,我可以删除允许的 uname syscall

> jq 'del(.syscalls[0].names[] | select(. == "uname"))' /tmp/profile.json > /tmp/no-uname-profile.json

然后尝试使用新配置文件 /tmp/no-uname-profile.json 再次运行

> sudo ./spoc run -p /tmp/no-uname-profile.json ./main
10:39:12.707798 Reading file /tmp/no-uname-profile.json
10:39:12.707892 Setting up seccomp
10:39:12.707920 Load seccomp profile
10:39:12.707982 Starting audit log enricher
10:39:12.707998 Enricher reading from file /var/log/audit/audit.log
10:39:12.709164 Running command with PID: 480512
panic: operation not permitted

goroutine 1 [running]:
main.main()
        /path/to/main.go:10 +0x85
10:39:12.713035 Unable to run: launch runner: wait for command: exit status 2

好吧,这是预料之中的!应用的 seccomp 配置文件阻止了 uname syscall,这会导致“操作不允许”错误。此错误非常通用,并且没有提供有关 seccomp 阻止的内容的任何提示。通常很难预测如果 seccomp 禁止单个 syscall,应用程序会如何行为。应用程序可能会像我们的简单演示中那样终止,但也可能导致奇怪的错误行为,并且应用程序根本不会停止。

如果我现在将配置文件的默认 seccomp 操作从 SCMP_ACT_ERRNO 更改为 SCMP_ACT_LOG,如下所示

> jq '.defaultAction = "SCMP_ACT_LOG"' /tmp/no-uname-profile.json > /tmp/no-uname-profile-log.json

然后,当使用 spoc run 时,日志增强器会提示我们 uname syscall 被阻止

> sudo ./spoc run -p /tmp/no-uname-profile-log.json ./main
10:48:07.470126 Reading file /tmp/no-uname-profile-log.json
10:48:07.470234 Setting up seccomp
10:48:07.470245 Load seccomp profile
10:48:07.470302 Starting audit log enricher
10:48:07.470339 Enricher reading from file /var/log/audit/audit.log
10:48:07.470889 Running command with PID: 522268
10:48:07.472007 Seccomp: uname (63)

应用程序将不再终止,但 seccomp 会将该行为记录到 /var/log/audit/audit.log,并且 spoc 将解析数据以将其直接与我们的程序关联起来。将日志消息生成到审核子系统会带来很大的性能开销,应在生产系统中谨慎处理。当在生产环境中以审核模式运行不受信任的应用程序时,它还会带来安全风险。

此演示应该让您了解如何使用安全配置文件操作器的功能来调试应用程序的 seccomp 配置文件问题,可能通过使用我们闪亮的新辅助工具。spoc 是一个灵活且可移植的二进制文件,适用于资源受限的边缘情况,甚至 Kubernetes 本身也可能无法提供全部功能。

感谢您阅读这篇博客文章!如果您对更多信息感兴趣,想提供反馈或寻求帮助,请随时通过 Slack (#security-profiles-operator)邮件列表 直接与我们联系。