實現(xiàn)一個容器鏡像白名單的 K8S 準(zhǔn)入控制器 | 視頻文字稿

前面我們已經(jīng)介紹了準(zhǔn)入控制器的含義,了解可以通過有兩個特殊的“動態(tài)”控制器 -ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 來讓開發(fā)者自行實現(xiàn)自己的準(zhǔn)入邏輯。這兩個控制器沒有實現(xiàn)任何固定邏輯,相反,它們使我們能夠在每次在集群中創(chuàng)建、更新或刪除Kubernetes 資源時通過 webhooks 靈活地實現(xiàn)和執(zhí)行自定義邏輯。
示例
接下來我們將構(gòu)建一個準(zhǔn)入控制器示例,只允許使用來自白名單鏡像倉庫的資源創(chuàng)建 Pod,拒絕使用不受信任的鏡像倉庫中進(jìn)行拉取鏡像。
比如我們這里只允許使用來自 docker.io 或者 gcr.io 鏡像倉庫的鏡像創(chuàng)建 Pod,其他不受信任的鏡像創(chuàng)建的 Pod 將會被拒絕。
要實現(xiàn)這個需求,我們就需要構(gòu)建一個 ValidatingAdmissionWebhook,并將其注冊到 APIServer。在編寫這個 Webhook 之前我們就需要先鏈接通過注冊的 Webhook 從 APIServer 接收到的請求的結(jié)構(gòu),以及我們對 APIServer 的響應(yīng)結(jié)構(gòu)。
APIServer 實際上使用的是一個 AdmissionReview 類型的對象來向我們自定義的 Webhook 發(fā)送請求和接收響應(yīng)。
對于每個請求,在 AdmissionReview 結(jié)構(gòu)體內(nèi)部都有一個 AdmissionRequest 類型的屬性,該屬性中封裝了發(fā)送到 APIServer 的原始請求數(shù)據(jù),我們主要關(guān)心的就是該對象內(nèi)部包含的正在創(chuàng)建/更新或刪除的 Kubernetes 對象(比如 Pod、Deployment 等) JSON payload 數(shù)據(jù)。下面是用于驗證準(zhǔn)入控制器的 AdmissionReview 請求對象示例:
{
??"apiVersion":?"admission.k8s.io/v1",
??"kind":?"AdmissionReview",
??"request":?{
????#?Random?uid?uniquely?identifying?this?admission?call
????"uid":?,
????...
????"object":?{"apiVersion":"v1","kind":"Pod",...},
????...
????}
}
對于驗證準(zhǔn)入控制器,我們的應(yīng)用程序必須接收一個 AdmissionReview 對象,對其進(jìn)行處理來決定是否允許/不允許該請求,并通過在 AdmissionReview 結(jié)構(gòu)中填充一個類型為 AdmissionResponse 的 response 屬性來返回我們的驗證結(jié)果。在 response 中,我們使用一個名為 allowed 的布爾類型來表示是否允許/不允許,我們還可以選擇包含一個 HTTP 狀態(tài)碼和一條 message 消息,將其傳遞回客戶端。下面是用于驗證準(zhǔn)入控制器的 AdmissionReview 響應(yīng)對象示例:
{
??"apiVersion":?"admission.k8s.io/v1",
??"kind":?"AdmissionReview",
??"response":?{
????"uid":?"" ,
????"allowed":?true/false,
????"status":?{
??????"code":?200/403>,
??????"message":?"optional?message"
????}
??}
}
如果我們要構(gòu)建一個 Mutating 準(zhǔn)入控制器,我們將使用一個 JSONPatch 類型的對象作為 AdmissionReview 響應(yīng)的 response 屬性的一部分發(fā)送回變更的結(jié)果,原始請求將使用此JSON Patch 進(jìn)行修改。下面是用于 Mutating 準(zhǔn)入控制器的 AdmissionReview 響應(yīng)對象示例:
{
??"apiVersion":?"admission.k8s.io/v1",
??"kind":?"AdmissionReview",
??"response":?{
????"uid":?"" ,
????"allowed":?true/false,
????"status":?{
??????"code":?200/403>,
??????"message":?"optional?message"
????},
????"patchType":?"JSONPatch",
????"patch":?
??}
}
關(guān)于 AdmissionReview 的完整結(jié)構(gòu)定義可以查看文檔:https://github.com/kubernetes/api/blob/master/admission/v1/types.go。
邏輯實現(xiàn)
這里我們要實現(xiàn)的是一個簡單的帶 TLS 認(rèn)證的 HTTP 服務(wù),用 Deployment 方式部署在我們的集群中。
首先新建項目:
???mkdir?admission-registry
???cd?admission-registry
???export?GOPROXY=https://goproxy.cn
???go?mod?init?github.com/cnych/admission-registry
go:?creating?new?go.mod:?module?github.com/cnych/admission-registry
然后在根目錄下面新建一個 main.go 的入口文件,在該文件中定義 webhook server 的入口點(diǎn),代碼如下所示:
var?param?server.WhSvrParam
//?命令行參數(shù)
flag.IntVar(¶m.Port,?"port",?443,?"Webhook?server?port.")
flag.StringVar(¶m.CertFile,?"tlsCertFile",?"/etc/webhook/certs/tls.crt",?"File?containing?the?x509?Certificate?for?HTTPS.")
flag.StringVar(¶m.KeyFile,?"tlsKeyFile",?"/etc/webhook/certs/tls.key",?"File?containing?the?x509?private?key?to?--tlsCertFile.")
flag.Parse()
klog.Info(fmt.Sprintf("port=%d,?cert-file=%s,?key-file=%s",?param.Port,?param.CertFile,?param.KeyFile))
pair,?err?:=?tls.LoadX509KeyPair(param.CertFile,?param.KeyFile)
if?err?!=?nil?{
?klog.Errorf("Failed?to?load?key?pair:?%v",?err)
?return
}
whsvr?:=?&server.WebhookServer{
?Server:?&http.Server{
??Addr:??????fmt.Sprintf(":%v",?param.Port),
??TLSConfig:?&tls.Config{Certificates:?[]tls.Certificate{pair}},
?},
?WhiteListRegistries:?strings.Split(os.Getenv("WHITELIST_REGISTRIES"),?","),
}
//?定義?http?server?和?handler
mux?:=?http.NewServeMux()
mux.HandleFunc("/validate",?whsvr.Serve)
mux.HandleFunc("/mutate",?whsvr.Serve)
whsvr.Server.Handler?=?mux
//?在一個新的?goroutine?中啟動?webhook?server
go?func()?{
?if?err?:=?whsvr.Server.ListenAndServeTLS("",?"");?err?!=?nil?{
??klog.Errorf("Failed?to?listen?and?serve?webhook?server:?%v",?err)
?}
}()
klog.Info("Server?started")
//?監(jiān)聽?OS?shutdown?信號
signalChan?:=?make(chan?os.Signal,?1)
signal.Notify(signalChan,?syscall.SIGINT,?syscall.SIGTERM)
<-signalChan
klog.Infof("Got?OS?shutdown?signal,?shutting?down?webhook?server?gracefully...")
if?err?:=?whsvr.Server.Shutdown(context.Background());?err?!=?nil?{
?klog.Errorf("HTTP?server?Shutdown:?%v",?err)
}
通過 flag 來獲取傳遞的命令行參數(shù),比如 TLS 證書,鏡像倉庫白名單等。然后使用標(biāo)準(zhǔn)庫 http 來定義服務(wù),通過一個 WebhookServer 結(jié)構(gòu)體進(jìn)行了簡單的封裝,雖然我們這里主要是實現(xiàn) validate 校驗功能,為了擴(kuò)展支持 muate,這里我們分別定義兩個端點(diǎn)來進(jìn)行支持:
mux.HandleFunc("/validate",?whsvr.Serve)
mux.HandleFunc("/mutate",?whsvr.Serve)
所以這里最重要的就是 serve 函數(shù)了,用來處理傳入的 mutate 和 validating 函數(shù)的 HTTP 請求。該函數(shù)從請求中反序列化?AdmissionReview?對象,執(zhí)行一些基本的內(nèi)容校驗,根據(jù) URL 路徑調(diào)用相應(yīng)的 mutate 和 validate 函數(shù),然后序列化 AdmissionReview 對象:
//?Serve?method?for?webhook?server
func?(serv?*WebhookServer)?Serve(w?http.ResponseWriter,?r?*http.Request)?{
?var?body?[]byte
?if?r.Body?!=?nil?{
??if?data,?err?:=?ioutil.ReadAll(r.Body);?err?==?nil?{
???body?=?data
??}
?}
?if?len(body)?==?0?{
??klog.Error("empty?body")
??http.Error(w,?"empty?body",?http.StatusBadRequest)
??return
?}
?//?verify?the?content?type?is?accurate
?contentType?:=?r.Header.Get("Content-Type")
?if?contentType?!=?"application/json"?{
??klog.Errorf("Content-Type=%s,?expect?application/json",?contentType)
??http.Error(w,?"invalid?Content-Type,?expect?`application/json`",?http.StatusUnsupportedMediaType)
??return
?}
?var?admissionResponse?*admv1.AdmissionResponse
?requestedAdmissionReview?:=?admv1.AdmissionReview{}
?if?_,?_,?err?:=?deserializer.Decode(body,?nil,?&requestedAdmissionReview);?err?!=?nil?{
??klog.Errorf("Can't?decode?body:?%v",?err)
??admissionResponse?=?&admv1.AdmissionResponse{
???Result:?&metav1.Status{
????Message:?err.Error(),
???},
??}
?}?else?{
??if?r.URL.Path?==?"/mutate"?{
???admissionResponse?=?serv.mutate(&requestedAdmissionReview)
??}?else?if?r.URL.Path?==?"/validate"?{
???admissionResponse?=?serv.validate(&requestedAdmissionReview)
??}
?}
?//?構(gòu)造返回的?AdmissionReview?結(jié)構(gòu)
?responseAdmissionReview?:=?admv1.AdmissionReview{}
?//?admission.k8s.io/v1?版本需要指定對應(yīng)的?APIVersion
?responseAdmissionReview.APIVersion?=?requestedAdmissionReview.APIVersion
?responseAdmissionReview.Kind?=?requestedAdmissionReview.Kind
?if?admissionResponse?!=?nil?{
??//?設(shè)置?response?屬性
??responseAdmissionReview.Response?=?admissionResponse
??if?requestedAdmissionReview.Request?!=?nil?{
???//?返回相同的?UID
???responseAdmissionReview.Response.UID?=?requestedAdmissionReview.Request.UID
??}
?}
?klog.Info(fmt.Sprintf("sending?response:?%v",?responseAdmissionReview.Response))
?respBytes,?err?:=?json.Marshal(responseAdmissionReview)
?if?err?!=?nil?{
??klog.Errorf("Can't?encode?response:?%v",?err)
??http.Error(w,?fmt.Sprintf("could?not?encode?response:?%v",?err),?http.StatusInternalServerError)
?}
?klog.Infof("Ready?to?write?response?...")
?if?_,?err?:=?w.Write(respBytes);?err?!=?nil?{
??klog.Errorf("Can't?write?response:?%v",?err)
??http.Error(w,?fmt.Sprintf("could?not?write?response:?%v",?err),?http.StatusInternalServerError)
?}
}
在上面的 serve 函數(shù)中會根據(jù)傳入的 PATH 來決定調(diào)用的邏輯,這里我們主要是實現(xiàn)校驗的功能,所以主要是實現(xiàn) validate 函數(shù)的邏輯:
//?validate?pod
func?(serv?*WebhookServer)?validate(ar?*admv1.AdmissionReview)?*admv1.AdmissionResponse?{
?req?:=?ar.Request
?var?(
??allowed?=?true
??code????=?200
??message?=?""
?)
?klog.Infof("AdmissionReview?for?Kind=%s,?Namespace=%s?Name=%v?UID=%v?Operation=%v?UserInfo=%v",
??req.Kind.Kind,?req.Namespace,?req.Name,?req.UID,?req.Operation,?req.UserInfo)
?var?pod?corev1.Pod
?if?err?:=?json.Unmarshal(req.Object.Raw,?&pod);?err?!=?nil?{
??klog.Errorf("Could?not?unmarshal?raw?object:?%v",?err)
??allowed?=?false
??code?=?400
??return?&admv1.AdmissionResponse{
???Allowed:?allowed,
???Result:?&metav1.Status{
????Code:????int32(code),
????Message:?err.Error(),
???},
??}
?}
?for?_,?container?:=?range?pod.Spec.Containers?{
??var?whitelisted?=?false
??for?_,?reg?:=?range?serv.WhiteListRegistries?{
???if?strings.HasPrefix(container.Image,?reg)?{
????whitelisted?=?true
???}
??}
??if?!whitelisted?{
???allowed?=?false
???code?=?403
???message?=?fmt.Sprintf("%s?image?comes?from?an?untrusted?registry!?Only?images?from?%v?are?allowed.",
????container.Image,?serv.WhiteListRegistries)
???break
??}
?}
?return?&admv1.AdmissionResponse{
??Allowed:?allowed,
??Result:?&metav1.Status{
???Code:????int32(code),
???Message:?message,
??},
?}
}
代碼實現(xiàn)邏輯也很簡單的,就是拿著傳入的對象 Pod,循環(huán)里面的鏡像,判斷這些鏡像是否都是白名單列表中的鏡像,如果是則校驗通過,否則校驗失敗,返回 allowed=false。
部署
證書
上面我們實現(xiàn)了最基本的業(yè)務(wù)邏輯,由于 webhook 要求是通過 HTTPS 暴露服務(wù),所以我們還需要為其生成相關(guān)的證書。為了方便這里我們可以使用 cfssl 來生成相關(guān)證書。
安裝 cfssl:
#?OS?X
???brew?install?cfssl
#?Linux
???wget?-q?--show-progress?--https-only?--timestamping?\
??https://pkg.cfssl.org/R1.2/cfssl_linux-amd64?\
??https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
???chmod?+x?cfssl_linux-amd64?cfssljson_linux-amd64
???sudo?mv?cfssl_linux-amd64?/usr/local/bin/cfssl
???sudo?mv?cfssljson_linux-amd64?/usr/local/bin/cfssljson
然后創(chuàng)建 CA 證書機(jī)構(gòu),執(zhí)行下面的命令:
???cat?>?ca-config.json?<{
??"signing":?{
????"default":?{
??????"expiry":?"8760h"
????},
????"profiles":?{
??????"server":?{
????????"usages":?["signing",?"key?encipherment",?"server?auth",?"client?auth"],
????????"expiry":?"8760h"
??????}
????}
??}
}
EOF
???cat?>?ca-csr.json?<{
????"CN":?"kubernetes",
????"key":?{
????????"algo":?"rsa",
????????"size":?2048
????},
????"names":?[
????????{
????????????"C":?"CN",
????????????"L":?"BeiJing",
????????????"ST":?"BeiJing",
????????????"O":?"k8s",
????????????"OU":?"System"
????????}
????]
}
EOF
然后使用下面的命令生成 CA 證書和私鑰:
???cfssl?gencert?-initca?ca-csr.json?|?cfssljson?-bare?ca
2021/01/23?16:59:51?[INFO]?generating?a?new?CA?key?and?certificate?from?CSR
2021/01/23?16:59:51?[INFO]?generate?received?request
2021/01/23?16:59:51?[INFO]?received?CSR
2021/01/23?16:59:51?[INFO]?generating?key:?rsa-2048
2021/01/23?16:59:51?[INFO]?encoded?CSR
2021/01/23?16:59:51?[INFO]?signed?certificate?with?serial?number?502715407096434913295607470541422244575186494509
???ls?-la?*.pem
-rw-------??1?ych??staff??1675?Jan?23?17:05?ca-key.pem
-rw-r--r--??1?ych??staff??1310?Jan?23?17:05?ca.pem
然后接下來就可以創(chuàng)建 Server 端證書了:
???cat?>?server-csr.json?<{
??"CN":?"admission",
??"key":?{
????"algo":?"rsa",
????"size":?2048
??},
??"names":?[
????{
????????"C":?"CN",
????????"L":?"BeiJing",
????????"ST":?"BeiJing",
????????"O":?"k8s",
????????"OU":?"System"
????}
??]
}
EOF
???cfssl?gencert?-ca=ca.pem?-ca-key=ca-key.pem?-config=ca-config.json?\
??-hostname=admission-registry.default.svc?-profile=server?server-csr.json?|?cfssljson?-bare?server
2021/01/23?17:08:37?[INFO]?generate?received?request
2021/01/23?17:08:37?[INFO]?received?CSR
2021/01/23?17:08:37?[INFO]?generating?key:?rsa-2048
2021/01/23?17:08:37?[INFO]?encoded?CSR
2021/01/23?17:08:37?[INFO]?signed?certificate?with?serial?number?701199816701013791180179639053450980282079712724
???ls?-la?*.pem
-rw-------??1?ych??staff??1675?Jan?23?17:05?ca-key.pem
-rw-r--r--??1?ych??staff??1310?Jan?23?17:05?ca.pem
-rw-------??1?ych??staff??1675?Jan?23?17:08?server-key.pem
-rw-r--r--??1?ych??staff??1452?Jan?23?17:08?server.pem
其中最重要的就是 -hostname 的值,格式為 ?{service-name}.{service-namespace}.svc,其中 service-name 代表你 webhook 的 Service 名字,service-namespace 代表你 webhook 的命名空間。
然后使用生成的 server 證書和私鑰創(chuàng)建一個 Secret 對象:
#?創(chuàng)建Secret
???kubectl?create?secret?tls?admission-registry-tls?\
????????--key=server-key.pem?\
????????--cert=server.pem
secret/admission-registry-tls?created
后面我們通過 Volumes 的形式將 Secret 掛載到 webhook 的容器中指定的位置給 webhook 使用即可。
Docker 鏡像
然后接下來我們只需要將 webhook 打包成 Docker 鏡像,并使用一個 Deployment 來運(yùn)行這個容器應(yīng)用即可,對應(yīng)的 Dockerfile 文件如下所示:
#?Build?the?webhook?binary
FROM?golang:1.13?as?builder
RUN?apt-get?-y?update?&&?apt-get?-y?install?upx
WORKDIR?/workspace
#?Copy?the?Go?Modules?manifests
COPY?go.mod?go.mod
COPY?go.sum?go.sum
#?Copy?the?go?source
COPY?main.go?main.go
COPY?server/?server/
#?Build
ENV?CGO_ENABLED=0
ENV?GOOS=linux
ENV?GOARCH=amd64
ENV?GO111MODULE=on
ENV?GOPROXY="https://goproxy.cn"
#?cache?deps?before?building?and?copying?source?so?that?we?don't?need?to?re-download?as?much
#?and?so?that?source?changes?don't?invalidate?our?downloaded?layer
RUN?go?mod?download?&&?\
????go?build?-a?-o?admission-registry?main.go?&&?\
????upx?admission-registry
FROM?alpine:3.9.2
COPY?--from=builder?/workspace/admission-registry?.
ENTRYPOINT?["/admission-registry"]
這里我們使用了 Docker 的多階段構(gòu)建功能,先將項目構(gòu)建打包成二進(jìn)制文件,然后在 distrolesss 中運(yùn)行該應(yīng)用,執(zhí)行項目的命令構(gòu)建推送鏡像即可:
???docker?build?-t?cnych/admission-registry:v0.0.1?.
???docker?push?cnych/admission-registry:v0.0.1
部署 webhook
現(xiàn)在 webhook 的鏡像已經(jīng)準(zhǔn)備好了,接下來我們就需要將其部署到 Kubernetes 集群中,這里我們使用 Deployment + Service 來提供服務(wù)即可,在 Pod 的規(guī)范中配置環(huán)境變量 WHITELIST_REGISTRIES 來定義白名單鏡像倉庫地址,然后將證書通過 Secret 的 Volumes 形式掛載到 Pod 容器中,對應(yīng)的資源清單文件如下所示:
apiVersion:?apps/v1
kind:?Deployment
metadata:
??name:?admission-registry
??labels:
????app:?admission-registry
spec:
??selector:
????matchLabels:
??????app:?admission-registry
??template:
????metadata:
??????labels:
????????app:?admission-registry
????spec:
??????containers:
????????-?name:?whitelist
??????????image:?cnych/admission-registry:v0.1.1
??????????imagePullPolicy:?IfNotPresent
??????????env:
??????????-?name:?WHITELIST_REGISTRIES
????????????value:?"docker.io,gcr.io"
??????????ports:
??????????-?containerPort:?443
??????????volumeMounts:
??????????-?name:?webhook-certs
????????????mountPath:?/etc/webhook/certs
????????????readOnly:?true
??????volumes:
????????-?name:?webhook-certs
??????????secret:
????????????secretName:?admission-registry-tls
---
apiVersion:?v1
kind:?Service
metadata:
??name:?admission-registry
??labels:
????app:?admission-registry
spec:
??ports:
??-?port:?443
????targetPort:?443
??selector:
????app:?admission-registry
直接創(chuàng)建上面的資源清單即可:
???kubectl?get?pods?-l?app=admission-registry
NAME??????????????????????????????????READY???STATUS????RESTARTS???AGE
admission-registry-675bc5c575-vqxsn???1/1?????Running???0??????????37s
???kubectl?get?svc?-l?app=admission-registry????????
NAME?????????????????TYPE????????CLUSTER-IP?????EXTERNAL-IP???PORT(S)???AGE
admission-registry???ClusterIP???10.96.14.219??? ????????443/TCP???61s
注冊 webhook
上面我們只是單純將我們實現(xiàn)的 webhook 部署到了 Kubernetes 集群中,但是還并沒有和 ValidatingWebhook 對接起來,要將我們上面實現(xiàn)的服務(wù)注冊到 ValidatingWebhook 中只需要創(chuàng)建一個類型為 ValidatingWebhookConfiguration 的 Kubernetes 資源對象即可,在這個對象中就可以來配置我們的 webhook 這個服務(wù)。
如下所示,我們將 webhook 命名為 io.ydzs.admission-registry ,只需要保證在集群中名稱唯一即可。然后在 rules 屬性下面就是來指定在什么條件下使用該 webhook 的配置,這里我們只需要在創(chuàng)建 Pod 的時候才調(diào)用這個 webhook。此外在 ClientConfig 屬性下我們還需要指定 Kubernetes APIServer 如何來找到我們的 webhook 服務(wù),這里我們將通過一個在 default 命名空間下面的名為 admission-registry 的 Service 服務(wù)在 /validate 路徑下面提供服務(wù),此外還指定了一個 caBundle 的屬性,這個屬性通過指定一個 PEM 格式的 CA bundle 來表示 APIServer 作為客戶端可以使用它來驗證我們的 webhook 應(yīng)用上的服務(wù)器證書。對應(yīng)的注冊 webhook 的資源清單如下所示:
#?validatingwebhook.yaml
apiVersion:?admissionregistration.k8s.io/v1
kind:?ValidatingWebhookConfiguration
metadata:
??name:?admission-registry
webhooks:
-?name:?io.ydzs.admission-registry
??rules:
??-?apiGroups:???[""]
????apiVersions:?["v1"]
????operations:??["CREATE"]
????resources:???["pods"]
??clientConfig:
????service:
??????namespace:?default
??????name:?admission-registry
??????path:?"/validate"
????caBundle:?CA_BUNDLE
??admissionReviewVersions:?["v1"]
??sideEffects:?None
上面的 CA_BUNDLE 值使用的是上面生成 ca.crt 文件內(nèi)容的 base64 值:
???cat?ca.pem?|?base64
LS0tLS1CRUdVlNQ......EUtLS0tLQo=
然后將得到的值替換掉 validatingwebhook.yaml 文件中的 CA_BUNDLE ,然后就可以直接部署到集群中:
???kubectl?apply?-f?manifests/validatingwebhook.yaml
???kubectl?get?validatingwebhookconfiguration???????
NAME?????????????????????????????WEBHOOKS???AGE
admission-registry???????????????1??????????52s
到這里我們的鏡像白名單校驗的 webhook 就部署完成了。
測試
接下來我們來測試下上面我們 webhook 是否生效了。首先創(chuàng)建一個如下所示的測試 Pod 清單:
#?test-pod1.yaml
apiVersion:?v1
kind:?Pod
metadata:
??name:?test-pod1
spec:
??containers:
??-?name:?nginx
????image:?docker.io/nginx:latest
由于 docker.io 是我們的鏡像白名單,所以正常上面的應(yīng)用是可以正常創(chuàng)建的:
???kubectl?apply?-f?test-pod1.yaml
pod/test-pod1?created
然后創(chuàng)建另外一個 Pod,這次我們使用一個 ydzs.io 的鏡像倉庫的鏡像:
#?test-pod2.yaml
apiVersion:?v1
kind:?Pod
metadata:
??name:?test-pod2
spec:
??containers:
????-?name:?nginx
??????image:?ydzs.io/nginx:latest
由于 ydzs.io 并不在我們的鏡像白名單中,所以正常部署后會被拒絕:
???kubectl?apply?-f?test-pod2.yaml
kubectl?apply?-f?manifests/test-pod2.yaml???????????????????????????
Error?from?server:?error?when?creating?"manifests/test-pod2.yaml":?admission?webhook?"io.ydzs.admission-registry"?denied?the?request:?ydzs.io/nginx:latest?image?comes?from?an?untrusted?registry!?Only?images?from?[docker.io?gcr.io]?are?allowed.
可以看到上面的 Pod 部署失敗了,因為不在鏡像白名單中,證明我們的校驗準(zhǔn)入控制器邏輯是正確的。
清理
要移除這個校驗準(zhǔn)入控制器比較簡單,只需要移除上面的幾個資源對象即可:
???kubectl?delete?-f?validatingwebhook.yaml
???kubectl?delete?-f?webhook.yaml
總結(jié)
這里我們只是通過一個簡單的示例來說明我們應(yīng)該如何去開發(fā)一個校驗的準(zhǔn)入控制器,對于 Mutate 類型的控制器實現(xiàn)方式也基本一致。當(dāng)然如果我們只是簡單的想現(xiàn)在下鏡像倉庫,我們也可以不需要自己去編寫代碼來實現(xiàn),畢竟這樣效率并不是很高,我們可以通過 Open Policy Agent Gatekeeper 項目來實現(xiàn),它提供了一種通過策略配置而不是編寫代碼來實現(xiàn)類似用例的方法。
進(jìn)階訓(xùn)練營第二期
本次訓(xùn)練營采用線上直播的形式,基于1.19.x版本,根據(jù)第1期課程的打磨,我們總結(jié)出了 Docker 基礎(chǔ) + Kubernetes 基礎(chǔ) + 原理 + 基本使用 + 進(jìn)階技能 +?完整項目實踐?的課程體系。加強(qiáng)系統(tǒng)知識吸收、夯實基礎(chǔ)的同時,并在實際操作過程中去了解排查問題的方式方法,更為重要的是我們的老師非常負(fù)責(zé)任,隨時幫你答疑解惑,我們認(rèn)為不只是課堂上講授知識,更重要的是售后支持,完全不用擔(dān)心學(xué)習(xí)不到知識。
?點(diǎn)擊屏末?|?閱讀原文?|?即刻學(xué)習(xí)


