本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
在邊緣裝置上玩轉 seccomp 配置檔案
Security Profiles Operator (SPO) 是一個功能豐富的 Kubernetes Operator,使管理 seccomp、SELinux 和 AppArmor profile 比以往任何時候都更容易。從頭開始記錄這些 profile 是該 Operator 的關鍵功能之一,這通常涉及整合到大型 CI/CD 系統中。能夠在邊緣情況下測試 Operator 的記錄功能是 SPO 最近的開發工作之一,這使得體驗 seccomp profile 變得異常簡單。
使用 spoc record
記錄 seccomp profile
Security Profiles Operator 的 v0.8.0 版本釋出了一個名為 spoc
的新命令列介面,這是一個用於記錄和重放 seccomp profile 的小助手工具,此外還有其他各種功能,但超出了本部落格文章的範圍。
記錄 seccomp profile 需要執行一個二進位制檔案,這可以是一個簡單的 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
,因為它會透過重用 Security Profiles Operator 本身的程式碼部分來內部執行一個 ebpf 程式。我可以看到 bpf 模組已成功載入,並且 spoc
已將所需的 tracepoint 附加到它。然後它將透過使用其掛載名稱空間來跟蹤主應用程式並處理記錄的系統呼叫資料。ebpf 程式的特性是它們可以看到核心的整個上下文,這意味著 spoc
跟蹤系統的所有系統呼叫,但不會干擾它們的執行。
日誌表明 spoc
找到了系統呼叫 read
、close
、mmap
等,包括 uname
。除 uname
之外的所有其他系統呼叫都來自 golang 執行時及其垃圾回收,這已經為像我們演示中這樣的基本應用程式增加了開銷。我還可以從日誌行 Adding base syscalls: …
中看到 spoc
向生成的 profile 添加了一系列基本系統呼叫。這些被 OCI 執行時(如 runc 或 crun)使用,以便能夠執行容器。這意味著 spoc
可以用於記錄 seccomp profile,然後可以直接進行容器化。此行為可以在 spoc
中透過使用 --no-base-syscalls
/-n
或透過 --base-syscalls
/-b
命令列標誌進行自定義來停用。這在某些情況下很有用,例如使用 crun 和 runc 以外的其他 OCI 執行時,或者如果我只想為應用程式記錄 seccomp profile 並將其與另一個基本 profile 堆疊。
生成的 profile 現在位於 /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 profile 自定義資源定義(CRD)可以直接與 Security Profiles Operator 一起使用,以便在 Kubernetes 中進行管理。spoc
也能夠透過使用 --type
/-t
raw-seccomp
標誌生成原始的 seccomp profile(以 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 profile。但它能做的更多:如何修改 seccomp profile 然後使用 spoc run
來測試它。
使用 `spoc run` 執行 seccomp profile
spoc
還能夠執行應用了 seccomp profile 的二進位制檔案,從而可以輕鬆地測試對其的任何修改。為此,只需執行
> 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。我還可以透過使用 --profile
/-p
標誌為 profile 指定一個自定義位置,但這沒有必要,因為我沒有修改記錄的預設輸出位置。spoc
會自動確定它是原始(JSON)還是基於 CRD(YAML)的 seccomp profile,然後將其應用於程序。
Security Profiles Operator 支援日誌豐富器功能,它透過解析審計日誌提供額外的 seccomp 相關資訊。spoc run
以同樣的方式使用豐富器,以便在除錯 seccomp profile 時向終端使用者提供更多資料。
現在我必須修改 profile 才能在輸出中看到任何有價值的東西。例如,我可以刪除允許的 uname
系統呼叫
> jq 'del(.syscalls[0].names[] | select(. == "uname"))' /tmp/profile.json > /tmp/no-uname-profile.json
然後嘗試使用新的 profile /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 profile 阻止了 uname
系統呼叫,這導致了“operation not permitted”錯誤。這個錯誤非常通用,沒有提供任何關於被 seccomp 阻止了什麼的線索。通常,預測應用程式在 seccomp 禁止單個系統呼叫時的行為是極其困難的。應用程式可能會像我們簡單的演示中那樣終止,但也可能導致奇怪的錯誤行為,並且應用程式根本不會停止。
如果我現在將 profile 的預設 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
系統呼叫被阻止了
> 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
會解析資料以將其直接與我們的程式關聯起來。向審計子系統生成日誌訊息會帶來很大的效能開銷,在生產系統中應謹慎處理。在生產環境中以審計模式執行不受信任的應用程式時,也存在安全風險。
這個演示應該能讓您瞭解如何使用我們閃亮的新助手工具,藉助 Security Profiles Operator 的功能來除錯應用程式的 seccomp profile 問題。spoc
是一個靈活便攜的二進位制檔案,適用於資源有限甚至 Kubernetes 本身可能無法提供完整功能的邊緣情況。
感謝您閱讀這篇博文!如果您對更多內容感興趣、提供反饋或尋求幫助,請隨時透過 Slack (#security-profiles-operator) 或郵件列表直接與我們聯絡。