使用 Ingress 在 HTTPS 負載平衡中使用多個 SSL 憑證


本頁面說明如何在 Google Kubernetes Engine (GKE) 叢集中,為 Ingress 資源設定多個 SSL 憑證。

總覽

如果要接受來自用戶端的 HTTPS 要求,應用程式負載平衡器必須要有憑證,以向用戶端證明其身分。負載平衡器還必須要有私密金鑰才能完成 HTTPS 握手。

當負載平衡器接受來自用戶端的 HTTPS 要求時,用戶端和負載平衡器之間的流量將會使用 TLS 加密。不過負載平衡器會終止 TLS 加密,並將要求在不加密的情況下轉給應用程式。透過 Ingress 設定應用程式負載平衡器時,可以設定負載平衡器向用戶端顯示最多 10 個 TLS 憑證。

負載平衡器會使用伺服器名稱指示 (SNI),依據 TLS 握手中的網域名稱判斷要向客戶呈現哪些憑證。若用戶端不使用 SNI,或者用戶端使用的網域名稱與其中一個憑證的常用名稱 (CN) 名稱不符,負載平衡器就會使用 Ingress 中列出的第一個憑證。

下圖顯示負載平衡器如何根據要求中使用的網域名稱,將流量傳送至不同後端:

「使用 Ingress 設定多個 SSL 憑證」系統圖表

您可以透過下列方法,為應用程式負載平衡器提供 SSL 憑證:

  • Google 代管的 SSL 憑證。 如需相關操作方式,請參閱代管憑證頁面。

  • Google Cloud 您自行管理的 SSL 憑證。SSL 憑證會使用您上傳到 Google Cloud 專案的預先共用憑證。

  • Kubernetes 密鑰。 密鑰包含您自行建立的憑證和金鑰。將 Secret 名稱新增至 Ingress 資訊清單的 tls 欄位。

您可以在同一個 Ingress 中使用多個方法,如此一來,在方法間進行遷移時就不會出現停機時間。

縱觀全局

以下概要說明本文中的步驟:

  1. 建立 Deployment。

  2. 建立 Service。

  3. 建立兩個憑證檔案和兩個金鑰檔案,或兩個 ManagedCertificate 物件。您必須在與負載平衡器部署位置相同的專案和命名空間中,設定這些憑證。

  4. 建立使用密鑰或預先共用憑證的 Ingress。 建立 Ingress 時,GKE 會建立並設定應用程式負載平衡器。

  5. 測試應用程式負載平衡器。

事前準備

開始之前,請確認你已完成下列工作:

  • 啟用 Google Kubernetes Engine API。
  • 啟用 Google Kubernetes Engine API
  • 如要使用 Google Cloud CLI 執行這項工作,請安裝初始化 gcloud CLI。如果您先前已安裝 gcloud CLI,請執行 gcloud components update,取得最新版本。
  • 您必須擁有兩個網域名稱。網域名稱的長度不得超過 63 個字元。

限制

  • Google 代管憑證僅適用於使用外部應用程式負載平衡器的 GKE Ingress。Google 代管憑證不支援第三方 Ingress 控制器。

  • 如果是內部應用程式負載平衡器,您必須在 Ingress 資訊清單中停用 HTTP。外部負載平衡器則不必執行這項操作。

  • 您不得手動變更或更新應用程式負載平衡器的設定。也就是說,您不得編輯任何負載平衡器的元件,包括目標 proxy、網址對應和後端服務。您所做的任何變更都將被 GKE 覆寫。

可建立部署作業

  1. 將下列資訊清單儲存為 my-mc-deployment.yaml

    apiVersion: apps/v1 kind: Deployment metadata:   name: my-mc-deployment spec:   selector:     matchLabels:       app: products       department: sales   replicas: 3   template:     metadata:       labels:         app: products         department: sales     spec:       containers:       - name: hello         image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0"         env:         - name: "PORT"           value: "50001"       - name: hello-again         image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0"         env:         - name: "PORT"           value: "50002" 

    這個資訊清單說明含有三個 Pod 的部署。每個 Pod 都有兩個容器。其中一個容器會執行 hello-app:1.0 並監聽 TCP 通訊埠 50001。另一個容器會執行 hello-app:2.0 並監聽 TCP 通訊埠 50002。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-deployment.yaml 

建立 Service

  1. 將下列資訊清單儲存為 my-mc-service.yaml

    apiVersion: v1 kind: Service metadata:   name: my-mc-service spec:   type: NodePort   selector:     app: products     department: sales   ports:   - name: my-first-port     protocol: TCP     port: 60001     targetPort: 50001   - name: my-second-port     protocol: TCP     port: 60002     targetPort: 50002 

    這份資訊清單說明瞭具有下列欄位的 Service:

    • selector:指定同時具有 app: products 標籤和 department: sales 標籤的任何 Pod,都是這個 Service 的成員。
    • ports:指定當用戶端將要求傳送至 my-first-port 上的 Service 時,GKE 會將要求轉送至通訊埠 50001 上的其中一個成員 Pod。當用戶端傳送要求到 my-second-port 上的 Service 時,GKE 會將要求轉送到通訊埠 50002 上的其中一個成員 Pod。
  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-service.yaml 

建立憑證和金鑰

若要進行此頁面的練習,您要有兩個憑證,每個憑證都有對應的金鑰。每一個憑證都必須有一個與您所擁有的網域相等的共用名稱 (CN)。

您可以手動建立這些憑證,也可以使用 Google 代管的憑證。

若您已有具備適當的「共用名稱」值的兩個憑證檔案,可直接前往下一節。

使用者管理的憑證

  1. 建立第一個金鑰:

    openssl genrsa -out test-ingress-1.key 2048 
  2. 建立第一個憑證簽署要求:

    openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \     -subj "/CN=FIRST_DOMAIN" 

    FIRST_DOMAIN 替換為您擁有的網域名稱,例如 example.com

  3. 建立第一個憑證:

    openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \     -out test-ingress-1.crt 
  4. 建立第二個金鑰:

    openssl genrsa -out test-ingress-2.key 2048 
  5. 建立第二個憑證簽署要求:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \     -subj "/CN=SECOND_DOMAIN" 

    SECOND_DOMAIN 替換為您擁有的其他網域名稱,例如 examplepetstore.com

  6. 建立第二個憑證:

    openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \     -out test-ingress-2.crt 

如要進一步瞭解憑證和金鑰,請參閱「安全資料傳輸層 (SSL) 憑證總覽」。

您現在有兩個憑證檔案和兩個金鑰檔案。

其餘工作會使用下列預留位置來參照您的網域、憑證和金鑰:

  • FIRST_CERT_FILE:第一個憑證檔案的路徑。
  • FIRST_KEY_FILE:與第一個憑證搭配使用的金鑰檔案路徑。
  • FIRST_DOMAIN:您擁有的網域名稱。
  • FIRST_SECRET_NAME:包含第一個憑證和金鑰的 Secret 名稱。
  • SECOND_CERT_FILE:第二個憑證檔案的路徑。
  • SECOND_KEY_FILE:與第二個憑證搭配使用的金鑰檔案路徑。
  • SECOND_DOMAIN:您擁有的第二個網域名稱。
  • SECOND_SECRET_NAME:包含第二個憑證和金鑰的 Secret 名稱。

Google 代管的憑證

如要建立 Google 代管的憑證,您必須將 ManagedCertificate 物件新增至 Ingress 的命名空間。您可以使用下列範本,為網域定義憑證:

  apiVersion: networking.gke.io/v1   kind: ManagedCertificate   metadata:     name: FIRST_CERT_NAME   spec:     domains:       - FIRST_DOMAIN   ---   apiVersion: networking.gke.io/v1   kind: ManagedCertificate   metadata:     name: SECOND_CERT_NAME   spec:     domains:       - SECOND_DOMAIN 

更改下列內容:

  • FIRST_CERT_NAME:第一個 ManagedCertificate 物件的名稱。
  • FIRST_DOMAIN:您擁有的第一個網域。
  • SECOND_CERT_NAME:第二個 ManagedCertificate 物件的名稱。
  • SECOND_DOMAIN:您擁有的第二個網域。

ManagedCertificate 物件的名稱與實際建立的憑證名稱不同。您只需要知道 ManagedCertificate 物件的名稱,即可在 Ingress 中使用這些物件。

指定 Ingress 的憑證

下一個步驟是建立 Ingress 物件。在您的 Ingress 資訊清單中,可以使用下列其中一種方法來提供負載平衡器的憑證:

  • 密鑰
  • 預先共用的憑證
  • Google 代管憑證

密鑰

  1. 建立包含第一個憑證和金鑰的「密鑰」:

    kubectl create secret tls FIRST_SECRET_NAME \     --cert=FIRST_CERT_FILE \     --key=FIRST_KEY_FILE 
  2. 建立包含第二個憑證和金鑰的「密鑰」:

    kubectl create secret tls SECOND_SECRET_NAME \     --cert=SECOND_CERT_FILE \     --key=SECOND_KEY_FILE 

建立 Ingress

  1. 將下列資訊清單儲存為 my-mc-ingress.yaml

    apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   name: my-mc-ingress spec:   tls:   - secretName: FIRST_SECRET_NAME   - secretName: SECOND_SECRET_NAME   rules:   - host: FIRST_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60001   - host: SECOND_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60002 

    FIRST_DOMAINSECOND_DOMAIN 替換為您擁有的網域名稱,例如 example.comexamplepetstore.com

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-ingress.yaml 
  3. 說明您的 Ingress:

    kubectl describe ingress my-mc-ingress 

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

    Name: my-mc-ingress Address: 203.0.113.1 ... TLS:   FIRST_SECRET_NAME terminates   SECOND_SECRET_NAME terminates Rules:   Host              Path  Backends   ----              ----  --------   FIRST_DOMAIN                       my-mc-service:my-first-port (<none>)   SECOND_DOMAIN                       my-mc-service:my-second-port (<none>) Annotations: ... Events:   Type    Reason  Age   From                     Message   ----    ------  ----  ----                     -------   Normal  ADD     3m    loadbalancer-controller  default/my-mc-ingress   Normal  CREATE  2m    loadbalancer-controller  ip: 203.0.113.1 

    此結果顯示有兩個密鑰與 Ingress 關聯。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候幾分鐘,然後再次嘗試執行指令。

Pre-shared certs (預先共用的憑證)

  1. 建立憑證:

    gcloud compute ssl-certificates create FIRST_CERT_NAME \     --certificate=FIRST_CERT_FILE \     --private-key=FIRST_KEY_FILE 

    更改下列內容:

    • FIRST_CERT_NAME:第一個憑證的名稱。
    • FIRST_CERT_FILE:您的第一個憑證檔案
    • FIRST_KEY_FILE:您的第一個金鑰檔案。
  2. 建立第二個憑證:

    gcloud compute ssl-certificates create SECOND_CERT_NAME \     --certificate=SECOND_CERT_FILE \     --private-key=SECOND_KEY_FILE 

    更改下列內容:

    • SECOND_CERT_NAME:第二個憑證的名稱。
    • SECOND_CERT_FILE:第二個憑證檔案。
    • SECOND_KEY_FILE:第二個金鑰檔案。
  3. 查看您的憑證資源:

    gcloud compute ssl-certificates list 

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

    NAME                   CREATION_TIMESTAMP FIRST_CERT_NAME      2018-11-03T12:08:47.751-07:00 SECOND_CERT_NAME     2018-11-03T12:09:25.359-07:00 

建立 Ingress

  1. 將下列資訊清單儲存為 my-psc-ingress.yaml

    apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   name: my-psc-ingress   annotations:     ingress.gcp.kubernetes.io/pre-shared-cert: "FIRST_CERT_NAME,SECOND_CERT_NAME" spec:   rules:   - host: FIRST_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60001   - host: SECOND_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60002 

    FIRST_DOMAINSECOND_DOMAIN 替換成您的網域名稱。

    這份資訊清單說明 Ingress,在註解中列出預先共用的憑證資源。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-psc-ingress.yaml 
  3. 說明您的 Ingress:

    kubectl describe ingress my-psc-ingress 

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

    Name:             my-psc-ingress Address:          203.0.113.2 ... Rules:   Host              Path  Backends   ----              ----  --------   FIRST_DOMAIN                       my-mc-service:my-first-port (<none>)   SECOND_DOMAIN                       my-mc-service:my-second-port (<none>) Annotations:   ...   ingress.gcp.kubernetes.io/pre-shared-cert:    FIRST_CERT_NAME,SECOND_CERT_NAME   ...   ingress.kubernetes.io/ssl-cert:               FIRST_CERT_NAME,SECOND_CERT_NAME Events:   Type    Reason  Age   From                     Message   ----    ------  ----  ----                     -------   Normal  ADD     2m    loadbalancer-controller  default/my-psc-ingress   Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2 

    從此結果顯示 Ingress 與名為 FIRST_CERT_NAMESECOND_CERT_NAME 的預先共用憑證有關聯。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候幾分鐘,然後再次嘗試執行指令。

Google 代管的憑證

建立 Ingress

  1. 將下列資訊清單儲存為 my-gmc-ingress.yaml

    apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   name: my-gmc-ingress   annotations:     networking.gke.io/managed-certificates: "FIRST_CERT_NAME,SECOND_CERT_NAME" spec:   rules:   - host: FIRST_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60001   - host: SECOND_DOMAIN     http:       paths:       - pathType: ImplementationSpecific         backend:           service:             name: my-mc-service             port:               number: 60002 

    FIRST_DOMAINSECOND_DOMAIN 替換成您的網域名稱。

    這份資訊清單說明 Ingress,在註解中列出預先共用的憑證資源。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-gmc-ingress.yaml 
  3. 說明您的 Ingress:

    kubectl describe ingress my-gmc-ingress 

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

    Name:             my-gmc-ingress Address:          203.0.113.2 ... Rules:   Host              Path  Backends   ----              ----  --------   FIRST_DOMAIN                       my-mc-service:my-first-port (<none>)   SECOND_DOMAIN                       my-mc-service:my-second-port (<none>) Annotations:   ...   ingress.gcp.kubernetes.io/pre-shared-cert:    mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4   ...   ingress.kubernetes.io/ssl-cert:               mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4   networking.gke.io/managed-certificates:       FIRST_CERT_NAME,SECOND_CERT_NAME Events:   Type    Reason  Age   From                     Message   ----    ------  ----  ----                     -------   Normal  ADD     2m    loadbalancer-controller  default/my-gmc-ingress   Normal  CREATE  1m    loadbalancer-controller  ip: 203.0.113.2 

    從此結果顯示 Ingress 與名為 FIRST_CERT_NAMESECOND_CERT_NAME 的代管憑證有關聯。GKE 會自動使用您透過 ManagedCertificate 物件建立的 Google 代管憑證,填入 ingress.gcp.kubernetes.io/pre-shared-certingress.kubernetes.io/ssl-cert 註解。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候片刻,然後再次嘗試執行指令。

測試負載平衡器

請稍候五分鐘,等待 GKE 設定負載平衡器完成。

如果您使用 Google 代管的憑證,系統需要佈建憑證並驗證指定網域的 DNS 設定,因此完成設定的時間可能會大幅延長。

如要測試負載平衡器,您必須擁有兩個網域名稱,且這兩個網域名稱必須解析外部應用程式負載平衡器的外部 IP 位址。

  1. 使用您的第一個網域名稱傳送要求至負載平衡器:

    curl -v https://FIRST_DOMAIN 

    您可能需要使用 curl -k 選項執行不安全的 SSL 傳輸,這樣 curl 才會接受自行簽署的憑證。

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

    ... *   Trying 203.0.113.1... ... * Connected to FIRST_DOMAIN (203.0.113.1) port 443 (#0) ... * TLSv1.2 (IN), TLS handshake, Certificate (11): ... * Server certificate: *  subject: CN=FIRST_DOMAIN ... > Host: FIRST_DOMAIN.com ... Hello, world! Version: 1.0.0 ... 

    結果顯示在 TLS 握手使用您的第一個憑證。

  2. 使用第二個網域名稱,將要求傳送至負載平衡器。

    curl -v https://SECOND_DOMAIN 

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

    ... *   Trying 203.0.113.1... ... * Connected to SECOND_DOMAIN (203.0.113.1) port 443 (#0) ... * Server certificate: *  subject: CN=SECOND_DOMAIN ... > Host: SECOND_DOMAIN ... Hello, world! Version: 2.0.0 

    結果顯示在 TLS 握手使用您的第二個憑證。

Ingress 物件的主機欄位。

IngressSpectls 欄位是 IngressTLS 物件的陣列。每個 IngressTLS 物件都有 hosts 欄位和 SecretName 欄位。 GKE 中不使用 hosts 欄位。GKE 會從密鑰中的憑證讀取共用名稱 (CN)。若共用名稱符合用戶端要求中的網域名稱,負載平衡器便會對用戶端提供相符的憑證。

提供的憑證有哪些?

負載平衡器會根據下列這些規則選擇憑證

  • 若密鑰和預先共用的憑證都列於 Ingress 中,預先共用的憑證會優先於密鑰。換句話說,系統仍會納入密鑰,但會優先顯示預先共用的憑證。

  • 若憑證的共用名稱 (CN) 都不符合服務用戶端要求中的網域名稱,負載平衡器便會向用戶端提供主要憑證。

  • 針對 tls 區塊中列出的密鑰,主要憑證是清單中的第一個密鑰。

  • 針對註解中列出的預先共用憑證,主要憑證為清單中的第一個憑證。

憑證輪替最佳做法

如要輪替 Secret 或預先共用憑證的內容,請參考下列最佳做法:

  • 建立含有新憑證資料的新密鑰或預先共用憑證,並使用不同名稱。按照先前提供的操作說明,將這個資源 (連同現有資源) 附加至 Ingress。確認變更沒有問題後,即可從 Ingress 移除舊憑證。
  • 如果不在意流量中斷,可以從 Ingress 移除舊資源,以相同名稱但不同內容佈建新資源,然後重新附加至 Ingress。

如要避免自行管理憑證輪替,請參閱「使用 Google 代管的 SSL 憑證」。

疑難排解

指定無效或不存在的密鑰會導致 Kubernetes 事件錯誤。 您可以查看 Ingress 的 Kubernetes 事件,如下:

kubectl describe ingress 

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

Name:             my-ingress Namespace:        default Address:          203.0.113.3 Default backend:  hello-server:8080 (10.8.0.3:8080) TLS:   my-faulty-Secret terminates Rules:   Host  Path  Backends   ----  ----  --------   *     *     my-service:443 (10.8.0.3:443) Events:    Error during sync: cannot get certs for Ingress default/my-ingress:  Secret "my-faulty-ingress" has no 'tls.crt' 

後續步驟