手把手教你在容器服務 TKE 中使用動態准入控制器

騰訊雲原生發表於2021-02-04

在 TKE 中使用動態准入控制器

原理概述

動態准入控制器 Webhook 在訪問鑑權過程中可以更改請求物件或完全拒絕該請求,其呼叫 Webhook 服務的方式使其獨立於叢集元件,具有非常大的靈活性,可以方便的做很多自定義准入控制,下圖為動態准入控制在 API 請求呼叫鏈的位置(來源於 Kubernetes 官網):

img

從上圖可以看出,動態准入控制過程分為兩個階段:首先執行 Mutating 階段,可以對到達請求進行修改,然後執行 Validating 階段來驗證到達的請求是否被允許,兩個階段可以單獨使用也可以組合使用,本文將在 TKE 中實現一個簡單的動態准入控制呼叫示例。

檢視驗證外掛

在 TKE 現有叢集版本中(1.10.5 及以上)已經預設開啟了 validating admission webhookmutating admission webhook API,如果是更低版本的叢集,可以在 Apiserver Pod 中執行 kube-apiserver -h | grep enable-admission-plugins 驗證當前叢集是否開啟,輸出外掛列表中如果有 MutatingAdmissionWebhookValidatingAdmissionWebhook 就說明當前叢集開啟了動態准入的控制器外掛,如下圖所示:

img

簽發證書

為了確保動態准入控制器呼叫的是可信任的 Webhook 服務端,必須通過 HTTPS 來呼叫 Webhook 服務(TLS認證), 所以需要為 Webhook 服務端頒發證書,並且在註冊動態准入控制 Webhook 時為 caBundle 欄位( ValidatingWebhookConfigurationMutatingAdmissionWebhook 資源清單中的 caBundle 欄位)繫結受信任的頒發機構證書(CA)來核驗 Webhook 服務端的證書是否可信任, 這裡分別介紹兩種推薦的頒發證書方法:

注意:當ValidatingWebhookConfigurationMutatingAdmissionWebhook 使用 clientConfig.service 配置時(Webhook 服務在叢集內),為伺服器端頒發的證書域名必須為 <svc_name>.<svc_namespace>.svc

方法一: 製作自簽證書

製作自簽證書的方法比較獨立,不依賴於 K8s 叢集,類似於為一個網站做一個自簽證書,有很多工具可以製作自簽證書,本示例使用 Openssl 製作自簽證書,操作步驟如下所示:

  1. 生成金鑰位數為 2048 的 ca.key:

    openssl genrsa -out ca.key 2048
    
  2. 依據 ca.key 生成 ca.crt,"webserver.default.svc" 為 Webhook 服務端在叢集中的域名,使用 -days 引數來設定證書有效時間:

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=webserver.default.svc" -days 10000 -out ca.crt
    
  3. 生成金鑰位數為 2048 的 server.key:

    openssl genrsa -out server.key 2048
    

    i. 建立用於生成證書籤名請求(CSR)的配置檔案 csr.conf 示例如下:

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    distinguished_name = dn
    [ dn ]
    C = cn
    ST = shaanxi
    L = xi'an
    O = default
    OU = websever
    CN = webserver.default.svc
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    
  4. 基於配置檔案 csr.conf 生成證書籤名請求:

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  5. 使用 ca.key、ca.crt 和 server.csr 頒發生成伺服器證書(x509簽名):

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
      -CAcreateserial -out server.crt -days 10000 \
      -extensions v3_ext -extfile csr.conf
    
  6. 檢視 Webhook server 端證書:

    openssl x509  -noout -text -in ./server.crt
    

其中,生成的證書、金鑰檔案說明如下:

ca.crt 為頒發機構證書,ca.key 為頒發機構證書金鑰,用於服務端證書頒發。

server.crt 為 頒發的服務端證書,server.key 為頒發的服務端證書金鑰.

方法二:使用 K8S CSR API 簽發

除了使用方案一加密工具製作自簽證書,還可以使用 k8s 的證書頒發機構系統來下發證書,執行下面指令碼可使用 K8s 叢集根證書和根金鑰簽發一個可信任的證書使用者,需要注意的是使用者名稱應該為 Webhook 服務在叢集中的域名:

USERNAME='webserver.default.svc' # 設定需要建立的使用者名稱為 Webhook 服務在叢集中的域名
# 使用 Openssl 生成自簽證書 key
openssl genrsa -out ${USERNAME}.key 2048
# 使用 Openssl 生成自簽證書 CSR 檔案, CN 代表使用者名稱,O 代表組名
openssl req -new -key ${USERNAME}.key -out ${USERNAME}.csr -subj "/CN=${USERNAME}/O=${USERNAME}" 
# 建立 Kubernetes 證書籤名請求(CSR)
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: ${USERNAME}
spec:
  request: $(cat ${USERNAME}.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF
# 證書審批允許信任
kubectl certificate approve ${USERNAME}
# 獲取自簽證書 CRT
kubectl get csr ${USERNAME} -o jsonpath={.status.certificate} > ${USERNAME}.crt

其中, ${USERNAME}.crt 為服務端證書, ${USERNAME}.key 為 Webhook 服務端證書金鑰。

操作示例

下面將使用 ValidatingWebhookConfiguration 資源在 TKE 中實現一個動態准入 Webhook 呼叫示例,本示例程式碼可在 示例程式碼 中獲取(為了確保可訪問性,示例程式碼 Fork 自 原始碼庫,作者實現了一個簡單的動態准入 Webhook 請求和響應的介面,具體介面格式請參考 Webhook 請求和響應 。為了方便,我將使用它作為我們的 Webhook 服務端程式碼。

  1. 準備 caBundle 內容

    • 若頒發證書方法是方案一, 使用 base64 編碼 ca.crt 生成 caBundle 欄位內容:

       cat ca.crt | base64 --wrap=0
      
      
    • 若頒發證書方法是方案二,叢集的根證書即為 caBundle 欄位內容,可以通過 TKE 叢集控制檯【基本資訊】-> 【叢集APIServer資訊】Kubeconfig 內容中的clusters.cluster[].certificate-authority-data 欄位獲取,該欄位已經 base64 編碼過了,無需再做處理。

  2. 複製生成的 ca.crt (頒發機構證書),server.crt(HTTPS 證書)), server.key(HTTPS 金鑰) 到專案主目錄:

    img

  3. 修改專案中的 Dockerfile ,新增三個證書檔案到容器工作目錄:
    img

    然後使用 docker 命令構建 Webhook 服務端映象:

    docker build -t webserver .
    
    
  4. 部署一個域名為 "weserver.default.svc" 的 Webhook 後端服務,修改適配後的 controller.yaml 如下:

    img

  5. 註冊建立型別為 ValidatingWebhookConfiguration 的資源,本示例配置的 Webhook 觸發規則是當建立 pods型別,API 版本 "v1" 時觸發呼叫,clientConfig 配置對應上述在叢集中建立的的 Webhook 後端服務, caBundle 欄位內容為證書頒發方法一獲取的ca.crt 內容,修改適配專案中的 admission.yaml 檔案如下圖:

    img

  6. 註冊好後建立一個 Pod 型別, API 版本為 "v1" 的測試資源如下:

    img

  7. 測試程式碼有列印請求日誌, 檢視 Webhook 服務端日誌可以看到動態准入控制器觸發了 webhook 呼叫,如下圖:

    img

  8. 此時檢視建立的測試pod 是成功建立的,是因為測試 Webhook 服務端程式碼寫死的 allowed: true,所以是可以建立成功的,如下圖:
    img

  9. 為了進一步驗證,我們把 "allowed" 改成 "false" ,然後重複上述步驟重新打 Webserver 服務端映象,並重新部署 controller.yaml 和 admission.yaml 資源,當再次嘗試建立 "pods" 資源時請求被動態准入攔截,說明配置的動態准入策略是生效的,如下圖所示:

    img

總結

本文主要介紹了動態准入控制器 Webhook 的概念和作用、如何在 TKE 叢集中籤發動態准入控制器所需的證書,並使用簡單示例演示如何配置和使用動態准入 Webhook 功能。

參考

Kubernetes Dynamic Admission Control by Example

Dynamic Admission Control(官網)

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多幹貨!!

相關文章