Skip to main content

Command Palette

Search for a command to run...

介绍一个小工具:Security Profiles Operator

Updated
4 min read

在云原生安全方面,Kubernetes 在不同维度提供了很多的不同内容,例如 RBAC、Networkpolicy、SecurityContext 等等,种种措施中,像我这样基础不牢的 YAML 工程师最头大的可能就要数 SecurityContext 里面的 SELinux、Seccomp 和 AppArmor 三大块了。Security Profiles Operator 项目为此而来,希望能够降低在 Kubernetes 集群中使用这些安全技术的难度。在项目网页上转了转,发现他所说的简化,除了定义几个 CRD 封装这样的 Operator 传统技能之外;还有一个使用 CRD 在节点间传输 Security Profile 的能力;最后也是最重要的,提供了很方便的录制功能,这倒是真的戳中了痛点——手写 Profile 固然酷炫,录制生成才是生产力啊。目前支持的功能矩阵如下:

功能SeccompSELinuxAppArmor
Profile CRDYesYesYes
ProfileBindingYesNoNo
Deploy profiles into nodesYesYesWIP
Remove profiles no longer in useYesYesWIP
Profile Auto-generation (logs)YesWIPNo
Profile Auto-generation (ebpf)YesNoNo
Audit log enrichmentYesWIPYes

部署

如果目标环境不是 Openshift,首先需要安装 Cert Manager

$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.6.1/cert-manager.yaml

customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
...

接下来安装 SPO:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/security-profiles-operator/master/deploy/operator.yaml
customresourcedefinition.apiextensions.k8s.io/profilebindings.security-profiles-operator.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/profilerecordings.security-profiles-operator.x-k8s.io created
customresourcedefinition.apiextensions.k8s.io/seccompprofiles.security-profiles-operator.x-k8s.io created

查看生成的 CRD,大致功能如下

全名缩写命名空间级功能
AppArmorProfileaatrue用于保存 AppArmor Profile
ProfileBindingN/Atrue把 Profile 绑定到 Pod 上
ProfileRecordingN/Atrue用录制的方式生成 Profile
RawSelinuxProfileN/Atrue
SeccompProfilesptrue用于保存 Seccomp Profile
SecurityProfileNodeStatusspnstrue
SecurityProfilesOperatorDaemonspodtrue
SelinuxProfileN/Atrue用于保存 Selinux Profile

最后创建一个命名空间 spo,并以此作为缺省命名空间,进行后续的试用过程。

借助 SPO 传递 Seccomp

创建一个 Seccomp Profile,其中加入了对系统调用的审计日志:

apiVersion: security-profiles-operator.x-k8s.io/v1beta1
kind: SeccompProfile
metadata:
  namespace: spo
  name: seccomp-profile-sample
spec:
  defaultAction: SCMP_ACT_LOG

Apply 之后 kubectl get sp seccomp-profile-sample -o yaml ,会发现状态如下:

metadata:
...
  finalizers:
  - gke-gcp-vlab-k8s-default-pool-7c61250b-x3h1-delete
  - gke-gcp-vlab-k8s-default-pool-7c61250b-n9l3-delete
  - gke-gcp-vlab-k8s-default-pool-7c61250b-86wz-delete
...
status:
...
  localhostProfile: operator/spo/seccomp-profile-sample.json
  status: Installed

表明 Profile 已经部署到三个节点,状态为 Installed

这样一来就可以创建一个 Pod,引用这个 Profile:

apiVersion: v1
kind: Pod
metadata:
  name: sleep-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: operator/spo/seccomp-profile-sample.json
  containers:
    - name: sleep
      image: dustise/sleep:v0.9.6
      volumeMounts:
      - name: kubelet
        mountPath: /data/kubelet
  volumes:
  - name: kubelet
    hostPath:
        path: /var/lib/kubelet/seccomp/operator/spo
        type: Directory

Pod 创建之后,进入 Pod Shell,可以看到我们生成的 Profile:

$ kubectl exec -it sleep-pod -- bash
bash-5.0# cd /data/kubelet
bash-5.0# ls
seccomp-profile-sample.json
bash-5.0# cat seccomp-profile-sample.json
{"defaultAction":"SCMP_ACT_LOG"}

接下来执行一下 CURL 点什么,登录节点看看日志

$ sudo journalctl -xe  | grep -i seccomp | grep curl
Mar 21 10:12:44 gke-gcp-vlab-k8s-default-pool-d97cb436-mdgb audit[180209]: SECCOMP auid=4294967295 uid=0 gid=0 ses=4294967295 subj==docker-default (enforce) pi
d=180209 comm="curl" exe="/usr/bin/curl" sig=0 arch=c000003e syscall=231 compat=0 ip=0x7f6d3b8d76f9 code=0x7ffc0000

删掉 Pod。便于进行后续步骤

如果我不想修改 Pod,可以用 ProfileBinding 把 Seccomp Profile 和镜像绑定到一起,如下配置:

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileBinding
metadata:
  name: sample-binding
spec:
  profileRef:
    kind: SeccompProfile
    name: seccomp-profile-sample
  image: dustise/sleep:v0.9.6

看看重建一个没有 securityPod 的 Pod 会有什么变化:

apiVersion: v1
kind: Pod
metadata:
  name: sleep-pod
spec:
  containers:
    - name: sleep
      image: dustise/sleep:v0.9.6
      volumeMounts:
      - name: kubelet
        mountPath: /data/kubelet
  volumes:
  - name: kubelet
    hostPath:
        path: /var/lib/kubelet/seccomp/operator/spo
        type: Directory

创建之后,我们看看线上的 YAML:

$ kubectl get po sleep-pod -o yaml
apiVersion: v1
kind: Pod
...
    securityContext:
      seccompProfile:
        localhostProfile: operator/spo/seccomp-profile-sample.json
        type: Localhost

果然这里被修改了。有兴趣还可以查查,这里用到的 Mutating Webhook。

录制 Profile

SPO 支持三种录制模式,分别是 hooklogeBPF

hook 指的是 OCI hooks,但是目前 containerd 还不支持

log 则是使用基于日志的录制方式。

eBPF 自然就是最新的基于 eBPF 的录制了。

缺省情况下 spod 没有开启 eBPF 录制功能,需要把开关 spec.enableBpfRecorder 设置为 true

$ kubectl patch spod spod -n security-profiles-operator  --type=merge -p '{"spec":{"enableBpfRecorder":true}}'
securityprofilesoperatordaemon.security-profiles-operator.x-k8s.io/spod patched

接下来创建一个录制对象:

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileRecording
metadata:
  name: sample-recording
spec:
  kind: SeccompProfile
  recorder: bpf
  podSelector:
    matchLabels:
      app: sleep

提交之后,我们给上一个 Pod 加上标签 app=sleep,创建出来,然后随便执行点什么:

$ kubectl apply -f recording.yaml
profilerecording.security-profiles-operator.x-k8s.io/sample-recording created
$ kubectl apply -f plain-pod.yaml
pod/sleep-pod created
$ kubectl exec -it sleep-pod -- curl http://jd.com
...
$ kubectl exec -it sleep-pod -- mkdir 123
...
$ kubectl exec -it sleep-pod -- ping baidu.com
...

删除 Pod,查看 SeccompProfile:

$ kubectl delete -f plain-pod.yaml
pod "sleep-pod" deleted
$ kubectl get sp
NAME                     STATUS      AGE
sample-recording-sleep   Installed   9s

$ kubectl get sp sample-recording-sleep -o yaml

kind: SeccompProfile
metadata:
  creationTimestamp: "2022-03-21T14:07:23Z"
  finalizers:
  - gke-gcp-vlab-k8s-default-pool-d97cb436-5tct-delete
  - gke-gcp-vlab-k8s-default-pool-d97cb436-mdgb-delete
  - gke-gcp-vlab-k8s-default-pool-d97cb436-d8d1-delete
  generation: 1
  labels:
    spo.x-k8s.io/profile-id: SeccompProfile-sample-recording-sleep
...
spec:
  architectures:
  - SCMP_ARCH_X86_64
  defaultAction: SCMP_ACT_ERRNO
  syscalls:
  - action: SCMP_ACT_ALLOW
    names:
    - arch_prctl
    - bind
...

这里看到,删除 Pod 之后,录制过程自动生成了新的 SeccompProfile,其中包含了 Pod 工作过程中使用的配置,并且已经被安装到了各个节点之上。

牢骚

Kubernetes 普及之后,新方向层出不穷,正如杨蒙恩说的——“遍地是大王,短暂又辉煌”,不过安全可能是目前确定性最高的一块内容,决不短暂。所谓安全无小事,没有网格、没有 Serverless 甚至没有多集群、经济性、混布都可以,没有安全可能就全盘皆输了;也不要总想着新瓶装旧酒,挑挑节点、固定一下 IP 就完事了,BMW 装上马鞍之后,丢的不只是风阻和车顶,至少车友群是不太容易混了。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts