從 IBM Cloud Kubernetes Service Ingress 到 Istio Ingress Gateway 的直接加密流量
設定 IBM Cloud Kubernetes Service 應用程式負載平衡器,以使用相互 TLS 將流量導向 Istio Ingress Gateway。
在這篇部落格文章中,我將展示如何在 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 A
和 service B
。service 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 網域。
初始設定
建立
httptools
命名空間並啟用 Istio Sidecar 注入$ kubectl create namespace httptools $ kubectl label namespace httptools istio-injection=enabled namespace/httptools created namespace/httptools labeled
將
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 來交換這些憑證,以信任彼此的憑證,並使用它們的私鑰來加密和簽署流量。
將叢集的名稱儲存在
CLUSTER_NAME
環境變數中$ export CLUSTER_NAME=<your cluster name>
將 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>
將
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
為 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>
將先前命令中的網域名稱儲存在環境變數中
$ export INGRESS_GATEWAY_DOMAIN=<the domain from the previous command>
列出已註冊的網域名稱
$ 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
)。儲存新網域名稱的密鑰名稱
$ export INGRESS_GATEWAY_SECRET=<the secret's name as shown in the SSL Cert Secret Name column>
從為 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
下載 Let's Encrypt 憑證的簽發者憑證,這是 IKS 提供的憑證的簽發者。您將此憑證指定為要信任的憑證授權單位憑證,適用於 ALB 和 Istio Ingress Gateway。
$ curl https://letsencrypt.dev.org.tw/certs/trustid-x3-root.pem --output trusted.crt
建立要由 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
對於相互 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 提供給您的憑證和金鑰。
定義一個
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
設定透過
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
透過 curl 向
httpbin
發送請求,並將用戶端憑證 (--cert
選項) 和私鑰 (--key
選項) 作為參數傳遞$ curl https://$INGRESS_GATEWAY_DOMAIN/status/418 --cert alb_certs/client.crt --key alb_certs/client.key -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`
移除包含 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 所需的憑證。
設定 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
測試 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 驗證。
清理
刪除
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
關閉
httpbin
服務$ kubectl delete -f @samples/httpbin/httpbin.yaml@ -n httptools
刪除
httptools
命名空間$ kubectl delete namespace httptools