ZeroSSLの証明書を無制限にcert-managerで発行する

jagaNikumanPosted by

はじめに

この記事は GRIPHONE Advent Calendar 2020 21日目の記事です。

こんにちは、SREの岩立です。

Let’s Encrypt使っていますか? 無料で容易にSSL証明書を発行できてとても便利なサービスでとても助かっています。ただ、Let’s Encryptには1週間に発行できる証明書は50個までという制限があります。こういったレート制限に引っかかるようになってしまい、ACMEに対応していて無制限に証明書を発行できるサービスを探していたところZeroSSLというサービスを見つけたため、今回はZeroSSLを用いてcert-managerで証明書を作成してみたいと思います。

ZeroSSLとは

ZeroSSLは、ざっくり言うとSSL証明書を発行してくれるサービスです。

特徴としては月$10払うと無制限で証明書が発行でき、かつACMEにも対応しているのでcert-managerなどから自動で発行することも出来ます。

今回自分たちはLet’s Encryptのレート制限に引っかかって代替のサービスを探していたため、ZeroSSLを試してみることにします。

実際に発行してみる

ZeroSSLのEAB Credentialを取得しSecretを作成する

まず、ACMEでの認証に必要なEAB CredentialをZeroSSLで発行します。

ログイン後のページのDeveloperタブからEAB Credentialの発行ができるので、発行されたEAB KIDとEAB HMAC Keyを控えます。

以下のコマンドで、EAB HMAC Keyをbase64デコードし、それを元にcert-managerで使用するSecretを作成します。(EAB HMAC Keyを実際のキーに置き換えてください。)

echo -n "EAB HMAC Key" | base64 -d > eab-key
kubectl -n cert-manager create secret generic zerossl-eab-key --from-file eab-key

Issuerを作成

externalAccountBindingを使用したIssuerの雛形はcert-managerのドキュメントを参照しました。
https://cert-manager.io/docs/configuration/acme/#external-account-bindings

Manifestの内容を軽く説明すると、spec.acme.serverがZeroSSLのACMEのエンドポイントになります。こちらのドキュメントに載っています。
https://zerossl.com/documentation/acme/

そして、spec.acme.externalAccountBindingでZeroSSLのEAB Credentialを使用して認証情報を設定します。

そして、最後にspec.solversですが、これはACMEで使用するChallengeの設定になります。今回はGCPを使っていることもありCloudDNSでDNS01 Challengeをすることにしたので、CloudDNSの認証情報を設定しています。そのため、CloudDNSでdns01を使用する場合はServiceAccountを発行してK8sのSecretを作成し、適宜置き換えてしてください。また、それ以外のChallengeを使用する場合はspec.solvers以下を修正してください。

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: zerossl-dns01
spec:
  acme:
    server: https://acme.zerossl.com/v2/DV90
    externalAccountBinding:
      keyID: <EAB KIDを入力>
      keySecretRef:
        name: zerossl-eab-key
        key: eab-key
      keyAlgorithm: HS256
    privateKeySecretRef:
      name: zerossl-prod
    solvers:
    - dns01:
        clouddns:
          project: CloudDNSのZoneを置いているGCP Project ID
          serviceAccountSecretRef:
            key: CloudDNSのServiceAccountのSecretのKey
            name: CloudDNSのServiceAccountのSecret名

IssuerのManifestが作成できたら、kubectlでapplyしてください。

Issuerが作成できたかどうかは、作成したclusterissuerリソースのStatusを確認してください。
StatusがTrueになっていて、MessageがThe ACME account was registered with the ACME serverになっていれば無事認証が通り、Issuerが作成できています。

kubectl get clusterissuer
NAME            READY   AGE
zerossl-dns01   True    8h

kubectl describe clusterissuer zerossl-dns01
(省略)
Status:
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready

Certificateの作成

実際に動作確認用のCertificateを試しに発行してみます。

dnsNamesに発行したいドメインを書き、issuerRefに先ほど作成したIssuerを指定すれば終わりです。

また、今回ドメイン名はexample.localとして伏せていますのでご注意ください。

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: zerossl-test-example-local
spec:
  dnsNames:
  - zerossl.test.example.local
  secretName: zerossl-test-example-local
  issuerRef:
    name: zerossl-dns01
    kind: ClusterIssuer

CertificateのManifestも作成できたら、kubectlでapplyしてください。

こちらも作成できたかを確認するにはissuerと同様に作成したcertificateリソースのStatusを確認してください。

StatusがTrueになっていれば発行完了しています。数分待ってもReadyがTrueにならない場合はkubectl describeで詳細なStatusを確認してください。

kubectl get certificate
NAME                         READY   SECRET                       AGE
zerossl-test-example-local   True    zerossl-test-example-local   8h

動作確認

最後に先ほど作成したCertificateを使ってnginxを立ててみたいと思います。

以下のコマンドでnginxのDeployment, Serviceを作成します。

kubectl create deploy nginx --image nginx --port 80
kubectl expose deploy nginx --port 80

最後にIngressを作成します。spec.tlsに先ほど作成したCertificateから生成されるSecretを設定します。

# ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
  tls:
  - secretName: zerossl-test-example-local
    hosts:
      - zerossl.test.example.local

そして、このIngressのManifestをapplyします。

kubectl apply -f ingress.yaml

そうしたら、以下のコマンドでingressのIPアドレスを調べます。(IPアドレスは伏せました)

$ kubectl get ingress
NAME    HOSTS   ADDRESS         PORTS     AGE
nginx   *       **.***.**.***   80, 443   8h

IPアドレスを取得できたら、CloudDNSに証明書を取得しているドメインのAレコードに登録します。

そして、ブラウザからhttps://zerossl.test.example.localにアクセスしてつながることを確認します。また証明書を見てZeroSSL発行のものであることが確認できるかと思います。

おわりに

今回はZeroSSLの証明書をcert-managerで発行する方法について書いてみました。

ZeroSSLがACMEに対応してくれているおかげでcert-managerを用いて自動でシュッと証明書を発行することが出来て幸せですね。

それでは!