こんにちは、SREグループのカンタンです!
GO株式会社では AWS EKS と GCP GKE の Kubernetes クラスタを活用していて、EKS は以前から AWS マネージド証明書を利用していますが GKE は最近になって Let's Encrypt 証明書から Google マネージド証明書に移行し始めました。
Google マネージド証明書の構成方法が複数あり、構成方法によって GKE での使い方が異なるため AWS と比べて Google マネージド証明書の利用が意外と難しいと感じました。 本記事では Google マネージド証明書の種類と GKE での利用方法を紹介します。
証明書の構成方法
- Compute Engine SSL 証明書
- Certificate Manager
Compute Engine SSL 証明書
ドキュメントやコンソール画面によっては「Classic certificates」、「従来の証明書」とも呼ばれています。
Cloud Load Balancing によってプロビジョニングされる証明書で、 ドメイン認証にはロードバランサー認証 (HTTP-01 チャレンジ) しか対応されていないため、以下の制約があります。
- ドメイン認証ができるようにドメインの DNS レコードをロードバランサーに向かうように設定する必要があるため、証明書を事前に発行できない
- ワイルドカード証明書に対応していない
特にドメインの DNS レコードをロードバランサーに向かうように設定しないと証明書を発行できないため、例えば環境のマイグレーションのため古い環境から新しい環境に移行したい場合はダウンタイムが発生してしまいます。
セルフマネージド証明書も対応しているため、自分で発行した証明書を GCP コンソールや gcloud compute ssl-certificates
コマンドなどで GCP に登録することもできます。
Certificate Manager
Certificate Manager は証明書を発行するための最新のやり方で、ロードバランサー認証 (HTTP-01 チャレンジ)も DNS 認証 (DNS-01 チャレンジ)も対応しているため使いやすいです。
DNS 認証で証明書を発行する場合は以下の流れになります
- GCP コンソール、terraform、
gcloud certificate-manager certificates
コマンドなどで証明書を作成 - Certificate Manager から作成されたドメイン認証用の DNS レコード内容を DNS サービスに登録
- Certificate Manager が DNS レコードを確認し証明書を発行
DNS 認証を使えば証明書を事前に発行できるため、ダウンタイムなく環境のマイグレーションを実施できます。 また、ワイルドカード証明書も発行できますので複数のサブドメインに対して証明書を発行したい場合は便利です。
Certificate Manager もセルフマネージド証明書を対応しているため、自分で発行した証明書を登録できます。
GKE Ingress での証明書の設定方法
GKE ではロードバランサーを作成するために Ingress リソースを使うことが多いです。その場合、SSL 証明書を設定するには4つの方法があります。 後ほど説明しますが、Ingress では Certificate Manager が使えなくて、いずれも Compute Engine SSL の証明書を利用しています。最終的に Certificate Manager を使いたいため結局以下の方法を採用しませんでしたが参考までにご紹介します。
- セルフマネージド証明書を Secret に保存し Ingress に紐付ける
- セルフマネージド証明書を Compute Engine SSL 証明書として事前に登録し Ingress に紐付ける
- Google マネージドの Compute Engine SSL 証明書を事前に登録し Ingress に紐付ける
- ManagedCertificate で Google マネージドの Compute Engine SSL 証明書を作成し Ingress に紐付ける
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とは別)で自動的に発行する場合はその方法を使います。
- cert-manager が Let's Encrypt に発行された証明書を Secret に保存
- Ingress が Secret の内容を元にセルフマネージドの Compute Engine SSL 証明書を作成
- ロードバランサーが Compute Engine SSL 証明書を利用
- 更新の際、cert-manager が新しい証明書を Secret に保存し、Ingress が Compute Engine SSL 証明書を更新
裏ではセルフマネージドの 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
証明書の更新は以下のように手動で行う必要があるためこのやり方を推奨できません。
- 新しい Compute Engine SSL 証明書を作成
- Ingress の
ingress.gcp.kubernetes.io/pre-shared-cert
アノテーションに新しい証明書を設定 - 古い Compute Engine SSL 証明書を削除
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 証明書になっているため、上述の通りワイルドカード証明書に対応していないのと証明書の事前発行が不可能のため使えないケースもあります。
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
ドメインの DNS レコードを作成されたロードバランサーに向かうように設定すれば証明書が発行されます。 発行される証明書が Google マネージドのため更新が自動的に行われ便利です。ただし裏では Compute Engine SSL 証明書になっているため、上述の通りワイルドカード証明書に対応していないのと証明書の事前発行が不可能のため使えないケースもあります。
問題
GO株式会社では古いサービスを弊社の共通インフラ基盤に移行したりインフラの構成を変更したりすることがあるため、証明書の発行をロードバランサーに依存させることが望ましくないです。 Compute Engine SSL 証明書ではなく Certificate Manager の証明書を使うようにすれば、証明書の事前発行が可能になりロードバランサーに依存しなくなりますが、Ingress が Certificate Manager を対応しない方針になっていて使えません。
Certificate Manager を利用できるように、ロードバランサーを Ingress ではなく Gateway API で作成する必要があります!Kubernetes 業界の中でも Gateway API がロードバランシングを行うための最新方法で、Ingress に代わるものとして注目されています。
Gateway API で LB を作成!
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 マネージドのワイルドカード証明書を事前に発行でき、ロードバランサーに設定できました!
最後に
結果的にロードバランサーに依存しない形で 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) |