大家好,我是张晋涛。在我之前发布的文章 《云原生时代下的容器镜像安全》(系列)中,我提到过 Kubernetes 集群的核心组件 -- kube-apiserver,它允许来自终端用户或集群的各组件与之进行通信(例如,查询、创建、修改或删除 Kubernetes 资源)。
本篇我们将聚焦于 kube-apiserver 请求处理过程中一个很重要的部分 -- 准入控制器(Admission Controller)
K8s 的准入控制器是什么
K8s 中的请求处理流程
在聊 K8s 准入控制器是什么之前,让我们先来回顾一下 Kubernetes API 的处理具体请求的过程。
图 1 ,Kubernetes API 处理请求的过程 (从 API Handler 到 etcd 持久化的过程)
如上图所示,每个 API 的请求从开始被 kube-apiserver 接收到最终持久化到 ETCD 的过程,即为 Kubernetes API 的请求处理流程。
它主要包含了以下几个部分:
API Handler -- 主要负责提供服务,接收请求。
对于其内部实现而言,请求会先到 FullHandlerChain
(它是由 DefaultBuildHandlerChain
构建出来的)是一个 director
对象
typedirectorstruct{namestringgoRestfulContainer*restful.ContainernonGoRestfulMux*mux.PathRecorderMux}
director
根据配置进行初始化,如果 goRestfulContainer
的 WebServices 的 RootPath 是 /apis
,或者请求前缀与 RootPath 匹配,则进入 Restful 处理链路。
Authentication -- 认证的流程。
在TLS 连接建立后,会进行认证处理,如果请求认证失败,会拒绝该请求并返回 401 错误码;如果认证成功,将进行到鉴权的部分。目前支持的客户端认证方式有很多,例如:x509 客户端证书、Bearer Token、基于用户名密码的认证、OpenID 认证等。由于这些内容不是本篇的重点我们暂且跳过,感兴趣的小伙伴可以在评论区留言讨论。
Authorization -- 鉴权的流程。
对于 Kubernetes 而言,支持多种的鉴权模式,例如,ABAC 模式,RBAC 模式和 Webhook 模式等。我们在创建集群时,可以直接为 kube-apiserver 传递参数进行配置,这里也不赘述了。
Mutating Admission -- 指执行可用于变更操作的准入控制器,下文中会详细介绍。
Object Schema Validation -- 对资源对象的 schema 校验。
Validating Admission -- 指执行可用于验证操作的准入控制器,下文中会详细介绍。
ETCD -- ETCD 实现资源的持久化存储。
上面便是一个请求的处理流程,其中 Mutating Admission 和 Validating Admission 便是我们今天的主角。我们来详细的看一看。
什么是准入控制器(Admission Controller)
准入控制器是指在请求通过认证和授权之后,可用于对其进行变更操作或验证操作的一些代码或功能。
准入控制的过程分为两个阶段:
第一阶段,运行变更准入控制器(Mutating Admission)。它可以修改被它接受的对象,这就引出了它的另一个作用,将相关资源作为请求处理的一部分进行变更;
第二阶段,运行验证准入控制器(Validating Admission)。它只能进行验证,不能进行任何资源数据的修改操作;
需要注意的是,某些控制器可以既是变更准入控制器又是验证准入控制器。如果任一个阶段的准入控制器拒绝了该请求,则整个请求将立即被拒绝,并向终端用户返回错误。
为什么需要准入控制器 Admission Controller
我们主要从两个角度来理解为什么我们需要准入控制器:
从安全的角度
我们需要明确在 Kubernetes 集群中部署的镜像来源是否可信,以免遭受攻击;
一般情况下,在 Pod 内尽量不使用 root 用户,或者尽量不开启特权容器等;
从治理的角度
比如通过 label 对业务/服务进行区分,那么可以通过 admission controller 校验服务是否已经有对应的 label 存在之类的;
比如添加资源配额限制 ,以免出现资源超卖之类的情况;
准入控制器
考虑到这些需求比较有用 & 确实也比较需要,所以 Kubernetes 目前已经实现了很多内置的 Admission Controller 。 可以参考官方文档来获取详细列表: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#what-does-each-admission-controller-do
这些内置的 Admission Controller 都是以插件的方式与 kube-apiserver 构建到一起的,你可以对其进行启用和关停。比如用如下参数进行控制:
➜bin./kube-apiserver--help|grepadmission-plugins--admission-controlstringsAdmissionisdividedintotwophases.Inthefirstphase,onlymutatingadmissionpluginsrun.Inthesecondphase,onlyvalidatingadmissionpluginsrun.Thenamesinthebelowlistmayrepresentavalidatingplugin,amutatingplugin,orboth.Theorderofpluginsinwhichtheyarepassedtothisflagdoesnotmatter.Comma-delimitedlistof:AlwaysAdmit,AlwaysDeny,AlwaysPullImages,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DefaultIngressClass,DefaultStorageClass,DefaultTolerationSeconds,DenyServiceExternalIPs,EventRateLimit,ExtendedResourceToleration,ImagePolicyWebhook,LimitPodHardAntiAffinityTopology,LimitRanger,MutatingAdmissionWebhook,NamespaceAutoProvision,NamespaceExists,NamespaceLifecycle,NodeRestriction,OwnerReferencesPermissionEnforcement,PersistentVolumeClaimResize,PersistentVolumeLabel,PodNodeSelector,PodSecurity,PodSecurityPolicy,PodTolerationRestriction,Priority,ResourceQuota,RuntimeClass,SecurityContextDeny,ServiceAccount,StorageObjectInUseProtection,TaintNodesByCondition,ValidatingAdmissionWebhook.(DEPRECATED:Use--enable-admission-pluginsor--disable-admission-pluginsinstead.Willberemovedinafutureversion.)--disable-admission-pluginsstringsadmissionpluginsthatshouldbedisabledalthoughtheyareinthedefaultenabledpluginslist(NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,PodSecurity,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DefaultIngressClass,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota).Comma-delimitedlistofadmissionplugins:AlwaysAdmit,AlwaysDeny,AlwaysPullImages,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DefaultIngressClass,DefaultStorageClass,DefaultTolerationSeconds,DenyServiceExternalIPs,EventRateLimit,ExtendedResourceToleration,ImagePolicyWebhook,LimitPodHardAntiAffinityTopology,LimitRanger,MutatingAdmissionWebhook,NamespaceAutoProvision,NamespaceExists,NamespaceLifecycle,NodeRestriction,OwnerReferencesPermissionEnforcement,PersistentVolumeClaimResize,PersistentVolumeLabel,PodNodeSelector,PodSecurity,PodSecurityPolicy,PodTolerationRestriction,Priority,ResourceQuota,RuntimeClass,SecurityContextDeny,ServiceAccount,StorageObjectInUseProtection,TaintNodesByCondition,ValidatingAdmissionWebhook.Theorderofpluginsinthisflagdoesnotmatter.--enable-admission-pluginsstringsadmissionpluginsthatshouldbeenabledinadditiontodefaultenabledones(NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,PodSecurity,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DefaultIngressClass,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota).Comma-delimitedlistofadmissionplugins:AlwaysAdmit,AlwaysDeny,AlwaysPullImages,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DefaultIngressClass,DefaultStorageClass,DefaultTolerationSeconds,DenyServiceExternalIPs,EventRateLimit,ExtendedResourceToleration,ImagePolicyWebhook,LimitPodHardAntiAffinityTopology,LimitRanger,MutatingAdmissionWebhook,NamespaceAutoProvision,NamespaceExists,NamespaceLifecycle,NodeRestriction,OwnerReferencesPermissionEnforcement,PersistentVolumeClaimResize,PersistentVolumeLabel,PodNodeSelector,PodSecurity,PodSecurityPolicy,PodTolerationRestriction,Priority,ResourceQuota,RuntimeClass,SecurityContextDeny,ServiceAccount,StorageObjectInUseProtection,TaintNodesByCondition,ValidatingAdmissionWebhook.Theorderofpluginsinthisflagdoesnotmatter.
这么多的准入控制器中有两个比较特别,分别是 MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
。它们并没有真正执行相应的策略,而是为 kube-apiserver 提供了一种可扩展的方式, 用户可以通过配置 MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
来使用自构建的服务, 而且这种方式是无需对 kube-spiserver 执行编译或者重启,完全是动态的,非常的方便。
我们来具体看一下。
动态准入控制器
利用上文中提到的 MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
进行运行时配置的,以 Webhook 形式调用的 Admission Controller 即为动态准入控制器。
它是一种用于接收准入请求并对其进行处理的 HTTP 回调机制,就是一个 Web 服务。当前的两种类型的准入 webhook:
validating admission webhook
mutating admission webhook
mutating admission webhook 会优先被调用,这个过程中可以对资源进行修改。
如果我们需要确保对象的最终状态以执行某些操作应该考虑使用 validating admission webhook ,因为到达这个阶段的请求不会再被修改。
使用条件
确保 Kubernetes 集群版本至少为 v1.16(以便使用 admissionregistration.k8s.io/v1
API) 或者 v1.9 (以便使用 admissionregistration.k8s.io/v1beta1
API);
确保已经启用 MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
准入控制器;
确保启用了 admissionregistration.k8s.io/v1beta1
或者 admissionregistration.k8s.io/v1
API;
Admission Webhook 是什么
它其实就是一个普通的 HTTP Server,需要处理的就是 AdmissionReview
类型的资源,我们来看个示例,比如说要对 Ingress
资源进行准入校验:
func(ia*IngressAdmission)HandleAdmission(objruntime.Object)(runtime.Object,error){review,isV1:=obj.(*admissionv1.AdmissionReview)if!isV1{returnnil,fmt.Errorf("requestisnotoftypeAdmissionReviewv1")}if!apiequality.Semantic.DeepEqual(review.Request.Kind,ingressResource){returnnil,fmt.Errorf("rejectingadmissionreviewbecausetherequestdoesnotcontainanIngressresourcebut%swithname%sinnamespace%s",review.Request.Kind.String(),review.Request.Name,review.Request.Namespace)}status:=&admissionv1.AdmissionResponse{}status.UID=review.Request.UIDingress:=networking.Ingress{}codec:=json.NewSerializerWithOptions(json.DefaultMetaFactory,scheme,scheme,json.SerializerOptions{Pretty:true,})codec.Decode(review.Request.Object.Raw,nil,nil)_,_,err:=codec.Decode(review.Request.Object.Raw,nil,&ingress)iferr!=nil{klog.ErrorS(err,"failedtodecodeingress")status.Allowed=falsestatus.Result=&metav1.Status{Status:metav1.StatusFailure,Code:http.StatusBadRequest,Reason:metav1.StatusReasonBadRequest,Message:err.Error(),}review.Response=statusreturnreview,nil}iferr:=ia.Checker.CheckIngress(&ingress);err!=nil{klog.ErrorS(err,"invalidingressconfiguration","ingress",fmt.Sprintf("%v/%v",review.Request.Name,review.Request.Namespace))status.Allowed=falsestatus.Result=&metav1.Status{Status:metav1.StatusFailure,Code:http.StatusBadRequest,Reason:metav1.StatusReasonBadRequest,Message:err.Error(),}review.Response=statusreturnreview,nil}klog.InfoS("successfullyvalidatedconfiguration,accepting","ingress",fmt.Sprintf("%v/%v",review.Request.Name,review.Request.Namespace))status.Allowed=truereview.Response=statusreturnreview,nil}
核心处理逻辑其实就是处理请求 Webhook 时候发送的 AdmissionReview
,它会包含着我们待校验的资源对象。然后我们就按照实际的需要对资源对象进行校验或者修改了。
这里需要注意几个点:
Mutating Webhook 的处理是串行的,而 Validating Webhook 是并行处理的;
Mutating Webhook 虽然处理是串行的,但是并不保证顺序;
注意对 Mutating Webhook 的处理做到幂等性,以免结果不符合预期;
请求处理时,注意要处理资源对象的所有 API 版本;
如何部署 Admission Webhook
apiVersion:admissionregistration.k8s.io/v1kind:ValidatingWebhookConfigurationmetadata:labels:app.kubernetes.io/name:ingress-nginxname:ingress-nginx-admissionwebhooks:-name:validate.nginx.ingress.kubernetes.iomatchPolicy:Equivalentrules:-apiGroups:-networking.k8s.ioapiVersions:-v1operations:-CREATE-UPDATEresources:-ingressesfailurePolicy:FailsideEffects:NoneadmissionReviewVersions:-v1clientConfig:service:namespace:ingress-nginxname:ingress-nginx-controller-admissionpath:/networking/v1/ingresses
在 webhooks
中配置 webhook 的具体连接信息,以及触发规则。rules
中可指定对哪些资源的具体行为生效。
总结
本篇主要介绍了 Kubernetes 中的 Admission Controller ,默认情况下有一些已经以插件形式与 kube-apiserver 编译到了一起,另外我们也可以通过自己编写动态准入控制器来完成相关的需求。
当然,目前在 K8s 生态中已经有很多现成的工具可以帮我们完成对应的这些事情了,很多情况下不需要再自行开发对应的服务了。后续我来为大家分享当前一些比较主流的,可用于进行 Mutating 和 Validating 准入控制的工具, 欢迎关注。
欢迎订阅我的文章公众号【MoeLove】