Kubernetes安全机制深度解析(三):准入控制器
#作者:程宏斌
文章目录
- 准入控制器(Admission Controllers)
- 准入控制插件
- 准入控制扩展点
- 准入控制阶段
- 为什么需要准入控制器?
- 怎么关闭准入控制器?
- 哪些插件是默认启用的?
- 准入控制器的作用
准入控制器(Admission Controllers)
准入控制器是一段代码,它会在请求通过认证和鉴权之后、对象被持久化之前拦截到达 API 服务器的请求。
Kubernetes 的若干重要功能都要求启用一个准入控制器,以便正确地支持该特性。 因此,没有正确配置准入控制器的 Kubernetes API 服务器是不完整的,它无法支持你所期望的所有特性。
准入控制插件
准入控制器是 Kubernetes API 服务器中的代码, 用于检查请求中到达的数据,以修改资源。
准入控制器适用于创建、删除或修改对象的请求。 准入控制器也可以阻止自定义动作,例如通过 API 服务器代理连接到 Pod 的请求。 准入控制器不会(也不能)阻止读取(get、watch 或 list)对象的请求, 这是因为读取操作会绕过准入控制层。
准入控制器机制可以执行验证(Validating) 和/或变更(Mutating) 操作。 变更(Mutating)控制器可以为正在修改的资源修改数据;验证(Validating)控制器则不行。
Kubernetes 1.33 中的准入控制器由下面的列表组成, 并编译进 kube-apiserver 可执行文件,并且只能由集群管理员配置。
准入控制扩展点
在完整的列表中,有三个特殊的控制器: MutatingAdmissionWebhook、 ValidatingAdmissionWebhook 和 ValidatingAdmissionPolicy。 前两个 Webhook 控制器分别执行在 API 中所配置的变更和验证准入控制 Webhook。 而 ValidatingAdmissionPolicy 提供了一种在 API 中嵌入声明式验证代码的方式,无需依赖任何外部 HTTP 调用。
你可以使用这三个准入控制器来定制准入时的集群行为。
准入控制阶段
准入控制过程分为两个阶段。第一阶段,运行变更准入控制器。第二阶段,运行验证准入控制器。 再次提醒,某些控制器既是变更准入控制器又是验证准入控制器。
如果两个阶段之一的任何一个控制器拒绝了某请求,则整个请求将立即被拒绝,并向最终用户返回错误。
最后,除了对对象进行变更外,准入控制器还可能有其它副作用:将相关资源作为请求处理的一部分进行变更。 增加配额用量就是一个典型的示例,说明了这样做的必要性。 此类用法都需要相应的回收或回调过程,因为任一准入控制器都无法确定某个请求能否通过所有其它准入控制器。
为什么需要准入控制器?
Kubernetes 的多个重要特性需要按顺序启用某个准入控制器才能正确支持对应的特性。 因此,如果 Kubernetes API 服务器未正确配置相应的准入控制器集, 那么这种 API 服务器将是不完整的,并且无法支持你所期望的所有特性。
如何启用一个准入控制器?
Kubernetes API 服务器的 enable-admission-plugins 标志接受一个(以逗号分隔的)准入控制插件列表, 这些插件会在集群修改对象之前被调用。
例如,下面的命令启用 NamespaceLifecycle 和 LimitRanger 准入控制插件:
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger …
说明:
根据你 Kubernetes 集群的部署方式以及 API 服务器的启动方式,你可能需要以不同的方式应用设置。 例如,如果将 API 服务器部署为 systemd 服务,你可能需要修改 systemd 单元文件; 如果以自托管方式部署 Kubernetes,你可能需要修改 API 服务器的清单文件。
怎么关闭准入控制器?
Kubernetes API 服务器的 disable-admission-plugins 标志,会将传入的(以逗号分隔的) 准入控制插件列表禁用,即使是默认启用的插件也会被禁用。
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny …
哪些插件是默认启用的?
要查看哪些插件是被启用的:
kube-apiserver -h | grep enable-admission-plugins
在 Kubernetes 1.33 中,默认启用的插件有:
CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, Defaul
准入控制器的作用
AlwaysAdmit
特性状态: Kubernetes v1.13 [deprecated]
类别:验证。
该准入控制器允许所有的 Pod 进入集群。此插件已被弃用,因其行为与没有准入控制器一样。
AlwaysDeny
特性状态: Kubernetes v1.13 [deprecated]
类别:验证。
拒绝所有的请求。由于它没有实际意义,已被弃用。
AlwaysPullImages
该准入控制器会修改每个新创建的 Pod,将其镜像拉取策略设置为 Always。 这在多租户集群中是有用的,这样用户就可以放心,他们的私有镜像只能被那些有凭证的人使用。 如果没有这个准入控制器,一旦镜像被拉取到节点上,任何用户的 Pod 都可以通过已了解到的镜像的名称 (假设 Pod 被调度到正确的节点上)来使用它,而不需要对镜像进行任何鉴权检查。 启用这个准入控制器之后,启动容器之前必须拉取镜像,这意味着需要有效的凭证。
CertificateApproval
类别:验证。
此准入控制器获取审批 CertificateSigningRequest 资源的请求并执行额外的鉴权检查, 以确保针对设置了 spec.signerName 的 CertificateSigningRequest 资源而言, 审批请求的用户有权限对证书请求执行 审批 操作。
有关对 CertificateSigningRequest 资源执行不同操作所需权限的详细信息, 请参阅证书签名请求。
CertificateSigning
类别:验证。
此准入控制器监视对 CertificateSigningRequest 资源的 status.certificate 字段的更新请求, 并执行额外的鉴权检查,以确保针对设置了 spec.signerName 的 CertificateSigningRequest 资源而言, 签发证书的用户有权限对证书请求执行 签发 操作。
有关对 CertificateSigningRequest 资源执行不同操作所需权限的详细信息, 请参阅证书签名请求。
CertificateSubjectRestriction
类别:验证。
此准入控制器监视 spec.signerName 被设置为 kubernetes.io/kube-apiserver-client 的 CertificateSigningRequest 资源创建请求,并拒绝所有将 “group”(或 “organization attribute”) 设置为 system:masters 的请求。
DefaultIngressClass
类别:变更。
该准入控制器监测没有请求任何特定 Ingress 类的 Ingress 对象创建请求,并自动向其添加默认 Ingress 类。 这样,没有任何特殊 Ingress 类需求的用户根本不需要关心它们,他们将被设置为默认 Ingress 类。
当未配置默认 Ingress 类时,此准入控制器不执行任何操作。如果有多个 Ingress 类被标记为默认 Ingress 类, 此控制器将拒绝所有创建 Ingress 的操作,并返回错误信息。 要修复此错误,管理员必须重新检查其 IngressClass 对象,并仅将其中一个标记为默认 (通过注解 “ingressclass.kubernetes.io/is-default-class”)。 此准入控制器会忽略所有 Ingress 更新操作,仅处理创建操作。
关于 Ingress 类以及如何将 Ingress 类标记为默认的更多信息,请参见 Ingress 页面。
DefaultStorageClass
类别:变更。
此准入控制器监测没有请求任何特定存储类的 PersistentVolumeClaim 对象的创建请求, 并自动向其添加默认存储类。 这样,没有任何特殊存储类需求的用户根本不需要关心它们,它们将被设置为使用默认存储类。
当默认的 StorageClass 不存在时,此准入控制器不执行任何操作。如果将多个存储类标记为默认存储类, 而且你之后在未设置 storageClassName 的情况下创建 PersistentVolumeClaim, Kubernetes 将使用最近创建的默认 StorageClass。 当使用指定的 volumeName 创建 PersistentVolumeClaim 时,如果在应用任意默认的 StorageClass 之后, 静态卷的 storageClassName 与 PersistentVolumeClaim 上的 storageClassName 不匹配, 则 PersistentVolumeClaim 保持在 Pending 状态。 此准入控制器会忽略所有 PersistentVolumeClaim 更新操作,仅处理创建操作。
关于持久卷申领和存储类,以及如何将存储类标记为默认, 请参见持久卷页面。
DefaultTolerationSeconds
类别:变更。
此准入控制器基于 k8s-apiserver 的输入参数 default-not-ready-toleration-seconds 和 default-unreachable-toleration-seconds 为 Pod 设置默认的容忍度,以容忍 notready:NoExecute 和 unreachable:NoExecute 污点 (如果 Pod 尚未容忍 node.kubernetes.io/not-ready:NoExecute 和 node.kubernetes.io/unreachable:NoExecute 污点的话)。 default-not-ready-toleration-seconds 和 default-unreachable-toleration-seconds 的默认值是 5 分钟。
DenyServiceExternalIPs
类别:验证。
此准入控制器拒绝新的 Service 中使用字段 externalIPs。 此功能非常强大(允许网络流量拦截),并且无法很好地受策略控制。 启用后,集群用户将无法创建使用 externalIPs 的新 Service,也无法在现有 Service 对象上为 externalIPs 添加新值。 externalIPs 的现有使用不受影响,用户可以在现有 Service 对象上从 externalIPs 中删除值。
大多数用户根本不需要此特性,集群管理员应考虑将其禁用。 确实需要使用此特性的集群应考虑使用一些自定义策略来管理 externalIPs 的使用。 此准入控制器默认被禁用。
PodTopologyLabels
特性状态: Kubernetes v1.33 []
类型:变更
PodTopologyLabels 准入控制器变更所有绑定到节点的 pod 的 pods/binding 子资源, 添加与所绑定节点相匹配的拓扑标签。 这使得节点拓扑标签可以作为 Pod 标签使用,并且可以通过 Downward API 提供给运行中的容器。 由于此控制器而可用的标签是 topology.kubernetes.io/region 和 topology.kubernetes.io/zone 标签。
{{ }}
如果有任何变更的准入 Webhook 添加或修改了 pods/binding 子资源的标签, 这些变更将由于此控制器传播到 Pod 标签,覆盖具有冲突键的标签。 {{ }}
当 PodTopologyLabelsAdmission 特性门控被启用时,才能启用此准入控制器。
EventRateLimit
特性状态: Kubernetes v1.13 [alpha]
类别:验证。
此准入控制器缓解了请求存储新事件时淹没 API 服务器的问题。集群管理员可以通过以下方式指定事件速率限制:
启用 EventRateLimit 准入控制器;
在通过 API 服务器的命令行标志 --admission-control-config-file 设置的文件中, 引用 EventRateLimit 配置文件
可以在配置中指定的限制有四种类型:
- Server:API 服务器收到的所有(创建或修改)Event 请求共享一个桶。
- Namespace:每个名字空间都对应一个专用的桶。
- User:为每个用户分配一个桶。
- SourceAndObject:根据事件的来源和涉及对象的各种组合分配桶。
下面是一个针对此配置的 eventconfig.yaml 示例:
apiVersion: eventratelimit.admission.k8s.io/v1alpha1
kind: Configuration
limits:
- type: Namespace
qps: 50
burst: 100
cacheSize: 2000
- type: User
qps: 10
burst: 50
此准入控制器默认被禁用。
ExtendedResourceToleration
类别:变更。
此插件有助于创建带有扩展资源的专用节点。 如果运维人员想要创建带有扩展资源(如 GPU、FPGA 等)的专用节点,他们应该以扩展资源名称作为键名, 为节点设置污点。 如果启用了此准入控制器,会将此类污点的容忍度自动添加到请求扩展资源的 Pod 中, 用户不必再手动添加这些容忍度。
此准入控制器默认被禁用。
ImagePolicyWebhook
类别:验证。
ImagePolicyWebhook 准入控制器允许使用后端 Webhook 做出准入决策。
此准入控制器默认被禁用。
LimitPodHardAntiAffinityTopology
类别:验证。
此准入控制器拒绝定义了 AntiAffinity 拓扑键的任何 Pod (requiredDuringSchedulingRequiredDuringExecution 中的 kubernetes.io/hostname 除外)。
此准入控制器默认被禁用。
LimitRanger
类别:变更和验证。
此准入控制器会监测传入的请求,并确保请求不会违反 Namespace 中 LimitRange 对象所设置的任何约束。 如果你在 Kubernetes 部署中使用了 LimitRange 对象,则必须使用此准入控制器来执行这些约束。 LimitRanger 还可以用于将默认资源请求应用到没有设定资源约束的 Pod; 当前,默认的 LimitRanger 对 default 名字空间中的所有 Pod 都设置 0.1 CPU 的需求。
MutatingAdmissionWebhook
类别:变更。
此准入控制器调用任何与请求匹配的变更(Mutating) Webhook。匹配的 Webhook 将被顺序调用。 每一个 Webhook 都可以自由修改对象。
MutatingAdmissionWebhook,顾名思义,仅在变更阶段运行。
如果由此准入控制器调用的 Webhook 有副作用(如:减少配额), 则它 必须 具有协调系统,因为不能保证后续的 Webhook 和验证准入控制器都会允许完成请求。
如果你禁用了 MutatingAdmissionWebhook,那么还必须使用 --runtime-config 标志禁止 admissionregistration.k8s.io/v1 组/版本中的 MutatingWebhookConfiguration, 二者都是默认启用的。
NamespaceAutoProvision
类别:变更。
此准入控制器会检查针对名字空间域资源的所有传入请求,并检查所引用的名字空间是否确实存在。 如果找不到所引用的名字空间,控制器将创建一个名字空间。 此准入控制器对于不想要求名字空间必须先创建后使用的集群部署很有用。
NamespaceExists
类别:验证。
此准入控制器检查针对名字空间作用域的资源(除 Namespace 自身)的所有请求。 如果请求引用的名字空间不存在,则拒绝该请求。
NamespaceLifecycle
类别:验证。
该准入控制器禁止在一个正在被终止的 Namespace 中创建新对象,并确保针对不存在的 Namespace 的请求被拒绝。该准入控制器还会禁止删除三个系统保留的名字空间,即 default、 kube-system 和 kube-public。
Namespace 的删除操作会触发一系列删除该名字空间中所有对象(Pod、Service 等)的操作。 为了确保这个过程的完整性,我们强烈建议启用这个准入控制器。
NodeRestriction
类别:验证。
该准入控制器限制了某 kubelet 可以修改的 Node 和 Pod 对象。 为了受到这个准入控制器的限制,kubelet 必须使用在 system:nodes 组中的凭证, 并使用 system:node:<nodeName>
形式的用户名。 这样,kubelet 只可修改自己的 Node API 对象,只能修改绑定到自身节点的 Pod 对象。
不允许 kubelet 更新或删除 Node API 对象的污点。
NodeRestriction 准入插件可防止 kubelet 删除其 Node API 对象, 并对前缀为 kubernetes.io/ 或 k8s.io/ 的标签的修改对 kubelet 作如下限制:
- 禁止 kubelet 添加、删除或更新前缀为 node-restriction.kubernetes.io/ 的标签。 这类前缀的标签时保留给管理员的,用以为 Node 对象设置标签以隔离工作负载,而不允许 kubelet 修改带有该前缀的标签。
- 允许 kubelet 添加、删除、更新以下标签:
kubernetes.io/hostname
kubernetes.io/arch
kubernetes.io/os
beta.kubernetes.io/instance-type
node.kubernetes.io/instance-type
failure-domain.beta.kubernetes.io/region(已弃用)
failure-domain.beta.kubernetes.io/zone(已弃用)
topology.kubernetes.io/region
topology.kubernetes.io/zone
kubelet.kubernetes.io/ 为前缀的标签
node.kubernetes.io/ 为前缀的标签
以 kubernetes.io 或 k8s.io 为前缀的所有其他标签都限制 kubelet 使用,并且将来可能会被 NodeRestriction 准入插件允许或禁止。
将来的版本可能会增加其他限制,以确保 kubelet 具有正常运行所需的最小权限集。
OwnerReferencesPermissionEnforcement
类别:验证。
此准入控制器保护对对象的 metadata.ownerReferences 的访问,以便只有对该对象具有 delete 权限的用户才能对其进行更改。 该准入控制器还保护对 metadata.ownerReferences[x].blockOwnerDeletion 对象的访问, 以便只有对所引用的 属主(owner) 的 finalizers 子资源具有 update 权限的用户才能对其进行更改。
PersistentVolumeClaimResize
特性状态: Kubernetes v1.24 [stable]
类别:验证。
此准入控制器检查传入的 PersistentVolumeClaim 调整大小请求,对其执行额外的验证检查操作。
建议启用 PersistentVolumeClaimResize 准入控制器。除非 PVC 的 StorageClass 明确地将 allowVolumeExpansion 设置为 true 来显式启用调整大小。 否则,默认情况下该准入控制器会阻止所有对 PVC 大小的调整。
PodNodeSelector
特性状态: Kubernetes v1.5 [alpha]
类别:验证。
此准入控制器通过读取名字空间注解和全局配置,来为名字空间中可以使用的节点选择器设置默认值并实施限制。
此准入控制器默认被禁用。
PodSecurity
特性状态: Kubernetes v1.25 [stable]
类别:验证。
PodSecurity 准入控制器在新 Pod 被准入之前对其进行检查, 根据请求的安全上下文和 Pod 所在名字空间允许的 Pod 安全性标准的限制来确定新 Pod 是否应该被准入。
PodSecurity 取代了一个名为 PodSecurityPolicy 的旧准入控制器。
PodTolerationRestriction
特性状态: Kubernetes v1.7 [alpha]
类别:变更和验证。
准入控制器 PodTolerationRestriction 检查 Pod 的容忍度与其名字空间的容忍度之间是否存在冲突。 如果存在冲突,则拒绝 Pod 请求。 控制器接下来会将名字空间的容忍度合并到 Pod 的容忍度中, 根据名字空间的容忍度白名单检查所得到的容忍度结果。 如果检查成功,则将接受 Pod 请求,否则拒绝该请求。
如果 Pod 的名字空间没有任何关联的默认容忍度或容忍度白名单, 则使用集群级别的默认容忍度或容忍度白名单(如果有的话)。
名字空间的容忍度通过注解键 scheduler.alpha.kubernetes.io/defaultTolerations 来设置。可接受的容忍度可以通过 scheduler.alpha.kubernetes.io/tolerationsWhitelist 注解键来添加。
优先级
类别:变更和验证。
优先级准入控制器使用 priorityClassName 字段并用整型值填充优先级。 如果找不到优先级,则拒绝 Pod。
ResourceQuota
类别:验证。
此准入控制器会监测传入的请求,并确保它不违反任何一个 Namespace 中的 ResourceQuota 对象中列举的约束。如果你在 Kubernetes 部署中使用了 ResourceQuota, 则必须使用这个准入控制器来强制执行配额限制。
RuntimeClass
类别:变更和验证。
如果你所定义的 RuntimeClass 包含 Pod 开销, 这个准入控制器会检查新的 Pod。 被启用后,此准入控制器会拒绝所有已经设置了 overhead 字段的 Pod 创建请求。 对于配置了 RuntimeClass 并在其 .spec 中选定 RuntimeClass 的 Pod, 此准入控制器会根据相应 RuntimeClass 中定义的值为 Pod 设置 .spec.overhead。
ServiceAccount
类别:变更和验证。
此准入控制器实现了 ServiceAccount 的自动化。强烈推荐为 Kubernetes 项目启用此准入控制器。 如果你打算使用 Kubernetes 的 ServiceAccount 对象,你应启用这个准入控制器。
关于 kubernetes.io/enforce-mountable-secrets 注解:尽管注解的名称表明它只涉及 Secret 的挂载, 但其执行范围也扩展到 Pod 上下文中 Secret 的其他使用方式。 因此,确保所有引用的 Secret 在 ServiceAccount 中被正确指定是至关重要的。
StorageObjectInUseProtection
类别:变更。
StorageObjectInUseProtection 插件将 kubernetes.io/pvc-protection 或 kubernetes.io/pv-protection 终结器(finalizers)添加到新创建的持久卷申领(PVC) 或持久卷(PV)中。如果用户尝试删除 PVC/PV,除非 PVC/PV 的保护控制器移除终结器, 否则 PVC/PV 不会被删除。有关更多详细信息, 请参考保护使用中的存储对象。
TaintNodesByCondition
类别:变更。
该准入控制器为新创建的节点添加 NotReady 和 NoSchedule 污点。 这些污点能够避免一些竞态条件的发生,而这类竞态条件可能导致 Pod 在更新节点污点以准确反映其所报告状况之前,就被调度到新节点上。
ValidatingAdmissionPolicy
类别:验证。
此准入控制器针对传入的匹配请求实现 CEL 校验。当 validatingadmissionpolicy 和 admissionregistration.k8s.io/v1alpha1 特性门控组/版本被启用时, 此特性被启用。如果任意 ValidatingAdmissionPolicy 失败,则请求失败。
ValidatingAdmissionWebhook
类别:验证。
此准入控制器调用与请求匹配的所有验证性 Webhook。 匹配的 Webhook 将被并行调用。如果其中任何一个拒绝请求,则整个请求将失败。 该准入控制器仅在验证(Validating)阶段运行;与 MutatingAdmissionWebhook 准入控制器所调用的 Webhook 相反,它调用的 Webhook 不可以变更对象。
如果以此方式调用的 Webhook 有其它副作用(如:减少配额),则它必须具有协调机制。 这是因为无法保证后续的 Webhook 或其他验证性准入控制器都允许请求完成。
如果你禁用了 ValidatingAdmissionWebhook,还必须通过 --runtime-config 标志来禁用 admissionregistration.k8s.io/v1 组/版本中的 ValidatingWebhookConfiguration 对象。
推荐使用的准入控制器默认情况下都处于启用状态
https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-apiserver/#options