Kubernetes中Pod/容器的安全管控Security Context

我们有时候在运行一个容器的时候,可能需要使用 sysctl 命令来修改内核参数,比如 netvmkernel 等参数,但是 systcl 需要容器拥有超级权限,才可以使用,在 Docker 容器启动的时候我们可以加上 --privileged 参数来使用特权模式。那么在 Kubernetes 中应该如何来使用呢?文章源自靠谱运维-https://www.ixdba.net/archives/1921

这个时候我们就需要使用到 Kubernetes 中的 Security Context,也就是常说的安全上下文,主要是来限制容器非法操作宿主节点的系统级别的内容,使得节点的系统或者节点上其他容器组受到影响。Kubernetes 提供了三种配置安全上下文级别的方法:文章源自靠谱运维-https://www.ixdba.net/archives/1921

  • Container-level Security Context:仅应用到指定的容器
  • Pod-level Security Context:应用到 Pod 内所有容器以及 Volume
  • Pod Security Policies(PSP):应用到集群内部所有 Pod 以及 Volume

我们可以用如下几种方式来设置 Security Context文章源自靠谱运维-https://www.ixdba.net/archives/1921

  • 访问权限控制:根据用户 ID(UID)和组 ID(GID)来限制对资源(比如:文件)的访问权限
  • Security Enhanced Linux (SELinux):为对象分配 SELinux 标签
  • 以 privileged(特权)模式运行
  • Linux Capabilities:给某个特定的进程超级权限,而不用给 root 用户所有的 privileged 权限
  • AppArmor:使用程序文件来限制单个程序的权限
  • Seccomp:过滤容器中进程的系统调用(system call)
  • AllowPrivilegeEscalation(允许特权扩大):此项配置是一个布尔值,定义了一个进程是否可以比其父进程获得更多的特权,直接效果是,容器的进程上是否被设置 no_new_privs 标记。当出现如下情况时,AllowPrivilegeEscalation 的值始终为 true:
    • 容器以 privileged 模式运行
    • 容器拥有 CAP_SYS_ADMIN 的 Linux Capability

为 Pod 设置 Security Context

我们只需要在 Pod 定义的资源清单文件中添加 securityContext 字段,就可以为 Pod 指定安全上下文相关的设定,通过该字段指定的内容将会对当前 Pod 中的所有容器生效。文章源自靠谱运维-https://www.ixdba.net/archives/1921

apiVersion: v1
kind: Pod
metadata:
  name: security-context-pod-demo
spec:
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: ["sh", "-c", "sleep 60m"]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /pod/demo
    securityContext:
      allowPrivilegeEscalation: false

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

在当前资源清单文件中我们在 Pod 下面添加了 securityContext 字段,其中:文章源自靠谱运维-https://www.ixdba.net/archives/1921

  • runAsUser 字段指定了该 Pod 中所有容器的进程都以 UID 1000 的身份运行,runAsGroup 字段指定了该 Pod 中所有容器的进程都以 GID 3000 的身份运行
    • 如果省略该字段,容器进程的 GID 为 root(0)
    • 容器中创建的文件,其所有者为 userID 1000,groupID 3000
  • fsGroup 字段指定了该 Pod 的 fsGroup 为 2000
    • 数据卷 (对应挂载点 /pod/demo 的数据卷为 sec-ctx-demo) 的所有者以及在该数据卷下创建的任何文件,其 GID 都为 2000

下表是我们常用的一些 securityContext 字段设置内容介绍:文章源自靠谱运维-https://www.ixdba.net/archives/1921

Kubernetes中Pod/容器的安全管控Security Context文章源自靠谱运维-https://www.ixdba.net/archives/1921

直接创建上面的 Pod 对象:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl apply -f https:/www.qikqiak.com/k8strain/security/manifests/security-context-pod-demo-1.yaml
$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
security-context-pod-demo   1/1     Running   0          6m45s

运行完成后,我们可以验证下容器中的进程运行的 ownership:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl exec security-context-pod-demo top
Mem: 7586020K used, 422948K free, 298660K shrd, 1247656K buff, 3867660K cached
CPU:  2.1% usr  1.0% sys  0.0% nic 96.3% idle  0.2% io  0.0% irq  0.0% sirq
Load average: 0.30 0.35 0.35 1/956 50
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
   46     0 1000     R     1292  0.0   0  0.0 top
    1     0 1000     S     1280  0.0   0  0.0 sleep 60m

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

我们直接运行一个 top 进程,查看容器中的所有正在执行的进程,我们可以看到 USER ID 都为 1000(runAsUser 指定的),然后查看下挂载的数据卷的 ownership:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl exec security-context-pod-demo -- ls -la /pod
total 8
drwxr-xr-x    3 root     root          4096 Nov 26 15:44 .
drwxr-xr-x    1 root     root          4096 Nov 26 15:44 ..
drwxrwsrwx    2 root     2000             6 Nov 26 15:43 demo

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

因为上面我们指定了 fsGroup=2000,所以声明挂载的数据卷 /pod/demo 的 GID 也变成了 2000。直接调用容器中的 id 命令:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl exec security-context-pod-demo id
uid=1000 gid=3000 groups=2000

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

我们可以看到 gid 为 3000,与 runAsGroup 字段所指定的一致,如果 runAsGroup 字段被省略,则 gid 取值为 0(即 root),此时容器中的进程将可以操作 root Group 的文件。文章源自靠谱运维-https://www.ixdba.net/archives/1921

比如我们现在想要去删除容器中的 /tmp 目录就没有权限了,因为该目录的用户和组都是 root,而我们当前要去删除使用的进程的 ID 号就变成了 1000:3000,所以没有权限操作:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl exec security-context-pod-demo -- ls -la /tmp
total 8
drwxrwxrwt    2 root     root          4096 Oct 29 02:40 .
drwxr-xr-x    1 root     root          4096 Nov 26 15:44 ..
$ kubectl exec security-context-pod-demo -- rm -rf /tmp
rm: can't remove '/tmp': Permission denied

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

为容器设置 Security Context

除了在 Pod 中可以设置安全上下文之外,我们还可以单独为某个容器设置安全上下文,同样也是通过 securityContext 字段设置,当该字段的配置与 Pod 级别的 securityContext 配置相冲突时,容器级别的配置将覆盖 Pod 级别的配置。容器级别的 securityContext 不影响 Pod 中的数据卷。如下资源清单所示:文章源自靠谱运维-https://www.ixdba.net/archives/1921

apiVersion: v1
kind: Pod
metadata:
  name: security-context-container-demo
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 60m" ]
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

直接创建上面的 Pod 对象:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl apply -f https:/www.qikqiak.com/k8strain/security/manifests/security-context-pod-demo-2.yaml
$ kubectl get pods
NAME                              READY   STATUS              RESTARTS   AGE
security-context-container-demo   1/1     Running             0          5s

同样我们直接执行容器中的 top 命令:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl exec security-context-container-demo top
Mem: 4991896K used, 3016924K free, 52308K shrd, 158364K buff, 3282996K cached
CPU:  2.6% usr  7.8% sys  2.6% nic 86.8% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.12 0.09 0.12 1/848 10
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    6     0 2000     R     1292  0.0   1  0.0 top
    1     0 2000     S     1280  0.0   3  0.0 sleep 60m

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

容器的进程以 UID 2000 的身份运行,该取值由 spec.containers[*].securityContext.runAsUser 容器组中的字段定义。Pod 中定义的 spec.securityContext.runAsUser 取值 1000 被覆盖。文章源自靠谱运维-https://www.ixdba.net/archives/1921

设置 Linux Capabilities

我们使用 docker run 的时候可以通过 --cap-add 和 --cap-drop 命令来给容器添加 Linux Capabilities。那么在 Kubernetes 下面如何来设置呢?要了解如何设置,首先我们还是需要了解下 Linux Capabilities 是什么?文章源自靠谱运维-https://www.ixdba.net/archives/1921

Linux Capabilities

要了解 Linux Capabilities,这就得从 Linux 的权限控制发展来说明。在 Linux 2.2 版本之前,当内核对进程进行权限验证的时候,Linux 将进程划分为两类:特权进程(UID=0,也就是超级用户)和非特权进程(UID!=0),特权进程拥有所有的内核权限,而非特权进程则根据进程凭证(effective UID, effective GID,supplementary group 等)进行权限检查。文章源自靠谱运维-https://www.ixdba.net/archives/1921

比如我们以常用的 passwd 命令为例,修改用户密码需要具有 root 权限,而普通用户是没有这个权限的。但是实际上普通用户又可以修改自己的密码,这是怎么回事呢?在 Linux 的权限控制机制中,有一类比较特殊的权限设置,比如 SUID(Set User ID on execution),允许用户以可执行文件的 owner 的权限来运行可执行文件。因为程序文件 /bin/passwd 被设置了 SUID 标识,所以普通用户在执行 passwd 命令时,进程是以 passwd 的所有者,也就是 root 用户的身份运行,从而就可以修改密码了。文章源自靠谱运维-https://www.ixdba.net/archives/1921

但是使用 SUID 却带来了新的安全隐患,当我们运行设置了 SUID 的命令时,通常只是需要很小一部分的特权,但是 SUID 却给了它 root 具有的全部权限,一旦 被设置了 SUID 的命令出现漏洞,是不是就很容易被利用了。文章源自靠谱运维-https://www.ixdba.net/archives/1921

为此 Linux 引入了 Capabilities 机制来对 root 权限进行了更加细粒度的控制,实现按需进行授权,这样就大大减小了系统的安全隐患。文章源自靠谱运维-https://www.ixdba.net/archives/1921

什么是 Capabilities

从内核 2.2 开始,Linux 将传统上与超级用户 root 关联的特权划分为不同的单元,称为 capabilitesCapabilites 每个单元都可以独立启用和禁用。这样当系统在作权限检查的时候就变成了:在执行特权操作时,如果进程的有效身份不是 root,就去检查是否具有该特权操作所对应的 capabilites,并以此决定是否可以进行该特权操作。比如如果我们要设置系统时间,就得具有 CAP_SYS_TIME 这个 capabilites。下面是从 capabilities man page 中摘取的 capabilites 列表:文章源自靠谱运维-https://www.ixdba.net/archives/1921

Kubernetes中Pod/容器的安全管控Security Context文章源自靠谱运维-https://www.ixdba.net/archives/1921

如何使用 Capabilities

我们可以通过 getcap 和 setcap 两条命令来分别查看和设置程序文件的 capabilities 属性。比如当前我们是zuiapp 这个用户,使用 getcap 命令查看 ping 命令目前具有的 capabilities文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ ll /bin/ping
-rwxr-xr-x. 1 root root 62088 Nov  7  2016 /bin/ping
$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

我们可以看到具有 cap_net_admin 这个属性,所以我们现在可以执行 ping 命令:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ ping www.qikqiak.com
PING www.qikqiak.com.w.kunlungr.com (115.223.14.186) 56(84) bytes of data.
64 bytes from 115.223.14.186 (115.223.14.186): icmp_seq=1 ttl=54 time=7.87 ms
64 bytes from 115.223.14.186 (115.223.14.186): icmp_seq=2 ttl=54 time=7.85 ms

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

但是如果我们把命令的 capabilities 属性移除掉:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ sudo setcap cap_net_admin,cap_net_raw-p /bin/ping
$ getcap /bin/ping
/bin/ping =

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

这个时候我们执行 ping 命令可以发现已经没有权限了:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ ping www.qikqiak.com
ping: socket: Operation not permitted

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

因为 ping 命令在执行时需要访问网络,所需的 capabilities 为 cap_net_admin 和 cap_net_raw,所以我们可以通过 setcap 命令可来添加它们:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ sudo setcap cap_net_admin,cap_net_raw+p /bin/ping
$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p
$ ping www.qikqiak.com
PING www.qikqiak.com.w.kunlungr.com (115.223.14.188) 56(84) bytes of data.
64 bytes from 115.223.14.188 (115.223.14.188): icmp_seq=1 ttl=54 time=7.39 ms

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

命令中的 p 表示 Permitted 集合(接下来会介绍),+ 号表示把指定的capabilities 添加到这些集合中,- 号表示从集合中移除。文章源自靠谱运维-https://www.ixdba.net/archives/1921

对于可执行文件的属性中有三个集合来保存三类 capabilities,它们分别是:文章源自靠谱运维-https://www.ixdba.net/archives/1921

  • Permitted:在进程执行时,Permitted 集合中的 capabilites 自动被加入到进程的 Permitted 集合中。
  • Inheritable:Inheritable 集合中的 capabilites 会与进程的 Inheritable 集合执行与操作,以确定进程在执行 execve 函数后哪些 capabilites 被继承。
  • Effective:Effective 只是一个 bit。如果设置为开启,那么在执行 execve 函数后,Permitted 集合中新增的 capabilities 会自动出现在进程的 Effective 集合中。

对于进程中有五种 capabilities 集合类型,相比文件的 capabilites,进程的 capabilities 多了两个集合,分别是 Bounding 和 Ambient文章源自靠谱运维-https://www.ixdba.net/archives/1921

我们可以通过下面的命名来查看当前进程的 capabilities 信息:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ cat /proc/7029/status | grep 'Cap'  #7029为PID
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000001fffffffff
CapAmb: 0000000000000000

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

然后我们可以使用 capsh 命令把它们转义为可读的格式,这样基本可以看出进程具有的 capabilities 了:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ capsh --decode=0000001fffffffff
0x0000001fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,35,36

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

Docker Container Capabilities

我们说 Docker 容器本质上就是一个进程,所以理论上容器就会和进程一样会有一些默认的开放权限,默认情况下 Docker 会删除必须的 capabilities 之外的所有 capabilities,因为在容器中我们经常会以 root 用户来运行,使用 capabilities 现在后,容器中的使用的 root 用户权限就比我们平时在宿主机上使用的 root 用户权限要少很多了,这样即使出现了安全漏洞,也很难破坏或者获取宿主机的 root 权限,所以 Docker 支持 Capabilities 对于容器的安全性来说是非常有必要的。文章源自靠谱运维-https://www.ixdba.net/archives/1921

不过我们在运行容器的时候可以通过指定 --privileded 参数来开启容器的超级权限,这个参数一定要慎用,因为他会获取系统 root 用户所有能力赋值给容器,并且会扫描宿主机的所有设备文件挂载到容器内部,所以是非常危险的操作。文章源自靠谱运维-https://www.ixdba.net/archives/1921

但是如果你确实需要一些特殊的权限,我们可以通过 --cap-add 和 --cap-drop 这两个参数来动态调整,可以最大限度地保证容器的使用安全。下面表格中列出的 Capabilities 是 Docker 默认给容器添加的,我们可以通过 --cap-drop 去除其中一个或者多个:文章源自靠谱运维-https://www.ixdba.net/archives/1921

Kubernetes中Pod/容器的安全管控Security Context文章源自靠谱运维-https://www.ixdba.net/archives/1921

下面表格中列出的 Capabilities 是 Docker 默认删除的,我们可以通过--cap-add添加其中一个或者多个:文章源自靠谱运维-https://www.ixdba.net/archives/1921

Kubernetes中Pod/容器的安全管控Security Context文章源自靠谱运维-https://www.ixdba.net/archives/1921

--cap-add--cap-drop 这两参数都支持ALL值,比如如果你想让某个容器拥有除了MKNOD之外的所有内核权限,那么可以执行下面的命令: $ sudo docker run --cap-add=ALL --cap-drop=MKNOD ...文章源自靠谱运维-https://www.ixdba.net/archives/1921

比如现在我们需要修改网络接口数据,默认情况下是没有权限的,因为需要的 NET_ADMIN 这个 Capabilities 默认被移除了:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ docker run -it --rm busybox /bin/sh
/ # ip link add dummy0 type dummy
ip: RTNETLINK answers: Operation not permitted
/ #

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

所以在不使用 --privileged 的情况下(不建议)我们可以使用 --cap-add=NET_ADMIN 将这个 Capabilities 添加回来:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ docker run -it --rm --cap-add=NET_ADMIN busybox /bin/sh
/ # ip link add dummy0 type dummy
/ # 

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

可以看到已经 OK 了。文章源自靠谱运维-https://www.ixdba.net/archives/1921

Kubernetes 配置 Capabilities

上面我介绍了在 Docker 容器下如何来配置 Capabilities,在 Kubernetes 中也可以很方便的来定义,我们只需要添加到 Pod 定义的 spec.containers.sercurityContext.capabilities中即可,也可以进行 add 和 drop 配置,同样上面的示例,我们要给 busybox 容器添加 NET_ADMIN 这个 Capabilities,对应的 YAML 文件可以这样定义:(cpb-demo.yaml)文章源自靠谱运维-https://www.ixdba.net/archives/1921

apiVersion: v1
kind: Pod
metadata:
  name: cpb-demo
spec:
  containers:
  - name: cpb
    image: busybox
    args:
    - sleep
    - "3600"
    securityContext:
      capabilities:
        add: # 添加
        - NET_ADMIN
        drop:  # 删除
        - KILL

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

我们在 securityContext 下面添加了 capabilities 字段,其中添加了 NET_ADMIN 并且删除了 KILL 这个默认的容器 Capabilities,这样我们就可以在 Pod 中修改网络接口数据了:文章源自靠谱运维-https://www.ixdba.net/archives/1921

$ kubectl apply -f cpb-demo.yaml
$ kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
cpb-demo                  1/1     Running   0          2m9s
$ kubectl exec -it cpb-demo /bin/sh
/ # ip link add dummy0 type dummy
/ #

在 Kubernetes 中通过 sercurityContext.capabilities 进行配置容器的 Capabilities,当然最终还是通过 Docker 的 libcontainer 去借助 Linux kernel capabilities 实现的权限管理。文章源自靠谱运维-https://www.ixdba.net/archives/1921

为容器设置 SELinux 标签

SELinux (Security-Enhanced Linux) 是一种强制访问控制(mandatory access control)的实现。它的作法是以最小权限原则(principle of least privilege)为基础,在 Linux 核心中使用 Linux 安全模块(Linux Security Modules)。Pod 或容器定义的 securityContext 中 seLinuxOptions 字段是一个 SELinuxOptions 对象,该字段可用于为容器指定 SELinux 标签。文章源自靠谱运维-https://www.ixdba.net/archives/1921

securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"

 文章源自靠谱运维-https://www.ixdba.net/archives/1921

AppArmor

TODO文章源自靠谱运维-https://www.ixdba.net/archives/1921

Seccomp

TODO文章源自靠谱运维-https://www.ixdba.net/archives/1921

除了这些针对容器和 Pod 的一些安全管控之外,还有集群级别的安全策略设置 Pod Security Policies(PSP),可以自动为集群内的 Pod 和 Volume 设置 Security Context。文章源自靠谱运维-https://www.ixdba.net/archives/1921

  • 本文由 发表于 2023年11月7日14:42:24
  • 转载请务必保留本文链接:https://www.ixdba.net/archives/1921
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: