設定祕密存放區 CSI 驅動程式以使用 TLS 啟用 NGINX 輸入控制器

本文將逐步引導您使用 Azure Kubernetes Service (AKS) 叢集和 Azure Key Vault (AKV) 執行個體來保護 NGINX 輸入控制器的流程。 如需詳細資訊,請參閱 Kubernetes 中的 TLS

您可以使用下列其中一種方法,將輸入 TLS 憑證匯入叢集:

  • 應用程式:應用程式部署資訊清單會宣告並掛接提供者磁碟區。 只有當您部署應用程式時,叢集中才會提供憑證。 當您移除應用程式時,也會移除祕密。 此案例適用於負責應用程式安全性基礎結構和其與叢集整合的開發小組。
  • 輸入控制器:輸入部署已修改,可宣告和掛接提供者磁碟區。 建立輸入 Pod 時,會匯入祕密。 應用程式的 Pod 無法存取 TLS 憑證。 此案例適用於一個小組 (例如 IT) 管理及建立基礎結構和網路元件 (包括 HTTPS TLS 憑證),而其他小組管理應用程式生命週期的案例。

必要條件

產生 TLS 憑證

  • 使用下列命令產生 TLS 憑證。

    export CERT_NAME=aks-ingress-cert
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
        -out aks-ingress-tls.crt \
        -keyout aks-ingress-tls.key \
        -subj "/CN=demo.azure.com/O=aks-ingress-tls"
    

將憑證匯入 AKV

  1. 執行下列命令,將憑證匯出至 PFX 檔案。

    export AKV_NAME="[YOUR AKV NAME]"
    openssl pkcs12 -export -in aks-ingress-tls.crt -inkey aks-ingress-tls.key  -out $CERT_NAME.pfx
    # skip Password prompt
    
  2. 使用 az keyvault certificate import (部分機器翻譯) 命令匯入憑證。

    az keyvault certificate import --vault-name $AKV_NAME -n $CERT_NAME -f $CERT_NAME.pfx
    

部署 SecretProviderClass

  1. 使用下列命令匯出新的命名空間。

    export NAMESPACE=ingress-basic
    
  2. 使用 kubectl create namespace (部分機器翻譯) 命令建立命名空間。

    kubectl create namespace $NAMESPACE
    
  3. 選取方法以提供存取身分識別,並據以設定您的 SecretProviderClass YAML。

    • 務必使用 objectType=secret,這是從 AKV 取得私密金鑰和憑證的唯一方式。
    • secretObjects 區段中將 kubernetes.io/tls 設定為 type

    參閱下列範例,了解您的 SecretProviderClass 可能的樣子:

    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: azure-tls
    spec:
      provider: azure
      secretObjects:                            # secretObjects defines the desired state of synced K8s secret objects
        - secretName: ingress-tls-csi
          type: kubernetes.io/tls
          data: 
            - objectName: $CERT_NAME
              key: tls.key
            - objectName: $CERT_NAME
              key: tls.crt
      parameters:
        usePodIdentity: "false"
        useVMManagedIdentity: "true"
        userAssignedIdentityID: <client id>
        keyvaultName: $AKV_NAME                 # the name of the AKV instance
        objects: |
          array:
            - |
              objectName: $CERT_NAME
              objectType: secret
        tenantId: $TENANT_ID                    # the tenant ID of the AKV instance
    
  4. 使用 kubectl apply (部分機器翻譯) 命令,將 SecretProviderClass 套用至您的 Kubernetes 叢集。

    kubectl apply -f secretProviderClass.yaml -n $NAMESPACE
    

部屬輸入控制器

新增官方輸入圖表存放庫

  • 使用下列 helm (部分機器翻譯) 命令,新增官方輸入圖表存放庫。

    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm repo update
    

設定和部署 NGINX 輸入

視您的情節而定,您可以選擇將憑證繫結至應用程式或輸入控制器。 根據您的選擇遵循以下指示:

將憑證繫結至應用程式

  • 使用 helm install (部分機器翻譯) 命令,將憑證繫結至應用程式。 應用程式的部署會參考祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者。

    helm install ingress-nginx/ingress-nginx --generate-name \
        --namespace $NAMESPACE \
        --set controller.replicaCount=2 \
        --set controller.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
        --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux
    

將憑證繫結至輸入控制器

  1. 使用 helm install (部分機器翻譯) 命令,將憑證繫結至輸入控制器。 輸入控制器的部署會參考祕密存放區 CSI 驅動程式的 Azure Key Vault 提供者。

    注意

    • 如果未使用 Microsoft Entra Pod 受控識別作為存取方法,請移除含有 --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME 的程式碼行。

    • 此外,需要將 SecretProviderClass 繫結至 Pod,祕密存放區 CSI 驅動程式才能掛接它,並產生 Kubernetes 祕密。 請參閱使用 Kubernetes 祕密同步掛接的內容 (部分機器翻譯)。

    helm install ingress-nginx/ingress-nginx --generate-name \
        --namespace $NAMESPACE \
        --set controller.replicaCount=2 \
        --set controller.nodeSelector."kubernetes\.io/os"=linux \
        --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
        --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME \
        -f - <<EOF
    controller:
      extraVolumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
      extraVolumeMounts:
          - name: secrets-store-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
    EOF
    
  2. 使用 kubectl get secret 命令,確認已建立 Kubernetes 祕密。

    kubectl get secret -n $NAMESPACE
    
    NAME                                             TYPE                                  DATA   AGE
    ingress-tls-csi                                  kubernetes.io/tls                     2      1m34s
    

部署應用程式

同樣地,指示會根據您的案例而有些微變更。 請遵循與您所選取案例相對應的指示進行操作。

使用應用程式參考部署應用程式

  1. 建立名為 aks-helloworld-one.yaml 且具有下列內容的檔案。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-one  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-one
      template:
        metadata:
          labels:
            app: aks-helloworld-one
        spec:
          containers:
          - name: aks-helloworld-one
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "Welcome to Azure Kubernetes Service (AKS)"
            volumeMounts:
            - name: secrets-store-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
          volumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-one  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-one
    
  2. 建立名為 aks-helloworld-two.yaml 且含有下列內容的檔案。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-two  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-two
      template:
        metadata:
          labels:
            app: aks-helloworld-two
        spec:
          containers:
          - name: aks-helloworld-two
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "AKS Ingress Demo"
            volumeMounts:
            - name: secrets-store-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
          volumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-two
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-two
    
  3. 使用 kubectl apply 命令,將 YAML 檔案套用至您的叢集。

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    
  4. 使用 kubectl get secret 命令,確認已建立 Kubernetes 祕密。

    kubectl get secret -n $NAMESPACE
    
    NAME                                             TYPE                                  DATA   AGE
    ingress-tls-csi                                  kubernetes.io/tls                     2      1m34s
    

使用輸入控制器參考部署應用程式

  1. 建立名為 aks-helloworld-one.yaml 且具有下列內容的檔案。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-one  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-one
      template:
        metadata:
          labels:
            app: aks-helloworld-one
        spec:
          containers:
          - name: aks-helloworld-one
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "Welcome to Azure Kubernetes Service (AKS)"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-one
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-one
    
  2. 建立名為 aks-helloworld-two.yaml 且含有下列內容的檔案。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-two  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-two
      template:
        metadata:
          labels:
            app: aks-helloworld-two
        spec:
          containers:
          - name: aks-helloworld-two
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "AKS Ingress Demo"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-two  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-two
    
  3. 使用 kubectl apply 命令,將 YAML 檔案套用至您的叢集。

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    

部署參考祕密的輸入資源

我們現在可以部署參考祕密的 Kubernetes 輸入資源。

  1. 建立名為 hello-world-ingress.yaml 且具有下列內容的檔案。

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingress-tls
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$2
    spec:
      ingressClassName: nginx
      tls:
      - hosts:
        - demo.azure.com
        secretName: ingress-tls-csi
      rules:
      - host: demo.azure.com
        http:
          paths:
          - path: /hello-world-one(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
          - path: /hello-world-two(/|$)(.*)
            pathType: Prefix      
            backend:
              service:
                name: aks-helloworld-two
                port:
                  number: 80
          - path: /(.*)
            pathType: Prefix      
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
    
  2. 記下會參考稍早所建立之祕密的 tls 區段,並使用 kubectl apply 命令將該檔案套用至您的叢集。

    kubectl apply -f hello-world-ingress.yaml -n $NAMESPACE
    

取得輸入控制器的外部 IP 位址

  • 使用 kubectl get service 命令,取得輸入控制器的外部 IP 位址。

    kubectl get service --namespace $NAMESPACE --selector app.kubernetes.io/name=ingress-nginx
    
    NAME                                       TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE
    nginx-ingress-1588032400-controller        LoadBalancer   10.0.255.157   EXTERNAL_IP      80:31293/TCP,443:31265/TCP   19m
    nginx-ingress-1588032400-default-backend   ClusterIP      10.0.223.214   <none>           80/TCP                       19m
    

測試使用 TLS 保護的輸入

  1. 使用下列 curl 命令,確認您的輸入已使用 TLS 正確設定。 確定您會使用上一個步驟中的外部 IP。

    curl -v -k --resolve demo.azure.com:443:EXTERNAL_IP https://demo.azure.com
    

    由於先前並未提供位址的另一個路徑,因此,輸入控制器預設為 / 路由。 此時會傳回第一個示範應用程式,如下列簡要範例輸出所示:

    [...]
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>Welcome to Azure Kubernetes Service (AKS)</title>
    [...]
    

    curl 命令中的 -v 參數會輸出詳細資訊,包括所收到的 TLS 憑證。 當您的 curl 輸出進行到一半時,您可以驗證是否已使用自有的 TLS 憑證。 即使我們使用自我簽署的憑證,-k 參數會繼續載入頁面。 下列範例顯示已使用 issuer: CN=demo.azure.com; O=aks-ingress-tls 憑證:

    [...]
     * Server certificate:
     *  subject: CN=demo.azure.com; O=aks-ingress-tls
     *  start date: Oct 22 22:13:54 2021 GMT
     *  expire date: Oct 22 22:13:54 2022 GMT
     *  issuer: CN=demo.azure.com; O=aks-ingress-tls
     *  SSL certificate verify result: self signed certificate (18), continuing anyway.
    [...]
    
  2. /hello-world-two 路徑新增至位址 (例如 https://demo.azure.com/hello-world-two),並確認已正確設定第二個示範應用程式。

    curl -v -k --resolve demo.azure.com:443:EXTERNAL_IP https://demo.azure.com/hello-world-two
    

    此時會傳回含有自訂標題的第二個示範應用程式,如下列簡要範例輸出所示:

    [...]
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>AKS Ingress Demo</title>
    [...]