GKE で Google マネージド証明書を利用するのが意外と難しい!Gateway API で LB を作成 (original) (raw)

こんにちは、SREグループのカンタンです!

GO株式会社では AWS EKS と GCP GKE の Kubernetes クラスタを活用していて、EKS は以前から AWS マネージド証明書を利用していますが GKE は最近になって Let's Encrypt 証明書から Google マネージド証明書に移行し始めました。

Google マネージド証明書の構成方法が複数あり、構成方法によって GKE での使い方が異なるため AWS と比べて Google マネージド証明書の利用が意外と難しいと感じました。 本記事では Google マネージド証明書の種類と GKE での利用方法を紹介します。

証明書の構成方法

GCP では証明書の構成方法が2つあります。

Compute Engine SSL 証明書

ドキュメントやコンソール画面によっては「Classic certificates」、「従来の証明書」とも呼ばれています。

Cloud Load Balancing によってプロビジョニングされる証明書で、 ドメイン認証にはロードバランサー認証 (HTTP-01 チャレンジ) しか対応されていないため、以下の制約があります。

特にドメインDNS レコードをロードバランサーに向かうように設定しないと証明書を発行できないため、例えば環境のマイグレーションのため古い環境から新しい環境に移行したい場合はダウンタイムが発生してしまいます。

lb-auth-issue

セルフマネージド証明書も対応しているため、自分で発行した証明書を GCP コンソールや gcloud compute ssl-certificates コマンドなどで GCP に登録することもできます。

Certificate Manager

Certificate Manager は証明書を発行するための最新のやり方で、ロードバランサー認証 (HTTP-01 チャレンジ)もDNS 認証 (DNS-01 チャレンジ)も対応しているため使いやすいです。

DNS 認証で証明書を発行する場合は以下の流れになります

DNS 認証を使えば証明書を事前に発行できるため、ダウンタイムなく環境のマイグレーションを実施できます。 また、ワイルドカード証明書も発行できますので複数のサブドメインに対して証明書を発行したい場合は便利です。

dns-auth-issue

Certificate Manager もセルフマネージド証明書を対応しているため、自分で発行した証明書を登録できます。

GKE Ingress での証明書の設定方法

GKE ではロードバランサーを作成するために Ingress リソースを使うことが多いです。その場合、SSL 証明書を設定するには4つの方法があります。 後ほど説明しますが、Ingress では Certificate Manager が使えなくて、いずれも Compute Engine SSL の証明書を利用しています。最終的に Certificate Manager を使いたいため結局以下の方法を採用しませんでしたが参考までにご紹介します。

Compute Engine SSL 証明書として事前に登録するセルフマネージド証明書と Google マネージド証明書は「事前共有証明書」、「Pre-shared certificate」と呼ばれています。

セルフマネージド証明書 - Secret

証明書を Secret に保存します。

kubectl create secret tls my-certificate-secret
--cert=certificate.pem
--key=key.pem

Secret を Ingress に設定します。

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: tls: - secretName: my-certificate-secret defaultBackend: service: name: my-service port: number: 80

例えば Let's Encrypt 証明書を cert-manager という OSS ツール(GCP の Certificate Managerとは別)で自動的に発行する場合はその方法を使います。

ingress-self-managed-secret

裏ではセルフマネージドの Compute Engine SSL 証明書が作成されていて、gcloud compute ssl-certificates list コマンドでも確認できます。

$ gcloud compute ssl-certificates list NAME TYPE CREATION_TIMESTAMP EXPIRE_TIME
k8s2-cr-xxxxxxx SELF_MANAGED 2024-07-24T20:12:17.117-07:00 2024-10-22T19:03:20.000-07:00

セルフマネージド証明書 - 事前共有証明書

Compute Engine SSL 証明書を事前に作成します (terraformも使えます)。

gcloud compute ssl-certificates create my-self-managed-pre-shared-certificate
--certificate=certificate.pem
--private-key=key.pem

ingress.gcp.kubernetes.io/pre-shared-cert アノテーションを指定することで作成した Compute Engine SSL 証明書を Ingress に設定します。

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: ingress.gcp.kubernetes.io/pre-shared-cert: my-self-managed-pre-shared-certificate spec: defaultBackend: service: name: my-service port: number: 80

ingress-self-managed-pre-shared

証明書の更新は以下のように手動で行う必要があるためこのやり方を推奨できません。

Google マネージド証明書 - 事前共有証明書

Google マネージドの Compute Engine SSL 証明書を事前に作成します (terraformも使えます)。

gcloud compute ssl-certificates create my-google-managed-pre-shared-certificate
--domains=www.example.com
--global

証明書をロードバランサーに紐づけてドメインDNS レコードをロードバランサーに向かうように設定しない限り証明書が発行されないため現時点では有効になっていないです。

ingress.gcp.kubernetes.io/pre-shared-cert アノテーションを指定することで作成した Compute Engine SSL 証明書を Ingress に設定します。

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: ingress.gcp.kubernetes.io/pre-shared-cert: my-google-managed-pre-shared-certificate spec: defaultBackend: service: name: my-service port: number: 80

ドメインDNS レコードを作成されたロードバランサーに向かうように設定すれば証明書が発行されます。 発行される証明書が Google マネージドのため更新が自動的に行われ便利です。ただし Compute Engine SSL 証明書になっているため、上述の通りワイルドカード証明書に対応していないのと証明書の事前発行が不可能のため使えないケースもあります。

ingress-google-managed-pre-shared

Google マネージド証明書 - ManagedCertificate

Compute Engine SSL 証明書を gcloud や terraform で作成する代わりに ManagedCertificate リソースを使って証明書の作成を GKE に完結させることができます。

apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: my-google-managed-certificate spec: domains: - www.example.com

networking.gke.io/managed-certificates アノテーションを指定することで作成した Compute Engine SSL 証明書を Ingress に設定します。

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: networking.gke.io/managed-certificates: my-google-managed-certificate spec: defaultBackend: service: name: my-service port: number: 80

ingress-managed

ドメインDNS レコードを作成されたロードバランサーに向かうように設定すれば証明書が発行されます。 発行される証明書が Google マネージドのため更新が自動的に行われ便利です。ただし裏では Compute Engine SSL 証明書になっているため、上述の通りワイルドカード証明書に対応していないのと証明書の事前発行が不可能のため使えないケースもあります。

問題

GO株式会社では古いサービスを弊社の共通インフラ基盤に移行したりインフラの構成を変更したりすることがあるため、証明書の発行をロードバランサーに依存させることが望ましくないです。 Compute Engine SSL 証明書ではなく Certificate Manager の証明書を使うようにすれば、証明書の事前発行が可能になりロードバランサーに依存しなくなりますが、Ingress が Certificate Manager を対応しない方針になっていて使えません。

Certificate Manager を利用できるように、ロードバランサーIngress ではなく Gateway API で作成する必要があります!Kubernetes 業界の中でも Gateway API がロードバランシングを行うための最新方法で、Ingress に代わるものとして注目されています。

Certificate Manager の証明書を利用できるようにロードバランサーGateway API で作成する必要があります。

まずは Certificate Manager を使って DNS 認証のワイルドカード証明書を作成します。

Certificate validation

resource "google_certificate_manager_dns_authorization" "example" { name = "my-certificate-dns-authorization" domain = "example.com" # without wildcard type = "PER_PROJECT_RECORD" # allow for multiple projects to validate the same domain }

Certificate

resource "google_certificate_manager_certificate" "example" { name = "my-certificate" managed { domains = ["*.example.com"] dns_authorizations = [google_certificate_manager_dns_authorization.example.id] } }

DNS 認証用のレコードを DNS サービスに登録します(今回はAWS Route 53を利用)。

resource "aws_route53_record" "dns_auth" { zone_id = "xxx" name = google_certificate_manager_dns_authorization.example.dns_resource_record[0].name type = google_certificate_manager_dns_authorization.example.dns_resource_record[0].type records = [google_certificate_manager_dns_authorization.example.dns_resource_record[0].data] ttl = 300 }

証明書をロードバランサーに紐づけられるように Certificate Map を作成します。

Certificate Map

resource "google_certificate_manager_certificate_map" "my_cert_map" { name = "my-cert-map" }

Certificate Map Entry

resource "google_certificate_manager_certificate_map_entry" "my_cert_map_entry" { name = "my-cert-map-entry" map = google_certificate_manager_certificate_map.my_cert_map.name hostname = "*.example.com" certificates = [ google_certificate_manager_certificate.example.name, ] }

ロードバランサーGateway API で作成し Certificate Map を紐付けます。

kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: my-gateway annotations: networking.gke.io/certmap: my-cert-map spec: gatewayClassName: gke-l7-global-external-managed listeners: - name: https protocol: HTTPS port: 443

通信をサービスにルーティングできるように HTTPRoute を作成します。

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: my-service-http-route spec: parentRefs: - kind: Gateway name: my-gateway rules: - backendRefs: - name: my-service port: 80

結果、Google マネージドのワイルドカード証明書を事前に発行でき、ロードバランサーに設定できました!

gateway-certificate-manager

最後に

結果的にロードバランサーに依存しない形で Google マネージド証明書を発行し安全に運用できるようになりましたが、Ingress で作っていたロードバランサーGateway API で作り直す必要があったためAWS EKS で一つのアノテーションの追加だけで済む話が GKE では大掛かりな変更が必要でした。

Google マネージド証明書を使いたい方は Certificate Manager の利用を推奨しますが、Ingress ではなく Gateway API を利用する必要があるためご注意ください。 それぞれの証明書の特徴を以下にまとめましたので参考にしていただければ幸いです。

項目 Certificate Manager 証明書 Compute Engine SSL 証明書
事前発行 O (DNS 認証可能) X (ロードバランサ認証のみ)
ワイルドカード証明書 O (DNS 認証可能) X (ロードバランサ認証のみ)
GKE での利用 Gatewayのみ Ingress / Gateway
発行方法 terraform / gcloud / console terraform / gcloud / console / ManagedCertificate (Ingressのみ)
セルフマネージド証明書 O (事前登録) O (事前共有 / Secret)