为什么在Kubernetes PodSecurityPolicy中要求放弃所有的能力与非root用户和禁止特权升级重复?

5
第二个例子策略来自PodSecurityPolicy文档,由以下PodSecurityPolicy片段组成。
...
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant with non-root + disallow privilege escalation,
  # but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
...

为什么对于非根用户和禁止特权升级来说,放弃所有能力是多余的?你可以拥有一个没有特权升级但具有有效能力的非根容器进程,对吗?

似乎在Docker中这是不可能的:

$ docker run --cap-add SYS_ADMIN --user 1000 ubuntu grep Cap /proc/self/status
CapInh: 00000000a82425fb
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000a82425fb
CapAmb: 0000000000000000

即使尝试明确添加有效能力,所有有效能力都已被删除。但是其他容器运行时可能会实现它,那么这个评论只针对Docker吗?
2个回答

8

为什么对于非root用户和禁止特权升级来说,放弃所有功能是多余的?

因为你需要特权升级才能使用“新”的功能,有效地allowPrivilegeEscalation: false就是在execve系统调用中禁用setuid,这会阻止使用任何新功能。
此外,正如文档中所示:“一旦设置了该位,它将在fork、clone和execve之间继承,并且无法取消设置”。更多信息请参见此处

privileged: false相结合,requiredDropCapabilities: [ALL]变得多余。

这里的等效Docker选项是:

  • --user=whatever => privileged: false
  • --security-opt=no-new-privileges => allowPrivilegeEscalation: false
  • --cap-drop=all => requiredDropCapabilities: [ALL]

看起来 Docker 不支持此操作

当你指定一个非特权用户时,Docker 会删除所有有效能力 (CapEff: 0000000000000000),即使你使用 --cap-add SYS_ADMIN

这个与选项 --security-opt=no-new-privileges 结合使用将使 --cap-drop=all 失效。

请注意,Docker 的默认能力掩码似乎包括 SYS_ADMIN

$ docker run --rm ubuntu grep Cap /proc/self/status
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
$ capsh --decode=00000000a82425fb
0x00000000a82425fb=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_admin,cap_mknod,cap_audit_write,cap_setfcap

这就解释了为什么不指定--cap-add选项时,00000000a82425fb是相同的。

但其他容器运行时可能会实现它,那么这个评论只是针对Docker吗?

我想是的,所以你可能会遇到这种情况:privileged: falseallowPrivilegeEscalation: false并没有有效地禁用功能,这可以通过requiredDropCapabilities:来丢弃(尽管我不明白为什么另一个运行时会想要更改Docker的行为)。

为什么需要特权升级才能使用功能?通过分叉,能够继承功能而无需父进程或子进程通过exec升级权限 - 如果您确实需要特权升级才能使用功能,则这不再是Docker特定的问题,而是更普遍地适用于Linux。 - dippynark
没错,我的意思是新的功能,这样与 privileged: false 结合使用就可以使 requiredDropCapabilities: ALL 失效了。我想当你指定 --user 1000 时,Docker 就是这样做的。 - Rico
稍微修改了答案,希望能更清晰地表达。如果还有其他意见或问题,请告诉我。 - Rico
谢谢 - 我认为这里的主要问题是特权的含义 - 在这里,特权字面上只是指能够在 Pod 规范中设置 privileged: true,因此您可以拥有功能而不必具备特权。我认为这仅适用于 Docker 将非 root 用户的所有有效功能都删除的情况,因此将其与禁止特权升级相结合可以防止它们获得任何功能,但并非所有运行时都会这样做,在这种情况下,要求在容器启动时放弃所有功能实际上是不同的 - 您同意吗? - dippynark
1
是的,我同意。唯一的问题是我不明白为什么另一个运行时想要改变这种行为,但这是可能的。在 priviledged: false + allowPrivilegeEscalation: false 的情况下不会丢失有效能力,并保留一些能够通过 requiredDropCapabilities: <something> 丢弃的能力。 - Rico
显示剩余2条评论

4

你的问题中有多个(好的)子问题。
我想重点关注主要问题:

为什么对于非root用户+禁止权限提升而言,放弃所有能力是多余的?

为了让它更简单,我认为我们可以把重点放在禁止权限提升部分,并简单地问:

当我们在PodSecurityPolicy中设置allowPrivilegeEscalation: false时,背后会发生什么?

K8S文档中可以看到,“这个布尔值直接控制容器进程是否设置no_new_privs标志”。

那么如果设置了这个标志会发生什么?

引用自内核文档“当设置了此标志时,execve承诺不授予任何不能通过execve调用实现的权限。
例如,setuid和setgid位将不再更改uid或gid;文件功能也不会添加到允许的集合中”

换句话说,设置allowPrivilegeEscalation:false将导致删除所有功能。

这就是为什么认为添加此部分是多余的原因:

 requiredDropCapabilities:
    - ALL

我希望这能使事情更简单一些。
我认为其他问题的答案在被接受的答案中已经很明确了,我没有什么可以补充的。

注意:如果您运行的内核版本>=4.10,则可以在/proc/[pid]/status文件中查看线程的no_new_privs属性值 - 在能力属性下:

.
.
CapInh: 00000000a82425fb
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000a82425fb
CapAmb: 0000000000000000
NoNewPrivs: 0 <-----
.
.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接