介绍一个小工具:Security Profiles Operator

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

功能 Seccomp SELinux AppArmor
Profile CRD Yes Yes Yes
ProfileBinding Yes No No
Deploy profiles into nodes Yes Yes WIP
Remove profiles no longer in use Yes Yes WIP
Profile Auto-generation (logs) Yes WIP No
Profile Auto-generation (ebpf) Yes No No
Audit log enrichment Yes WIP Yes

部署

如果目标环境不是 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,大致功能如下

全名 缩写 命名空间级 功能
AppArmorProfile aa true 用于保存 AppArmor Profile
ProfileBinding N/A true 把 Profile 绑定到 Pod 上
ProfileRecording N/A true 用录制的方式生成 Profile
RawSelinuxProfile N/A true
SeccompProfile sp true 用于保存 Seccomp Profile
SecurityProfileNodeStatus spns true
SecurityProfilesOperatorDaemon spod true
SelinuxProfile N/A true 用于保存 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 装上马鞍之后,丢的不只是风阻和车顶,至少车友群是不太容易混了。

Avatar
崔秀龙

简单,是大师的责任;我们凡夫俗子,能做到清楚就很不容易了。

comments powered by Disqus
下一页
上一页

相关