從 IBM Cloud Kubernetes Service Ingress 到 Istio Ingress Gateway 的直接加密流量

設定 IBM Cloud Kubernetes Service 應用程式負載平衡器,以使用相互 TLS 將流量導向 Istio Ingress Gateway。

2020 年 5 月 15 日 | 作者:Vadim Eisenberg - IBM

在這篇部落格文章中,我將展示如何在 IBM Cloud Kubernetes Service (IKS) 上設定 Ingress 應用程式負載平衡器 (ALB),以將流量導向 Istio Ingress Gateway,同時使用 相互 TLS 驗證來保護它們之間的流量。

當您在沒有 Istio 的情況下使用 IKS 時,您可以使用提供的 ALB 來控制您的 Ingress 流量。此 Ingress 流量路由是使用 Kubernetes Ingress 資源與 ALB 特定註解來設定的。IKS 提供 DNS 網域名稱、符合該網域的 TLS 憑證,以及該憑證的私鑰。IKS 將憑證和私鑰儲存在 Kubernetes 密鑰中。

當您開始在 IKS 叢集中使用 Istio 時,將流量傳送到已啟用 Istio 的工作負載的建議方法是使用 Istio Ingress Gateway,而不是使用 Kubernetes Ingress。使用 Istio Ingress Gateway 的主要原因之一是,當您啟用 STRICT 相互 TLS 時,IKS 提供的 ALB 將無法直接與網格內的服務通訊。在您轉換為僅將 Istio Ingress Gateway 作為主要進入點的過程中,您可以繼續將傳統 Ingress 用於非 Istio 服務,同時將 Istio Ingress Gateway 用於屬於網格的服務。

IKS 提供了一種方便的方法,讓客戶可以透過 IKS 命令為 Istio Gateway 的 IP 註冊新的 DNS 子網域,來存取 Istio Ingress Gateway。網域的格式如下:<叢集名稱>-<全域唯一帳戶雜湊>-0001.<區域>.containers.appdomain.cloud,例如 mycluster-a1b2cdef345678g9hi012j3kl4567890-0001.us-south.containers.appdomain.cloud。與 ALB 網域的方式相同,IKS 提供憑證和私鑰,並將它們儲存在另一個 Kubernetes 密鑰中。

這篇部落格文章說明如何將 IKS Ingress ALB 和 Istio Ingress Gateway 串聯在一起,以將流量傳送到已啟用 Istio 的工作負載,同時能夠繼續使用 ALB 的特定功能和 ALB 子網域名稱。您設定 IKS Ingress ALB,以透過 Istio Ingress Gateway 將流量導向 Istio 服務網格內的服務,同時在 ALB 和 Gateway 之間使用相互 TLS 驗證。對於相互 TLS 驗證,您將設定 ALB 和 Istio Ingress Gateway,以使用 IKS 為 ALB 和 NLB 子網域提供的憑證和金鑰。使用 IKS 提供的憑證可省去您管理 ALB 和 Istio Ingress Gateway 之間連線的自有憑證的負擔。

您將使用 NLB 子網域憑證作為 Istio Ingress Gateway 的伺服器憑證,這是預期的用途。NLB 子網域憑證代表提供特定 NLB 子網域的伺服器身分,在此案例中,即為 Ingress Gateway。

您將在 ALB 和 Istio Ingress 之間的相互 TLS 驗證中使用 ALB 子網域憑證作為用戶端憑證。當 ALB 作為伺服器時,它會向用戶端提供 ALB 憑證,以便用戶端可以驗證 ALB。當 ALB 作為 Istio Ingress Gateway 的用戶端時,它會向 Istio Ingress Gateway 提供相同的憑證,以便 Istio Ingress Gateway 可以驗證 ALB。

沒有 Istio Sidecar 的服務流量可以繼續像以前一樣直接從 ALB 流動。

下圖說明了所述設定。它顯示叢集中的兩個服務,service Aservice Bservice A 已注入 Istio Sidecar,並且需要相互 TLS。service B 沒有 Istio Sidecar。用戶端可以透過直接與 service B 通訊的 ALB 來存取 service B。用戶端也可以透過 ALB 來存取 service A,但在這種情況下,流量必須通過 Istio Ingress Gateway。ALB 和 Gateway 之間的相互 TLS 驗證是以 IKS 提供的憑證為基礎。用戶端也可以直接存取 Istio Ingress Gateway。IKS 為 ALB 和 Ingress Gateway 註冊不同的 DNS 網域。

A cluster with the ALB and the Istio ingress gateway
具有 ALB 和 Istio Ingress Gateway 的叢集

初始設定

  1. 建立 httptools 命名空間並啟用 Istio Sidecar 注入

    $ kubectl create namespace httptools
    $ kubectl label namespace httptools istio-injection=enabled
    namespace/httptools created
    namespace/httptools labeled
    
  2. httpbin 範例部署到 httptools

    壓縮
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n httptools
    service/httpbin created
    deployment.apps/httpbin created
    

為 ALB 和 Istio Ingress Gateway 建立密鑰

當您使用 ibmcloud ks nlb-dns-create 命令為外部 IP 註冊 DNS 網域時,IKS 會產生 TLS 憑證和私鑰,並將它們作為預設命名空間中的密鑰儲存。IKS 也會將 ALB 的憑證和私鑰作為預設命名空間中的密鑰儲存。您需要這些認證來建立 ALB 和 Istio Ingress Gateway 在它們之間進行相互 TLS 驗證時將呈現的身分。您將設定 ALB 和 Istio Ingress Gateway 來交換這些憑證,以信任彼此的憑證,並使用它們的私鑰來加密和簽署流量。

  1. 將叢集的名稱儲存在 CLUSTER_NAME 環境變數中

    $ export CLUSTER_NAME=<your cluster name>
    
  2. 將 ALB 的網域名稱儲存在 ALB_INGRESS_DOMAIN 環境變數中

    $ ibmcloud ks cluster get --cluster $CLUSTER_NAME | grep Ingress
    Ingress Subdomain:              <your ALB ingress domain>
    Ingress Secret:                 <your ALB secret>
    
    $ export ALB_INGRESS_DOMAIN=<your ALB ingress domain>
    $ export ALB_SECRET=<your ALB secret>
    
  3. istio-ingressgateway 服務的外部 IP 儲存在環境變數中。

    $ export INGRESS_GATEWAY_IP=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    $ echo INGRESS_GATEWAY_IP = $INGRESS_GATEWAY_IP
    
  4. 為 Istio Ingress Gateway 服務的 IP 建立 DNS 網域和憑證

    $ ibmcloud ks nlb-dns create classic --cluster $CLUSTER_NAME --ip $INGRESS_GATEWAY_IP --secret-namespace istio-system
    Host name subdomain is created as <some domain>
    
  5. 將先前命令中的網域名稱儲存在環境變數中

    $ export INGRESS_GATEWAY_DOMAIN=<the domain from the previous command>
    
  6. 列出已註冊的網域名稱

    $ ibmcloud ks nlb-dnss --cluster $CLUSTER_NAME
    Retrieving host names, certificates, IPs, and health check monitors for network load balancer (NLB) pods in cluster <your cluster>...
    OK
    Hostname                          IP(s)                       Health Monitor   SSL Cert Status   SSL Cert Secret Name                          Secret Namespace
    <your ingress gateway hostname>   <your ingress gateway IP>   None             created           <the matching secret name>           istio-system
    ...
    

    等待新網域名稱的憑證狀態(第四個欄位)變成 enabled(最初為 pending)。

  7. 儲存新網域名稱的密鑰名稱

    $ export INGRESS_GATEWAY_SECRET=<the secret's name as shown in the SSL Cert Secret Name column>
    
  8. 從為 ALB 提供的密鑰中擷取憑證和金鑰

    $ mkdir alb_certs
    $ kubectl get secret $ALB_SECRET --namespace=default -o yaml | grep 'tls.key:' | cut -f2 -d: | base64 --decode > alb_certs/client.key
    $ kubectl get secret $ALB_SECRET --namespace=default -o yaml | grep 'tls.crt:' | cut -f2 -d: | base64 --decode > alb_certs/client.crt
    $ ls -al alb_certs
    -rw-r--r--   1 user  staff  3738 Sep 11 07:57 client.crt
    -rw-r--r--   1 user  staff  1675 Sep 11 07:57 client.key
    
  9. 下載 Let's Encrypt 憑證的簽發者憑證,這是 IKS 提供的憑證的簽發者。您將此憑證指定為要信任的憑證授權單位憑證,適用於 ALB 和 Istio Ingress Gateway。

    $ curl https://letsencrypt.dev.org.tw/certs/trustid-x3-root.pem --output trusted.crt
    
  10. 建立要由 ALB 用於建立相互 TLS 連線的 Kubernetes 密鑰。

    $ kubectl create secret generic alb-certs -n istio-system --from-file=trusted.crt --from-file=alb_certs/client.crt --from-file=alb_certs/client.key
    secret "alb-certs" created
    
  11. 對於相互 TLS,Ingress Gateway 需要一個單獨名為 <tls-cert-secret>-cacert 的密鑰,其中包含 cacert 金鑰。

    $ kubectl create -n istio-system secret generic $INGRESS_GATEWAY_SECRET-cacert --from-file=ca.crt=trusted.crt
    secret/cluster_name-hash-XXXX-cacert created
    

設定相互 TLS Ingress Gateway

在本節中,您設定 Istio Ingress Gateway 來執行外部用戶端與 Gateway 之間的相互 TLS。您使用為 Ingress Gateway 和 ALB 提供給您的憑證和金鑰。

  1. 定義一個 Gateway,只允許在連接埠 443 上進行存取,並使用相互 TLS

    $ kubectl apply -n httptools -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: default-ingress-gateway
    spec:
      selector:
        istio: ingressgateway # use istio default ingress gateway
      servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        tls:
          mode: MUTUAL
          credentialName: $INGRESS_GATEWAY_SECRET
        hosts:
        - "$INGRESS_GATEWAY_DOMAIN"
        - "httpbin.$ALB_INGRESS_DOMAIN"
    EOF
    
  2. 設定透過 Gateway 進入的流量的路由

    $ kubectl apply -n httptools -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: default-ingress
    spec:
      hosts:
      - "$INGRESS_GATEWAY_DOMAIN"
      - "httpbin.$ALB_INGRESS_DOMAIN"
      gateways:
      - default-ingress-gateway
      http:
      - match:
        - uri:
            prefix: /status
        route:
        - destination:
            port:
              number: 8000
            host: httpbin.httptools.svc.cluster.local
    EOF
    
  3. 透過 curlhttpbin 發送請求,並將用戶端憑證 (--cert 選項) 和私鑰 (--key 選項) 作為參數傳遞

    $ curl https://$INGRESS_GATEWAY_DOMAIN/status/418 --cert alb_certs/client.crt  --key alb_certs/client.key
    
    -=[ teapot ]=-
    
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
    
  4. 移除包含 ALB 和 Ingress Gateway 憑證和金鑰的目錄。

    $ rm -r alb_certs trusted.crt
    

設定 ALB

您需要設定 Ingress 資源,以在使用儲存在 alb-certs 密鑰中的憑證時,將流量導向 Istio Ingress Gateway。通常,ALB 會先解密 HTTPS 請求,然後將流量轉送至您的應用程式。您可以設定 ALB,以使用 Ingress 資源上的 ssl-services 註解,在將流量轉送至 Istio Ingress Gateway 之前重新加密流量。此註解也允許您指定儲存在 alb-certs 密鑰中的憑證,這是相互 TLS 所需的憑證。

  1. 設定 ALB 的 Ingress 資源。您必須在 istio-system 命名空間中建立 Ingress 資源,才能將流量轉送至 Istio Ingress Gateway。

    $ kubectl apply -f - <<EOF
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: alb-ingress
      namespace: istio-system
      annotations:
        ingress.bluemix.net/ssl-services: "ssl-service=istio-ingressgateway ssl-secret=alb-certs proxy-ssl-name=$INGRESS_GATEWAY_DOMAIN"
    spec:
      tls:
      - hosts:
        - httpbin.$ALB_INGRESS_DOMAIN
        secretName: $ALB_SECRET
      rules:
      - host: httpbin.$ALB_INGRESS_DOMAIN
        http:
          paths:
          - path: /status
            backend:
              serviceName: istio-ingressgateway
              servicePort: 443
    EOF
    
  2. 測試 ALB Ingress

    $ curl https://httpbin.$ALB_INGRESS_DOMAIN/status/418
    
    -=[ teapot ]=-
    
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
    

恭喜!您已設定 IKS Ingress ALB 以將加密流量傳送至 Istio Ingress Gateway。您已為 Istio Ingress Gateway 配置主機名稱和憑證,並將該憑證用作 Istio Ingress Gateway 的伺服器憑證。您使用 IKS 為 ALB 提供的憑證作為 ALB 的用戶端憑證。將憑證部署為 Kubernetes 密鑰後,您將來自 ALB 的 Ingress 流量導向至某些特定路徑的 Istio Ingress Gateway,並使用這些憑證在 ALB 和 Istio Ingress Gateway 之間進行相互 TLS 驗證。

清理

  1. 刪除 Gateway 設定、VirtualService 和密鑰

    $ kubectl delete ingress alb-ingress -n istio-system
    $ kubectl delete virtualservice default-ingress -n httptools
    $ kubectl delete gateway default-ingress-gateway -n httptools
    $ kubectl delete secrets alb-certs -n istio-system
    $ rm -rf alb_certs trusted.crt
    $ unset CLUSTER_NAME ALB_INGRESS_DOMAIN ALB_SECRET INGRESS_GATEWAY_DOMAIN INGRESS_GATEWAY_SECRET
    
  2. 關閉 httpbin 服務

    壓縮
    $ kubectl delete -f @samples/httpbin/httpbin.yaml@ -n httptools
    
  3. 刪除 httptools 命名空間

    $ kubectl delete namespace httptools
    
分享這篇文章