<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3714字,需瀏覽 8分鐘

           ·

          2021-01-27 17:43

          前面我們已經(jīng)介紹了準(zhǔn)入控制器的含義,了解可以通過有兩個特殊的“動態(tài)”控制器 -ValidatingAdmissionWebhookMutatingAdmissionWebhook 來讓開發(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í)

          瀏覽 24
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  爱情岛论坛av | www狠狠干 | 亚洲无码高清观看 | 九九九999成人 | 无码系列亚洲精品国产A√现线 |