我的团队正在广场建造一个通用的Kubernetes集群。作为扩展的一部分,我们实施了Pod安全策略(PSP)来保护我们的群集免受许多容器逃逸风险。
这篇文章的重点是如何全面部署Pod安全策略,并锁定所有内容,以及如何授予例外。有关PSP的介绍,请阅读Kubernetes PSP官方文档。官方文档很好地展示了如何使用一个简单的PSP示例,但是没有描述一个功能齐全的PSP系统将如何工作,它将如何在默认情况下应用,以及如何管理异常。这篇帖子就是为了帮助填补这一空白而写的。最后,我们将讨论您在实施PSP时可能遇到的几个陷阱,并讨论一些故障排除策略。
Kubernetes是一个非常强大和复杂的工具;然而,这在社区内导致了几个安全问题。大多数库伯内斯的安全故障可分为两大阵营:
利用PSP的所有功能可以显著增加容器逃逸的难度,从而缩小第一个主要攻击载体。PSP不是灵丹妙药(什么都不是),但它们是保护集群和工作负载的强大工具。
注:以上是对复杂主题的简化。有关更多信息,微软最近发布了针对Kubernetes的攻击矩阵。PSP允许您限制31个挑战中的9个。
这篇文章中演示的PSP可以在以后的版本中使用,但是您应该仔细阅读官方文档,以确保列举了所有可用的控件。
为了简化异常管理,本文中的PSP示例在语法中只采用了很少的快捷方式,并显式列举了所有功能/权限。例如:对于requidDropCapability,我们提到了每种功能,而不是使用";ALL";。
本文中的PSP避免AppArmor和SELinux管理,因为这两个主题本身都是博客文章系列,只提到存根。
-api版本:策略/v1beta1Kind:PodSecurityPolicyMetadata:注释:kubernetes.io/Description:';所有标准用例的受限psp';seccomp.security.alpha.kubernetes.io/allowedProfileNames:停靠器/默认seccomp.security.alpha.kubernetes.io/defaultProfileName:停靠器/默认名称:restrictedspec:#alloweCapability:#-NET_BIND_SERVICE#如果工作负载需要低端口,则使用#-NET_BIND_SERVICE#。但默认情况下,它已注释掉#alloweHostPath:#-path Prefix:";/ome/path";#如何允许特定主机路径的示例#readOnly:true allowPrivilegeEscalation:false#禁止将权限提升到允许的任何特殊功能Procmount Types:#不允许完全/proc装载,仅允许";默认值";掩码/proc-default fsGroup:#不允许根fsGroups进行卷装载规则:MustRunAs范围:-max:65535分钟:1主机Ipc:False#不允许共享主机ipc命名空间主机网络:FALSE#不允许主机联网主机PID:FALSE#不允许共享主机进程ID命名空间主机端口:#不允许低位主机端口(这似乎只适用于EKS上的eth0)-最大:65535分钟:1025特权:FALSE#不允许特权POST。quiredDropCapability:#删除Linux内核中的所有权限-AUDIT_CONTROL-AUDIT_READ-WRITE-BLOCK_SUSPEND-CHOWN-DAC_OVERRIDE-DAC_READ_SEARCH-FOWNER-FSETID-IPC_LOCK-IPC_OWNER-KILL-LE-LINUX_IMMOTABLE-MAC_ADMIN-MAC_OVERRIDE-MKNOD-NET_ADMIN-NET_BIND_SERVICE-NET_BROADOAD-NET_RAW-SETGID-SETFCAR。sys_ptrace-SYS_RAWIO-SYS_RESOURCE-SYS_TIME-SYS_TTY_CONFIG-SYSLOG-WAKE_ALARM runAsGroup:#不允许Pod的GID 0(块根组)规则:MustRunAs范围:-max:65535分钟:1 RunAsUser:#不允许Pod的UID 0规则:MustRunAsNonRoot seLinux:#利用SELinux规则:RunAsAny补充组:#限制补充。emptyDir-Projected-Secret#-hostPath#默认情况下不允许使用主机路径。#-sistentVolumeClaim#如果使用statefulsets,你';我需要这件。
注意:您可以在此处找到所有Kubernetes卷类型的列表。您可能需要不同的卷列表,只是要小心使用它们。
然后,您必须确保所有用户都可以访问PSP。要理智地做到这一点,您需要授予所有用户对最严格的PSP的访问权限。
我们将这个受限的PSP授予System:Authenticated组,而不是枚举所有用户(这可能最终导致在启用PSP之后添加的丢失用户)。如果用户缺乏更明确的权限,则SYSTEM:AUTHENTED组是所有通过身份验证的用户的备用组。
注意:如果您允许未经身份验证的用户部署工作负载,则PSP可能不足以满足您的问题空间。但是,我要注意的是,确实存在System:UnAuthenticated组。
要附加此PSP访问,需要ClusterRole授予权限,并将ClusterRoleBinding绑定到SYSTEM:AUTHENTIZED组。ClusterRole和ClusterRoleBinding用于启用PSP访问,因为我们希望将此限制应用于群集中所有经过身份验证的用户。此ClusterRole用法不会取代限制用户功能(如创建Pod或部署)的其他现有RBAC策略。
-apiVersion:rbac.authization.k8s.io/v1Kind:ClusterRolemetadata:labels:eks.amazonaws.com/component:pod-security-policy kubernetes.io/cluster-service:";true";名称:PSP-受限规则:-apiGroups:-policy resource名称:-受限资源:-podsecuritypolicy谓词:-use-api版本:rbac.authization.k8s.io/v1Kind:ClusterRoleBindingMetadata:注释:kubernetes.io/Description:绑定到系统的限制性PSP:已通过身份验证以覆盖所有用户标签:eks.amazonaws.com/component:pod-security-policy kubernet.com/component:pod-security-policy kubernet。名称:psp-restrictedroleRef:apiGroup:rbac.authization.k8s.io Kind:群集角色名称:PSP-RestrictedSubjects:-apiGroup:rbac.authization.k8s.io Kind:组名:系统:已验证。
监控节点和工作负载?它可能需要一些特殊的主机挂载,以及可怕的/var/run/docker.sock
最后,每个异常都需要四样东西:一个自定义PSP、一个ServiceAccount、一个角色或ClusterRole,以及一个RoleBinding或ClusterRoleBinding。
如果您的用例仅在单个命名空间中创建工作负载,则可以使用角色/角色绑定。如果您的异常用例将在几个名称空间中重用给定的PSP,您可以为每个名称空间使用一个ClusterRole和一个服务帐户/RoleBinding。或者,每个NS可以有一个ServiceAccount和一个ClusterRole和ClusterRoleBinding,后者提到所有ServiceAccount。
我确定的模式是PSP、集群角色和绑定到它的角色。这具有将所有与PSP相关的群集角色保持在一起以进行审核的优点。使用到ClusterRole的RoleBinding还将PSP异常限制为单个命名空间中的工作负载。
下面显示了一个涵盖fluentd DaemonSet(fluentd将日志消息导出到其最终目标)的PSP异常示例,因为与限制性的默认PSP相比,它需要大量的异常:
-api版本:策略/v1beta1种类:PodSecurityPolicyMetadata:注释:kubernetes.io/Description:';针对Fluentd->;Fluentd';seccomp.security.alpha.kubernetes.io/allowedProfileNames:扩展坞/默认seccomp.security.alpha.kubernetes.io/defaultProfileName:扩展坞/默认名称:fluentd-fluentdspec:#alloweCapability:#-NET_BIND_SERVICE#在工作负载需要低端口时有用。但默认情况下,允许将其注释掉HostPath:-path Prefix:";/run/fluentd/data";#Exception:Fluentd需要一个主机持久位置来存储状态readOnly:false-path Prefix:";/var/lib/docker/Containers";#Exception:Fluentd需要具有对码头容器日志ReadOnly:true-path Prefix:";/var/log";的读取权限。#Exception:fluentd需要主机日志的读取权限只读:true allowPrivilegeEscalation:true#Exception:fluentd需要root用户才能读取允许的日志Procmount类型:#禁止完全/proc装载,只允许";默认值";MASTED/PROC-Default fsGroup:#Exception:Fluentd以特权身份运行,因此需要fsGroup 65535规则:MustRunAs范围:-max:1025min:0 hostIpc:FALSE#不允许共享主机ipc命名空间hostNetwork:FALSE#不允许共享主机联网主机PID:FALSE#不允许共享主机进程ID命名空间hostPort:#不允许低位主机端口(这似乎只适用于EKS上的eth0)-max:1025min:65535特权:TRUE#EXCEPTION。quiredDropCapability:#删除Linux内核中的所有权限-AUDIT_CONTROL-AUDIT_READ-WRITE-BLOCK_SUSPEND-CHOWN-DAC_OVERRIDE-DAC_READ_SEARCH-FOWNER-FSETID-IPC_LOCK-IPC_OWNER-KILL-LE-LINUX_IMMOTABLE-MAC_ADMIN-MAC_OVERRIDE-MKNOD-NET_ADMIN-NET_BROADIO-NET_RAW-SETGID-SETFCAP-SETPCAP。RAWIO-SYS_RESOURCE-SYS_TIME-SYS_TTY_CONFIG-SYSLOG-WAKE_ALARM runAsGroup:#Exception:Fluentd以特权身份运行,因此需要GID 0规则:MustRunAs范围:-max:65535分钟:0运行方式用户:规则:MustRunAs#Exception:Fluentd以特权身份运行,因此需要UID 0范围:-max:65535分钟:0 seLinux:#适用于SELinux的工具规则:RunAs任何补充组:#Exception:Fluentd以特权身份运行,因此需要GID 0规则:MustRunAs范围:-max:65535分钟:0卷。你';我需要这件。
注意:每个异常都以YAML注释的形式调用,并带有单词";异常";。这种做法使您可以在管理异常的代码中轻松跟踪异常,并使审计师在他们来访时感到高兴。
注意2:这与限制性PSP的布局相同,以便于使用要验证的风格工具。
为了允许Fluentd使用上面的PSP,我们有这个ClusterRole和RoleBinding:
-api版本:rbac.Authization.k8s.io/v1Kind:ClusterRolemetadata:Labels:eks.amazonaws.com/Component:pod-security-policy kubernetes.io/cluster-service:";true";名称:psp-fluentd-fluentd#为策略api组命名:-psp-<;namespace>;-<;serviceaccount>;rules:-资源名称:-fluentd-fluentd资源:-podsecuritypolicy谓词:-Use。为fluentd->;fluentd';量身定做的PSP标签:eks.amazonaws.com/component:pod-security-policy kubernetes.io/cluster-service:";true";名称:psp-fluentd-fluentd命名空间:fluentdroleRef:apiGroup:rbac.Authization.k8s.io Kind:ClusterRole名称:psp-fluentd-fluentdSubjects:-Kind:Servic。
要使用此PSP,DaemonSet只需在其Pod规范中指定serviceAccountName:fluentd,并在fluentd命名空间中运行以利用RoleBinding。
上面的流畅工作负载将可以访问两个不同的PSP.。这是怎么运作的?
PSP准入控制器按字母顺序对所有允许的PSP运行给定的PSP。使用允许工作负载的第一个PSP。人们似乎会纠结于按字母顺序排列,但实际上这没什么大不了的。此框架中的工作负载将可以访问一个或两个PSP:完全受限的PSP,也可能是特殊的PSP。首先评估哪一个并不重要,因为PSP是在一个单独的策略中处理的,如果只有一个策略允许该工作,它就会进入模式。如果存在完全受限的PSP,则其工作方式实际上类似于默认拒绝防火墙策略。
如果没有PSP允许该工作负载,您将看到一个Kubernetes事件,该事件显示工作负载针对其拥有访问权限的每个PSP所具有的所有错误的并集。这使得故障排除相当痛苦。
缺点是,当工作负荷使所有可用的PSP失效时,几乎没有提供有用的信息。
警告失败创建1s(3s上的x2)后台启动-控制器(由类似事件组合而成):创建错误:Pod";fluentd-4mqhj";被禁止:无法根据任何Pod安全策略进行验证:[]。
我用来帮助分类这些问题的一个策略是临时kubectl删除PSP限制,这样我就只能看到异常试图利用的实际PSP中的错误。显然,此测试将中断正在运行的群集的新工作负载(无PSP?没有启动),因此我们建议在非生产环境中进行此类分类。
到目前为止,我们在启用PSP时遇到的最大挑战是围绕着我们变异的准入控制器。
在幕后,Kubernetes准入控制器(包括PSP)具有复杂的优先级层次结构,导致PSP运行两次。以下是网络挂钩的订购顺序:
内置变异WebHooks:POD安全策略变异WebHook在此处运行,以根据确定最适用的PSP修改POD默认值。
内置验证WebHooks:Pod Security Policies验证WebHook在此处运行,以验证Pod对象是否仍符合PSP要求。
总而言之,上面的设置是合理的,可能也是启用所有这些不同的WebHook功能的唯一方法。然而,它几乎没有记录在案(除非你偶然发现了正确的GitHub问题,比如istio上的这个问题)。更糟糕的是,PSP准入控制器的验证版本产生的错误消息为零,因此您是在盲目地对其进行故障排除。
最后,如果您有一个变异的准入控制器,您需要确保它的突变对于集群中限制最严格的PSP是完全安全的。而且这个验证可能会很繁琐(对我们来说是这样),所以我们才会写这本指南。