6.3 Admission Control

突破了之前所说的认证和鉴权两道关卡之后,客户端的调用请求就能够得到API Server的真正响应了吗?答案是:不能!这个请求还要通过Admission Control(准入控制)所控制的一个准入控制链的层层考验,才能获得成功的响应。Kubernetes官方标准的“关卡”有30多个,还允许用户自定义扩展。

Admission Control配备了一个准入控制器的插件列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查不通过,API Server就会拒绝此调用请求。此外,准入控制器插件能够修改请求参数以完成一些自动化任务,比如ServiceAccount这个控制器插件。当前可配置的准入控制器插件如下。

◎ AlwaysAdmit:已弃用,允许所有请求。

◎ AlwaysPullImages:在启动容器之前总是尝试重新下载镜像。这对于多租户共享一个集群的场景非常有用,系统在启动容器之前可以保证总是使用租户的密钥去下载镜像。如果不设置这个控制器,则在Node上下载的镜像的安全性将被削弱,只要知道该镜像的名称,任何人便都可以使用它们了。

◎ AlwaysDeny:已弃用,禁止所有请求,用于测试。

◎ DefaultStorageClass:会关注PersistentVolumeClaim资源对象的创建,如果其中没有包含任何针对特定Storage class的请求,则为其指派指定的Storage class。在这种情况下,用户无须在PVC中设置任何特定的Storage class就能完成PVC的创建了。如果没有设置默认的Storage class,该控制器就不会进行任何操作;如果设置了超过一个的默认Storage class,该控制器就会拒绝所有PVC对象的创建申请,并返回错误信息。管理员必须检查StorageClass对象的配置,确保只有一个默认值。该控制器仅关注PVC的创建过程,对更新过程无效。

◎ DefaultTolerationSeconds:针对没有设置容忍node.kubernetes.io/not-ready:NoExecute或者node.alpha.kubernetes.io/unreachable:NoExecute的Pod,设置5min的默认容忍时间。

◎ DenyExecOnPrivileged:已弃用,拦截所有想在Privileged Container上运行命令的请求。如果集群支持Privileged Container,又希望限制用户在这些Privileged Container上运行命令,那么强烈推荐使用它。其功能已被合并到DenyEscalatingExec中。

◎ DenyEscalatingExec:拦截所有exec和attach到具有特权的Pod上的请求。如果集群支持运行有escalated privilege权限的容器,又希望限制用户在这些容器内运行命令,那么强烈推荐使用它。

◎ EventReateLimit:Alpha版本,用于应对事件密集情况下对API Server造成的洪水攻击。

◎ ExtendedResourceToleration:如果运维人员要创建带有特定资源(例如GPU、FPGA等)的独立节点,则可能会对节点进行Taint处理来进行特别配置。该控制器能够自动为申请这些特别资源的Pod加入Toleration定义,无须人工干预。

◎ ImagePolicyWebhook:Alpha版本,该插件将允许后端的一个Webhook程序来完成admission controller的功能。ImagePolicyWebhook需要使用一个配置文件(通过kube-apiserver的启动参数--admission-control-config-file设置)定义后端Webhook的参数。

◎ Initializers:Alpha版本,用于为动态准入控制提供支持,通过修改待创建资源的元数据来完成对该资源的修改。

◎ LimitPodHardAntiAffinityTopology:该插件启用了Pod的反亲和性调度策略设置,在设置亲和性策略参数requiredDuringSchedulingRequiredDuringExecution时要求将topologyKey的值设置为“kubernetes.io/hostname”,否则Pod会被拒绝创建。

◎ LimitRanger:该插件会监控进入的请求,确保请求的内容符合在Namespace中定义的LimitRange对象里的资源限制。如果要在Kubernetes集群中使用LimitRange对象,则必须启用该插件才能实施这一限制。LimitRanger还能用于为没有设置资源请求的Pod自动设置默认的资源请求,会为default命名空间中的所有Pod都设置0.1CPU的资源请求。

◎ MutatingAdmissionWebhook:Beta版本,会变更符合要求的请求的内容,Webhook以串行的方式顺序执行。

◎ NamespaceAutoProvision:该插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会自动创建命名空间。

◎ NamespaceExists:该插件会检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就会拒绝这一创建过程。

◎ NamespaceLifecycle:如果尝试在一个不存在的命名空间中创建资源对象,则该创建请求将被拒绝。删除一个命名空间时,系统将删除该命名空间中的所有对象,包括Pod、Service等,并阻止删除default、kube-system和kube-public这三个命名空间。

◎ NodeRestriction:该插件会限制kubelet对Node和Pod的修改。为了实现这一限制,kubelet必须使用system:nodes组中用户名为system:node:<nodeName>的Token来运行。符合条件的kubelet只能修改自己的Node对象,也只能修改分配到各自Node上的Pod对象。在Kubernetes 1.11以后的版本中,kubelet无法修改或者更新自身Node的taint属性。在Kubernetes 1.13以后,该插件还会阻止kubelet删除自己的Node资源,并限制对有kubernetes.io/或k8s.io/前缀的标签的修改。

◎ OwnerReferencesPermissionEnforcement:在该插件启用后,一个用户要想修改对象的metadata.ownerReferences,就必须具备delete权限。该插件还会保护对象的metadata.ownerReferences[x].blockOwnerDeletion字段,用户只有在对finalizers子资源拥有update权限时才能进行修改。

◎ PersistentVolumeLabel:已弃用。该插件自动根据云供应商(例如GCE或AWS)的定义,为PersistentVolume对象加入region或zone标签,以此来保障PersistentVolume和Pod同处一个区域。如果插件不为PV自动设置标签,则需要用户手动保证Pod和其加载卷的相对位置。该插件正在被Cloud controller manager替换,从Kubernetes 1.11版本开始默认被禁止。

◎ PodNodeSelector:该插件会读取命名空间的annotation字段及全局配置,来对一个命名空间中对象的节点选择器设置默认值或限制其取值。

◎ PersistentVolumeClaimResize:该插件实现了对PersistentVolumeClaim发起的resize请求的额外校验。

◎ PodPreset:该插件会使用PodSelector选择Pod,为符合条件的Pod进行注入。

◎ PodSecurityPolicy:在创建或修改Pod时决定是否根据Pod的security context和可用的PodSecurityPolicy对Pod的安全策略进行控制。

◎ PodTolerationRestriction:该插件首先会在Pod和其命名空间的Toleration中进行冲突检测,如果其中存在冲突,则拒绝该Pod的创建。它会把命名空间和Pod的Toleration合并,然后将合并的结果与命名空间中的白名单进行比较,如果合并的结果不在白名单内,则拒绝创建。如果不存在命名空间级别的默认Toleration和白名单,则会采用集群级别的默认Toleration和白名单。

◎ Priority:该插件使用priorityClassName字段来确定优先级,如果没有找到对应的Priority Class,该Pod就会被拒绝。

◎ ResourceQuota:用于资源配额管理,作用于命名空间。该插件拦截所有请求,以确保命名空间中的资源配额使用不会超标。推荐在Admission Control参数列表中将该插件排在最后一个,以免可能被其他插件拒绝的Pod被过早分配资源。在10.4节将详细介绍ResourceQuota的原理和用法。

◎ SecurityContextDeny:该插件将Pod中定义的SecurityContext选项全部失效。SecurityContext在Container中定义了操作系统级别的安全设定(uid、gid、capabilities、SELinux等)。在未设置PodSecurityPolicy的集群中建议启用该插件,以禁用容器设置的非安全访问权限。

◎ ServiceAccount:该插件让ServiceAccount实现了自动化,如果想使用ServiceAccount对象,那么强烈推荐使用它。

◎ StorageObjectInUseProtection:该插件会在新创建的PVC或PV中加入kubernetes.io/pvc-protection或kubernetes.io/pv-protection的finalizer。如果想删除PVC或者PV,则直到所有finalizer的工作都完成,删除动作才会执行。

◎ ValidatingAdmissionWebhook:在Kubernetes 1.8版本中为Alpha版本,在Kubernetes 1.9版本中为Beta版本。该插件会针对符合其选择要求的请求调用校验Webhook。目标Webhook会以并行方式运行;如果其中任何一个Webhook拒绝了该请求,该请求就会失败。

在API Server上设置参数即可定制我们需要的准入控制链,如果启用了多种准入控制选项,则建议这样设置:在Kubernetes 1.9及之前的版本中使用的参数是--admission-control,其中的内容是顺序相关的;在Kubernetes 1.10及之后的版本中使用的参数是--enable-admission-plugins,并且与顺序无关。

对Kubernetes 1.10及以上版本设置如下:

除了静态编译的Admission插件,也可以通过Webhook方式对接外部的Admission Webhook服务,实现与Admission插件一样的功能。但Webhook方式更加灵活,能够在API Server运行时修改和配置动态更新控制策略。Admission Webhook的实现方式是常见的HTTP回调,该回调方法首先接收一个Admission请求参数,然后对此参数做出修改或者准入判断的逻辑,前一种类型的Webhook被称为Mutating Admission Webhook,后一种被称为Validating Admission Webhook。如果需要修改Admission请求参数,则可以用Mutating Admission Webhook进行修改,并把它配置到准入控制链的靠前位置。

使用Admission Webhook实现准入控制的方式还有一个明显优势,即我们可以灵活指定访问哪些版本的资源对象需要通过Admission Webhook进行判断。比如下面这段配置表明 my-webhook.example.com这个Webhook只针对apps/v1及apps/v1beta1版本下的Deployments与Replicasets资源对象的CREATE与UPDATE操作进行控制。

不过,相对于Admission Control插件来说,使用Admission Webhook要复杂得多,除了需要开发一个Admission Webhook Server实现HTTP回调的逻辑,还需要创建一个对应的ValidatingWebhookConfiguration配置文件,如果Admission Webhook需要与API Server进行认证,则还需要创建对应的AdmissionConfiguration配置文件。