背景
在 k8s中各組件和kube apiserver通信時的認證和鑒權 中提到"NodeRestriction準入插件",實際上它是一個"準入控制器"。
"準入控制器"是一個重要的概念,在istio、apisix、某些安全產(chǎn)品中都有用到。
本文簡要記錄一下以下內(nèi)容:
- "準入控制器"是什么
- 怎么開啟"準入控制器"
- 從源碼淺析"準入控制器"
本文使用的k8s集群是用kubekey搭建,命令是./kk create cluster --with-kubernetes v1.21.5 --with-kubesphere v3.2.1
分析
"準入控制器"是什么?
它有點類似"插件",為apiserver提供了很好的"可擴展性"。
請求apiserver時,通過認證、鑒權后、持久化("api對象"保存到etcd)前,會經(jīng)過"準入控制器",讓它可以做"變更和驗證"。
"變更"可以修改"api對象",比如istio用來實現(xiàn)pod注入。"驗證"可以用來校驗"api對象",比如 校驗當前集群是否有足夠多的資源滿足"api對象"、校驗當前提交的"pod對象"是否合法。
怎么開啟"準入控制器"?
root@ip-172-31-14-33:~/kubernetes-1.21.5/_output/bin# ./kube-apiserver --help |grep admission-plugins
...
--enable-admission-plugins strings admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.
根據(jù)命令行幫助可以知道,默認會開啟17個"準入控制器"。
>>> a="NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, Persis
tentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota"
>>> len(a.split(","))
17
也可以用--enable-admission-plugins開啟額外的"準入控制器"。
這些"準入控制器"介紹可以查看 使用準入控制器 文檔。在我的實驗環(huán)境中,可以看到額外開啟了NodeRestriction準入控制器,它實現(xiàn)了apiserver對kubelet請求的權限控制。
root@ip-172-31-14-33:~# ps aux|grep kube-apis
root 9567 4.8 7.8 1381988 613048 ? Ssl 03:49 17:48 kube-apiserver ... --enable-admission-plugins=NodeRestriction ...
從源碼淺析"準入控制器"
我們可以通過"斷點調(diào)試"結合源碼分析,驗證前面說的兩個結論:
- 請求先經(jīng)過認證、鑒權,然后經(jīng)過"準入控制器"
- 默認開啟17個"準入控制器";加上NodeRestriction就是18個
"認證、日志審計、鑒權"在apiserver中都是以filter的形式存在,而"準入控制器"有點像包裝了一層servlet。
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler := filterlatency.TrackCompleted(apiHandler)
handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer) // 鑒權
...
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc) // 日志審計
...
handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences) // 認證
...
return handler
}
finishRequest函數(shù)中會開goroutine調(diào)用"準入控制器"

可以看到 admissionHandler切片長度是18,17個"默認開啟的準入控制器"加上NodeRestriction

部分控制器代碼在plugin/pkg/admission目錄中,會實現(xiàn)Admit接口

總結
- 請求先經(jīng)過認證、鑒權,然后經(jīng)過"準入控制器"
- 默認開啟17個"準入控制器";NodeRestriction不是默認開啟的
默認開啟的"準入控制器"中有兩個很特殊的,ValidatingAdmissionWebhook和MutatingAdmissionWebhook。這兩個控制器讓apiserver有了更多的可擴展性,實現(xiàn)了"動態(tài)準入控制"。
文章首發(fā)于火線Zone:https://zone.huoxian.cn/d/1255-k8s
作者:leveryd
火線安全平臺:https://www.huoxian.cn/
火線Zone社區(qū):https://zone.huoxian.cn/?sort=newest