GKEにCert-Managerを導入、DNS-01認証(Cloud DNS)で証明書を発行し、Ingressで利用する。

本記事では、GKEへのCert-Managerの導入、Cloud DNSでのDNS-01チャレンジ認証と、発行した証明書をIngressで利用するまでをまとめます。

お高いCloud Load Balancingを使わないように、Nginx社のNginx Ingress Controllerを導入していたため、Cert-Managerを利用した証明書発行が必要となりましが、Cloud Load Balancingを利用してIngressリソースを作成する場合は、Google マネージド SSL 証明書を利用するのが良いでしょう。

Cert-Managerの導入

公式ドキュメントを参考にGKEにCert-Managerをインストールしていきます。
マニュフェストを利用したインストール手順で実施します。

(1) GKEを利用している場合は、利用中のサービスアカウントにcluster-adminロールを付与します。

  kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

上記手順を実行しない場合に、インストール時にエラーが発生する可能性があります。

(2) cert-managerのマニュフェストを適用します。

kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.0/cert-manager-legacy.yaml

kubernetes バージョンが1.15未満のため、cert-manager-legacy.yamlを利用します。kubernetesバージョンが1.15以上の場合は cert-manager.yamlを利用してください。

(3) cert-managerが導入されたことを確認します。

kubectl get pods --namespace cert-manager

cert-manager, cert-manager-cainjector, cert-manager-webhookがRunningステータスであることを確認します。

DNS-01チャレンジのための設定

(1) DNS-01チャレンジでアクセスするためのサービスアカウントを作成します。

gcloud iam service-accounts create dns01-solver

(2) 作成したサービスアカウントにdns管理者ロールを付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
   --member serviceAccount:dns01-solver@$PROJECT_ID.iam.gserviceaccount.com \
   --role roles/dns.admin

(3) サービスアカウントの認証鍵を作成します。

export PROJECT_ID=your_project_id

gcloud iam service-accounts keys create key.json \
   --iam-account dns01-solver@$PROJECT_ID.iam.gserviceaccount.com

(4) 作成した認証鍵からsecretsを作成します。

kubectl create secret generic clouddns-dns01-solver-svc-acct \
   --from-file=key.json

証明書の発行

(1) Issuerリソースを作成します。

Issuerの場合、証明書を適用するIngressと同じnamespaceである必要があります。namespaceをまたいで発行したい場合はClusterIssuerを利用します。

以下をファイルを作成し、kubectlコマンドでapplyします。

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: letsencrypt-production
  namespace: default
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: user@example.com

    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-production

    # ACME DNS-01 provider configurations
    solvers:
    # An empty 'selector' means that this solver matches all domains
    - selector: {}
      dns01:
        clouddns:
          # The ID of the GCP project
          # reference: https://docs.cert-manager.io/en/latest/tasks/issuers/setup-acme/dns01/google.html
          project: $PROJECT_ID
          # This is the secret used to access the service account
          serviceAccountSecretRef:
            name: clouddns-dns01-solver-svc-acct
            key: key.json

(2) Issuerが正常に作成されたことを確認します。

kubectl describe issuers letsencrypt-production

以下のようにStatusがTrueで、TypeがReadyになっていればOKです。

  Conditions:
    Last Transition Time:  2020-05-26T14:30:38Z
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready

(3) 証明書を発行します。

以下のファイルを作成し、kubectlコマンドでapplyします。

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: example-com
  namespace: default
spec:
  secretName: example-com-tls
  issuerRef:
    # The issuer created previously
    name: example-issuer
  dnsNames:
  - example.com
  - www.example.com

上記を作成することで、自動的にDNS-01チャレンジが行われます。証明書を発行するドメインのCloud DNS上で、_acme-challengeのTXTレコードが作成されます。TXTレコードのデータには認証用のトークンが設定されます。

(4) 証明書が発行されたことを確認します。

kubectl describe certificate example-com

以下のように、Certificate issued successfullyと表示されていれば正常に作成されています。発行が完了するまで少し時間がかかります。

Events:
  Type    Reason      Age   From          Message
  ----    ------      ----  ----          -------
  Normal  CertIssued  4s    cert-manager  Certificate issued successfully

Ingressから証明書を利用する

(1) 以下のようにIngressリソースを作成します。前述の手順で用意した証明書(secrets)を定義します。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - www.example.com
    secretName: example-com-tls
  rules:
  - host: www.example.com
    http:
      paths:
      - backend:
          serviceName: nginx
          servicePort: 80

まとめ

以上で、Cert-Managerの導入から証明書の取得およびIngressでの利用までできました。Kubernetes上のリソースを作成するだけで証明書が発行できるので非常に便利ですね。