Cloud Service Mesh 範例:mTLS


在 Cloud Service Mesh 1.5 以上版本中,系統預設會啟用自動相互傳輸層安全標準 (自動 mTLS)。透過自動 mTLS,用戶端 Sidecar Proxy 會自動偵測伺服器是否具有 Sidecar。用戶端 Sidecar 會將 mTLS 傳送至含有 Sidecar 的工作負載,並將純文字傳送至不含 Sidecar 的工作負載。但請注意,服務接受純文字和 mTLS 流量。將補充代理伺服器注入 Pod 時,建議您也設定服務,只接受 mTLS 流量。

使用 Cloud Service Mesh 時,您只要套用單一 YAML 檔案,即可在應用程式程式碼外部強制執行 mTLS。Cloud Service Mesh 可讓您靈活地將驗證政策套用至整個服務網格、命名空間或個別工作負載。

雙向 mTLS

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

完成本教學課程後,您可以刪除已建立的資源,避免持續產生費用。詳情請參閱「清除」。

事前準備

部署 Ingress 閘道

  1. kubectl 的目前環境設為叢集:

    gcloud container clusters get-credentials CLUSTER_NAME  \
    --project=PROJECT_ID \
    --zone=CLUSTER_LOCATION 
    
  2. 為 Ingress 閘道建立命名空間:

    kubectl create namespace asm-ingress
    
  3. 啟用要用於注入的命名空間。步驟取決於控制層實作

    受管理 (TD)

    將預設插入標籤套用至命名空間:

    kubectl label namespace asm-ingress \
        istio.io/rev- istio-injection=enabled --overwrite
    

    受管理 (Istiod)

    建議:執行下列指令,將預設插入標籤套用至命名空間:

      kubectl label namespace asm-ingress \
          istio.io/rev- istio-injection=enabled --overwrite
    

    如果您是使用受管理 Istiod 控制平面的現有使用者: 建議您使用預設注入,但系統也支援以修訂版本為準的注入。請按照下列指示操作:

    1. 執行下列指令,找出可用的發布管道:

      kubectl -n istio-system get controlplanerevision
      

      輸出結果會與下列內容相似:

      NAME                AGE
      asm-managed-rapid   6d7h
      

      在輸出內容中,「NAME」欄下方的值是與 Cloud Service Mesh 版本可用發布管道對應的修訂版本標籤。

    2. 將修訂版本標籤套用至命名空間:

      kubectl label namespace asm-ingress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  4. anthos-service-mesh-samples 存放區中部署範例閘道:

    kubectl apply -n asm-ingress \
    -f docs/shared/asm-ingress-gateway
    

    預期輸出內容:

    serviceaccount/asm-ingressgateway configured
    service/asm-ingressgateway configured
    deployment.apps/asm-ingressgateway configured
    gateway.networking.istio.io/asm-ingressgateway configured
    

部署 Online Boutique 範例應用程式

  1. 如果尚未設定,請將 kubectl 的目前背景資訊設為叢集:

      gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. 為範例應用程式建立命名空間:

      kubectl create namespace onlineboutique
    
  3. 標記 onlineboutique 命名空間,自動插入 Envoy Proxy。 按照這些步驟啟用自動 Sidecar 插入功能。

  4. 部署範例應用程式、前端的 VirtualService,以及工作負載的服務帳戶。在本教學課程中,您將部署 Online Boutique,這是微服務示範應用程式。

      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/virtual-service.yaml
      kubectl apply \
      -n onlineboutique \
      -f docs/shared/online-boutique/service-accounts
    

查看服務

  1. 查看 onlineboutique 命名空間中的 Pod:

    kubectl get pods -n onlineboutique
    

    預期輸出內容:

    NAME                                     READY   STATUS    RESTARTS   AGE
    adservice-85598d856b-m84m6               2/2     Running   0          2m7s
    cartservice-c77f6b866-m67vd              2/2     Running   0          2m8s
    checkoutservice-654c47f4b6-hqtqr         2/2     Running   0          2m10s
    currencyservice-59bc889674-jhk8z         2/2     Running   0          2m8s
    emailservice-5b9fff7cb8-8nqwz            2/2     Running   0          2m10s
    frontend-77b88cc7cb-mr4rp                2/2     Running   0          2m9s
    loadgenerator-6958f5bc8b-55q7w           2/2     Running   0          2m8s
    paymentservice-68dd9755bb-2jmb7          2/2     Running   0          2m9s
    productcatalogservice-84f95c95ff-c5kl6   2/2     Running   0          114s
    recommendationservice-64dc9dfbc8-xfs2t   2/2     Running   0          2m9s
    redis-cart-5b569cd47-cc2qd               2/2     Running   0          2m7s
    shippingservice-5488d5b6cb-lfhtt         2/2     Running   0          2m7s
    

    應用程式的所有 Pod 應都已啟動並執行,且 READY 欄中應顯示 2/2。這表示 Pod 已成功插入 Envoy 補充容器 Proxy。如果幾分鐘後仍未顯示 2/2,請參閱疑難排解指南

  2. 取得外部 IP,並將其設為變數:

    kubectl get services -n asm-ingress
    export FRONTEND_IP=$(kubectl --namespace asm-ingress \
    get service --output jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}' \
    )
    

    畫面會顯示類似以下內容的輸出:

    NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
    asm-ingressgateway   LoadBalancer   10.19.247.233   35.239.7.64   80:31380/TCP,443:31390/TCP,31400:31400/TCP   27m
    
    
  3. 在網路瀏覽器中前往 EXTERNAL-IP 位址。您應該會在瀏覽器中看到 Online Boutique 商店。

    線上精品店前端

建立 TestCurl Pod

建立 TestCurl Pod,傳送純文字流量以進行測試。

  apiVersion: v1
  kind: Pod
  metadata:
    name: testcurl
    namespace: default
    annotations:
      sidecar.istio.io/inject: "false"
  spec:
    containers:
    - name: curl
      image: curlimages/curl
      command: ["sleep", "600"]

存取 Online Boutique

  1. kubectl 的目前內容設為部署 Online Boutique 的叢集:

    gcloud container clusters get-credentials CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  2. 列出 frontend 命名空間中的服務:

    kubectl get services -n frontend
    

    請注意,frontend-externalLoadBalancer,而且具有外部 IP 位址。範例應用程式包含負載平衡器服務,因此可部署在 GKE 上,不必使用 Cloud Service Mesh。

  3. 在瀏覽器中,使用frontend-external服務的外部 IP 位址造訪應用程式:

    http://FRONTEND_EXTERNAL_IP/
    
  4. Cloud Service Mesh 可讓您部署 Ingress 閘道。您也可以使用 Ingress 閘道的外部 IP 位址存取 Online Boutique。取得閘道的外部 IP。將預留位置替換為下列資訊:

    • GATEWAY_SERVICE_NAME:Ingress 閘道服務的名稱。如果您部署範例閘道時未進行修改,或是部署預設 Ingress 閘道,名稱為 istio-ingressgateway
    • GATEWAY_NAMESPACE:部署 Ingress 閘道的命名空間。如果您部署的是預設輸入閘道,命名空間為 istio-system
    kubectl get service GATEWAY_NAME -n GATEWAY_NAMESPACE
    
  5. 在瀏覽器中開啟另一個分頁,然後使用 Ingress 閘道的外部 IP 位址造訪應用程式:

    http://INGRESS_GATEWAY_EXTERNAL_IP/
    
  6. 執行下列指令,從另一個 Pod curl frontend 服務 (使用純 HTTP)。由於服務位於不同命名空間,因此您需要使用 frontend 服務的 DNS 名稱執行 curl。

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    您的要求會成功,狀態為 200,因為系統預設會接受 TLS 和純文字流量。

為每個命名空間啟用相互 TLS

如要強制執行 mTLS,請套用含有 kubectlPeerAuthentication 政策。

  1. 將下列驗證政策儲存為 mtls-namespace.yaml

    cat <<EOF > mtls-namespace.yaml
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "namespace-policy"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    YAML 中的 mode: STRICT 行會將服務設為只接受 mTLS。根據預設,modePERMISSIVE,這會將服務設定為接受純文字和 mTLS。

  2. 套用驗證政策,將所有 Online Boutique 服務設為只接受 mTLS:

    for ns in ad cart checkout currency email frontend loadgenerator \
         payment product-catalog recommendation shipping; do
    kubectl apply -n $ns -f mtls-namespace.yaml
    done
    

    預期輸出內容:

    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created
    peerauthentication.security.istio.io/namespace-policy created

  3. 在瀏覽器中前往使用 frontend-external 服務外部 IP 位址存取 Online Boutique 的分頁:

    http://FRONTEND_EXTERNAL_IP/
    
  4. 重新整理頁面。瀏覽器會顯示下列錯誤:

    無法連上網站

    重新整理頁面會導致系統將純文字傳送至 frontend 服務。 由於STRICT驗證政策,Sidecar Proxy 會封鎖對服務的要求。

  5. 前往瀏覽器中透過 istio-ingressgateway 外部 IP 位址存取 Online Boutique 的分頁,然後重新整理頁面,應會顯示成功。使用 Ingress 閘道存取 Online Boutique 時,要求會經過下列路徑:

    雙向 mTLS

    mTLS 驗證流程:

    1. 瀏覽器會將純文字 HTTP 要求傳送至伺服器。
    2. Ingress 閘道 Proxy 容器會攔截要求。
    3. Ingress 閘道 Proxy 會與伺服器端 Proxy (本例中的前端服務) 執行 TLS 握手。這項握手程序包括交換憑證。Cloud Service Mesh 會將這些憑證預先載入 Proxy 容器。
    4. Ingress 閘道 Proxy 會對伺服器憑證執行安全命名檢查,確認伺服器是由授權身分執行。
    5. 連入閘道和伺服器 Proxy 會建立相互 TLS 連線,而伺服器 Proxy 會將要求轉送至伺服器應用程式容器 (前端服務)。
  6. 執行下列指令,從另一個 Pod curl frontend 服務 (使用純 HTTP)。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    由於我們從沒有 Sidecar 的工作負載傳送純文字流量,且套用了 STRICT peerAuthentication 政策,因此您的要求失敗。

尋找及刪除驗證政策

  1. 如要查看服務網格中的所有 PeerAuthentication 政策,請執行下列指令:

    kubectl get peerauthentication --all-namespaces
    

    輸出結果會與下列內容相似:

    NAMESPACE         NAME               MODE     AGE
    ad                namespace-policy   STRICT   17m
    cart              namespace-policy   STRICT   17m
    checkout          namespace-policy   STRICT   17m
    currency          namespace-policy   STRICT   17m
    email             namespace-policy   STRICT   17m
    frontend          namespace-policy   STRICT   17m
    loadgenerator     namespace-policy   STRICT   17m
    payment           namespace-policy   STRICT   17m
    product-catalog   namespace-policy   STRICT   17m
    recommendation    namespace-policy   STRICT   17m
    shipping          namespace-policy   STRICT   17m
    
  2. 從所有 Online Boutique 命名空間中刪除驗證政策:

    for ns in ad cart checkout currency email frontend loadgenerator payment \
      product-catalog recommendation shipping; do
        kubectl delete peerauthentication -n $ns namespace-policy
    done;
    

    預期輸出內容:

    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    peerauthentication.security.istio.io "namespace-policy" deleted
    
  3. 使用 frontend-external 服務的外部 IP 位址存取 Online Boutique,然後重新整理頁面。頁面會如預期顯示。

  4. 執行下列指令,從另一個 Pod curl frontend 服務 (使用純 HTTP)。

    kubectl debug --image istio/base --target istio-proxy -it \
      $(kubectl get pod -l app=productcatalogservice -n product-catalog -o jsonpath={.items..metadata.name}) \
      -n product-catalog -- \
      curl http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    您的要求會成功,狀態為 200,因為系統預設會接受 TLS 和純文字流量。

在 Google Cloud 控制台中重新整理顯示「工作負載」清單的頁面,現在會顯示 mTLS 狀態為 Permissive

為每個工作負載啟用雙向 TLS

如要為特定工作負載設定 PeerAuthentication 政策,您必須設定 selector 區段,並指定與所需工作負載相符的標籤。不過,Cloud Service Mesh 無法彙整工作負載層級的政策,以供服務的外送 mTLS 流量使用。您需要設定目的地規則來管理這項行為。

  1. 將驗證政策套用至特定工作負載。請注意,下列政策如何使用標籤和選取器,指定特定frontend部署作業。

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "frontend"
      namespace: "frontend"
    spec:
      selector:
        matchLabels:
          app: frontend
      mtls:
        mode: STRICT
    EOF
    

    預期輸出內容:

    peerauthentication.security.istio.io/frontend created
  2. 設定相符目的地規則。

    cat <<EOF | kubectl apply -n frontend -f -
    apiVersion: "networking.istio.io/v1alpha3"
    kind: "DestinationRule"
    metadata:
      name: "frontend"
    spec:
      host: "frontend.demo.svc.cluster.local"
      trafficPolicy:
        tls:
          mode: ISTIO_MUTUAL
    EOF
    

    預期輸出內容:

    destinationrule.networking.istio.io/frontend created
  3. 使用 frontend-external 服務的外部 IP 位址存取 Online Boutique,然後重新整理頁面。由於 frontend service 設為 STRICT mTLS,且 Sidecar Proxy 封鎖要求,因此網頁不會顯示。

  4. 執行下列指令,從另一個 Pod curl frontend 服務 (使用純 HTTP)。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    由於我們從沒有 Sidecar 的工作負載傳送純文字流量,且套用了 STRICT peerAuthentication 政策,因此您的要求失敗。

  5. 刪除驗證政策:

    kubectl delete peerauthentication -n frontend frontend
    

    預期輸出內容:

    peerauthentication.security.istio.io "frontend" deleted
    
  6. 刪除目的地規則:

    kubectl delete destinationrule -n frontend frontend
    

    預期輸出內容:

    destinationrule.networking.istio.io "frontend" deleted
    

強制執行網格範圍的 mTLS

如要防止網格中的所有服務接受純文字流量,請設定網格範圍的 PeerAuthentication 政策,並將 mTLS 模式設為 STRICT。網格範圍的 PeerAuthentication 政策不應有選取器,且必須套用至根命名空間 istio-system。部署政策後,控制層會自動佈建 TLS 憑證,讓工作負載彼此驗證。

  1. 強制執行網格範圍的 mTLS:

    kubectl apply -f - <<EOF
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "mesh-wide"
      namespace: "istio-system"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    預期輸出內容:

    peerauthentication.security.istio.io/mesh-wide created

  2. 使用 frontend-external 服務的外部 IP 位址存取 Online Boutique,然後重新整理頁面。網頁不會顯示。

  3. 執行下列指令,從另一個 Pod curl frontend 服務 (使用純 HTTP)。

    kubectl exec testcurl -n default -- curl \
      http://frontend.frontend.svc.cluster.local:80/ -o /dev/null -s -w '%{http_code}\n'
    

    由於我們從沒有 Sidecar 的工作負載傳送純文字流量,且套用了 STRICT peerAuthentication 政策,因此您的要求失敗。

  4. 刪除 mesh-wide 政策:

    kubectl delete peerauthentication -n istio-system mesh-wide
    

    預期輸出內容:

    peerauthentication.security.istio.io "mesh-wide" deleted
    

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,請刪除含有該項資源的專案,或者保留專案但刪除個別資源。

  • 如要避免產生額外費用,請刪除叢集:

    gcloud container clusters delete  CLUSTER_NAME  \
        --project=PROJECT_ID \
        --zone=CLUSTER_LOCATION 
    
  • 如要保留叢集並移除 Online Boutique 範例,請按照下列步驟操作:

    1. 刪除應用程式命名空間:
      kubectl delete -f online-boutique/kubernetes-manifests/namespaces
    

    預期輸出內容:

    namespace "ad" deleted
    namespace "cart" deleted
    namespace "checkout" deleted
    namespace "currency" deleted
    namespace "email" deleted
    namespace "frontend" deleted
    namespace "loadgenerator" deleted
    namespace "payment" deleted
    namespace "product-catalog" deleted
    namespace "recommendation" deleted
    namespace "shipping" deleted
    
    1. 刪除服務項目:
      kubectl delete -f online-boutique/istio-manifests/allow-egress-googleapis.yaml
    

    預期輸出內容:

    serviceentry.networking.istio.io "allow-egress-googleapis" deleted
    serviceentry.networking.istio.io "allow-egress-google-metadata" deleted
    

後續步驟