使用 runAsNonRoot 运行容器并添加功能

问题描述 投票:0回答:2

我试图以非root身份运行我的pod,并授予它一些功能
这是我的配置:

 containers:
    - name: container-name
        securityContext:
          capabilities:
            add: ["SETUID", "SYS_TIME"]
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1001

当我部署 Pod 并连接到它时,我运行

ps aux
并看到:

PID   USER     TIME  COMMAND
    1 root      0:32 node bla.js
  205 root      0:00 /bin/bash
  212 root      0:00 ps aux

然后我做

cat /proc/1/status
并看到:

CapPrm: 0000000000000000
CapEff: 0000000000000000

这意味着我没有能力处理这个容器的进程。
问题是,如果我从

runAsNonRoot: true
中删除
securityContext
标志,我可以看到我 do 具有多种功能。
有没有办法以非 root 身份运行 pod 并仍然添加一些功能?

linux kubernetes linux-capabilities
2个回答
3
投票

这是预期的行为。这些功能旨在将传统上与超级用户(root)相关的权限划分为不同的单元;非 root 用户无法启用/禁用此类功能,这可能会造成安全漏洞。

capabilities
键中的
SecurityContext
功能旨在管理(限制或扩展)容器上下文的Linux功能;在以 root 身份运行的 pod 中,这意味着这些功能由进程继承,因为这些功能由 root 用户拥有;但是,如果 pod 以非 root 用户身份运行,则上下文是否启用这些功能并不重要,因为 Linux 内核不允许非 root 用户为进程设置功能。

这一点很容易说明。如果您将

runAsNonRoot
键设置为
true
来运行容器,并像在共享清单中那样添加功能,然后执行到 Pod 中,您应该能够看到这些功能已添加到上下文中命令:

$ capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap+i
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap

但是您会在用户 1001 运行的任何进程中看到

CapPrm
CapEff
设置为 x0:

$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
1001           1  0.0  0.0   4340   760 ?        Ss   14:57   0:00 /bin/sh -c node server.js
1001           7  0.0  0.5 772128 22376 ?        Sl   14:57   0:00 node server.js
1001          21  0.0  0.0   4340   720 pts/0    Ss   14:59   0:00 sh
1001          28  0.0  0.0  17504  2096 pts/0    R+   15:02   0:00 ps aux
$ grep Cap proc/1/status
CapInh: 00000000aa0425fb
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000aa0425fb
CapAmb: 0000000000000000

0
投票

接受的答案只是故事的一部分。您可以实现您正在寻找的目标,但您还需要在正在执行的文件上设置功能。

您需要阅读 Linux capability(7) 手册。该手册讨论了两个不同来源的功能集,它们组合在一起形成最终的功能集:调用 execve() 之前“线程”的功能和文件功能。最终的能力集基本上是这两个来源的结合。当 execve 以 root 身份运行进程时,规则有点不同,但本质上文件功能被忽略。

当您通过 CLI 标志、或者在 docker compose 文件中、或者在 k8s securitycontext.capability 块中向 docker 提供一组功能时,最终将导致 containerd 导致 runc 执行具有这些请求功能的进程(如边界集、允许集和有效集)在 execve 之前设置。然后根据功能手册中的规则定义最终的功能集。

以 root 身份运行时的功能

如果您查看

功能(7)手册

“root 程序的功能和执行”部分,您将在接受的答案中找到所发生情况的描述。

如果进程的真实或有效用户ID为0(root), 那么文件可继承集和允许集将被忽略; 相反,它们在理论上被认为是全一(即, 启用所有功能)。

最终结果是最终的功能集就是您提供的功能集。

例如给定 main.go:

package main import "fmt" import "kernel.org/pub/linux/libs/security/libcap/cap" func main() { c := cap.GetProc() fmt.Printf("this process has these caps:", c) }

docker文件:

FROM golang:1.18 as build WORKDIR /go/src/app COPY <<EOF ./go.mod module "app" go 1.18 EOF RUN go get "kernel.org/pub/linux/libs/security/libcap/cap" RUN go mod download COPY main.go . RUN CGO_ENABLED=0 go build -o /go/bin/app FROM gcr.io/distroless/static-debian11 COPY --from=build --chmod=550 --chown=0:0 /go/bin/app / CMD ["/app"]

和 docker-compose.yml:

version: '3.4' services: scratch: user: 0:0 cap_drop: - ALL cap_add: - SYS_TIME build: context: . dockerfile: ./Dockerfile

运行scratch会打印出
this process has these caps:%!(EXTRA *cap.Set=cap_sys_time=ep)

以非 root 身份运行时的功能

在这种情况下,文件功能不会被忽略。从手册

在 execve(2) 期间,内核计算新功能 使用以下算法的过程:

P'(ambient) = (file is privileged) ? 0 : P(ambient) P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding)) | P'(ambient) P'(effective) = F(effective) ? P'(permitted) : P'(ambient) P'(inheritable) = P(inheritable) [i.e., unchanged] P'(bounding) = P(bounding) [i.e., unchanged] where: P() denotes the value of a thread capability set before the execve(2) P'() denotes the value of a thread capability set after the execve(2) F() denotes a file capability set
因此,如果我们更新 dockerfile 并像这样编写文件:
dockerfile:

FROM golang:1.18 as build WORKDIR /go/src/app COPY <<EOF ./go.mod module "app" go 1.18 EOF RUN go get "kernel.org/pub/linux/libs/security/libcap/cap" RUN go mod download COPY main.go . RUN CGO_ENABLED=0 go build -o /go/bin/app FROM gcr.io/distroless/static-debian11:nonroot COPY --from=build --chmod=550 --chown=65534:65534 /go/bin/app / CMD ["/app"]

和 docker-compose.yml:

version: '3.4' services: scratch: user: 65534:65534 cap_drop: - ALL cap_add: - SYS_TIME build: context: . dockerfile: ./Dockerfile

运行scratch会打印出
this process has these caps:%!(EXTRA *cap.Set==)

,即无!因此,当不以 root 身份运行时,您无法单独使用 docker compose 文件中的功能。 (注意 65534 是“nobody”用户和组)。

但是,如果我们在 dockerfile 中设置文件的功能:

FROM golang:1.18 as build RUN apt-get update RUN apt-get install -y libcap2-bin WORKDIR /go/src/app COPY <<EOF ./go.mod module "app" go 1.18 EOF RUN go get "kernel.org/pub/linux/libs/security/libcap/cap" RUN go mod download COPY main.go . RUN CGO_ENABLED=0 go build -o /go/bin/app RUN setcap 'cap_sys_time=ep' /go/bin/app FROM gcr.io/distroless/static-debian11:nonroot COPY --from=build --chmod=550 --chown=65534:65534 /go/bin/app / CMD ["/app"]

无需对 docker compose 文件进行进一步更改,输出现在为 
this process has these caps:%!(EXTRA *cap.Set=cap_sys_time=ep)

。因此,该进程不是以 root 身份运行,但确实具有您所追求的额外功能。然而,如果您想向在解释器中运行的某些东西(例如 python 或 bash)添加功能,这确实是一件令人头疼的事情——因为解释器是需要添加功能的东西。

    

© www.soinside.com 2019 - 2024. All rights reserved.