新着:プロジェクトの最新情報をTwitterMastodonで入手

ACME / Let's Encrypt 証明書の問題のトラブルシューティング

cert-manager が ACME / Let's Encrypt 証明書の更新に失敗した場合の問題を診断する方法を説明します。

概要

ACME 証明書をリクエストすると、cert-manager はリクエストを完了するために OrderChallenges を作成します。そのため、プロセス中に問題が発生した場合、調査およびデバッグするリソースが増えます。これらのリソースの詳細については、コンセプトページを参照してください。

ここから始める前に、一般的なトラブルシューティングガイドを確認することをお勧めします。

1. (Cluster)Issuer のトラブルシューティング

まず、使用している (Cluster)Issuer が準備完了状態であるかどうかを確認します。

$ kubectl get issuer
$ kubectl get clusterissuer
NAME READY AGE
letsencrypt True 38m
letsencrypt-http False 32m

False と表示された場合は、kubectl describe を使用してステータスを確認します。例:

$ kubectl describe issuer letsencrypt-http
$ kubectl describe clusterissuer letsencrypt-http
Name: letsencrypt
API Version: cert-manager.io/v1
Kind: Issuer
Spec:
Acme:
Email: cert-manager@example.com
Private Key Secret Ref:
Name: letsencrypt
Server: https://acme-staging-v02.api.letsencrypt.org/directory
Status:
Acme:
Conditions:
Message: Failed to update ACME account:400 urn:ietf:params:acme:error:invalidEmail: Unable to update account :: invalid contact domain. Contact emails @example.com are forbidden
Reason: ErrUpdateACMEAccount
Status: False
Type: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ErrUpdateACMEAccount 101s (x3 over 106s) cert-manager Failed to update ACME account:400 urn:ietf:params:acme:error:invalidEmail: Unable to update account :: invalid contact domain. Contact emails @example.com are forbidden

一般的なエラー

  • Failed to update ACME account:400 urn:ietf:params:acme:error:invalidEmail: Issuer 構成で指定したメールアドレスが無効です。
  • Error initializing issuer: Failed to register ACME account: secrets "acme-key" already exists: 無効になった以前の Issuer からのアカウントが残っている可能性があります。再作成できるようにシークレットを削除する必要があります。
  • Error accepting challenge: 400 urn:ietf:params:acme:error:malformed: Unable to update challenge :: authorization must be pending: これは、cert-manager がチャレンジを受け入れるリクエストを ACME サーバーに送信した時点で、認証が「保留中」状態ではなかったことを示唆しています。これは、ドメイン検証が既に失敗し、認証が「無効」とマークされているためである可能性があります。Order または Challenge のステータスの認証 URL を確認して、認証のステータスと追加情報を確認してください。

2. オーダーのトラブルシューティング

CertificateRequest リソースで describe を実行すると、作成された Order が表示されます

$ kubectl describe certificaterequest example-com-2745722290
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal OrderCreated 5s cert-manager Created Order resource default/example-com-2745722290-439160286

Order は、証明書を発行するための ACME インスタンスへのリクエストです。特定のオーダーで kubectl describe order を実行すると、プロセス内の失敗に関する情報を収集できます

$ kubectl describe order example-com-2745722290-439160286
...
Reason:
State: pending
URL: https://acme-v02.api.letsencrypt.org/acme/order/41123272/265506123
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 1m cert-manager Created Challenge resource "example-com-2745722290-439160286-0" for domain "test1.example.com"
Normal Created 1m cert-manager Created Challenge resource "example-com-2745722290-439160286-1" for domain "test2.example.com"

ここでは、cert-manager が、署名付き証明書を取得するための ACME オーダーの要件である、特定のドメインを制御していることを検証するために 2 つの Challenge リソースを作成したことがわかります。

次に、kubectl describe challenge example-com-2745722290-439160286-0 を実行して、オーダーの進行状況をさらにデバッグできます。

オーダーが正常に完了すると、次のようなイベントが表示されるはずです

$ kubectl describe order example-com-2745722290-439160286
...
Reason:
State: valid
URL: https://acme-v02.api.letsencrypt.org/acme/order/41123272/265506123
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 72s cert-manager Created Challenge resource "example-com-2745722290-439160286-0" for domain "test1.example.com"
Normal Created 72s cert-manager Created Challenge resource "example-com-2745722290-439160286-1" for domain "test2.example.com"
Normal OrderValid 4s cert-manager Order completed successfully

Order のステータスからの認証 URL を使用して、このオーダーの一部として検証する必要がある ACME 認証の状態に関する追加情報を確認できます

$ kubectl get order <order-name> -ojsonpath='{.status.authorizations[x].url}'

オーダーが正常に完了しない場合は、次の手順で説明するように、Challenge リソースで kubectl describe を実行して、オーダーのチャレンジをデバッグできます。

3. チャレンジのトラブルシューティング

ACME オーダーが完了しない理由を特定するために、cert-manager が作成した Challenge リソースを使用してデバッグできます。

どの Challenge が失敗しているかを特定するには、kubectl get challenges を実行します

$ kubectl get challenges
...
NAME STATE DOMAIN REASON AGE
example-com-2745722290-4391602865-0 pending example.com Waiting for dns-01 challenge propagation 22s

これは、DNS01 ソルバーを使用してチャレンジが正常に提示され、cert-manager が「自己チェック」が成功するのを待っていることを示しています。

kubectl describe を使用して、チャレンジとそのライフサイクルに関する詳細情報を取得できます

$ kubectl describe challenge example-com-2745722290-4391602865-0
...
Status:
Presented: true
Processing: true
Reason: Waiting for dns-01 challenge propagation
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 19s cert-manager Challenge scheduled for processing
Normal Presented 16s cert-manager Presented challenge using dns-01 challenge mechanism

各チャレンジの状態に関する進行状況は、イベントとして、またはチャレンジの status ブロック (上記参照) に記録されます。

DNS01 の場合は、DNS プロバイダーからのエラーがここに表示されます。

HTTP01 と DNS01 の両方で、cert-manager が ACME プロバイダーにチャレンジを提示する前に、最初に「自己チェック」が行われます。これは、DNS またはロードバランサーの伝播による失敗したチャレンジで ACME プロバイダーに過負荷をかけないようにするためです。このステータスは、describe の Status ブロックにあります。

$ kubectl describe challenge
[...]
Status:
Presented: true
Processing: true
Reason: Waiting for http-01 challenge propagation: failed to perform self check GET request 'http://example.com/.well-known/acme-challenge/_fgdLz0i3TFiZW4LBjuhjgd5nTOkaMBhxYmTY': Get "http://example.com/.well-known/acme-challenge/_fgdLz0i3TFiZW4LBjuhjgd5nTOkaMBhxYmTY: remote error: tls: handshake failure
State: pending
[...]

この例では、ネットワークの問題により HTTP01 チェックが失敗しています。ここには、DNS プロバイダーからのエラーも表示されます。

Challenge のステータスからの認証 URL を使用して、チャレンジが検証する必要がある ACME 認証の状態に関する追加情報も確認できます

$ kubectl get challenge <challenge-name> -ojsonpath='{.spec.authorizationURL}'

HTTP01 のトラブルシューティング

まず、パブリックインターネットからチャレンジ URL を確認できるかどうかを確認します。これが機能しない場合は、Ingress およびファイアウォール構成と、ACME チャレンジを解決するために cert-manager が作成したサービスとポッドを確認してください。これが機能する場合は、クラスターもそれを確認できるかどうかを確認します。これはポッド内からテストすることが重要です。接続エラーが発生した場合は、クラスターのネットワーク構成を確認することをお勧めします。tls: handshake failure を受信した場合は、Ingress または Certificate リソースでアノテーション cert-manager.io/issue-temporary-certificate: "true" を設定してみてください。これにより、実際の証明書が発行される前に、イングレスコントローラーが使用するための一時的な自己署名証明書が発行されます。それでも問題が解決しない場合は、イングレスコントローラーが同じホスト名に対して複数のリソースを処理することに問題がある可能性があります。この場合、アノテーション acme.cert-manager.io/http01-edit-in-place: "true" が必要になる可能性があります。

たとえば、Google Cloud Loadbalancer で GKE を使用する場合は、次の設定をお勧めします

cert-manager.io/issue-temporary-certificate: "true"
acme.cert-manager.io/http01-edit-in-place: "true"

これにより、Google Cloud Loadbalancer は一時的な証明書を使用して HTTPS エンドポイントを正しく伝播できます。http01-edit-in-place 部分は、GKE がチャレンジエンドポイントに 2 番目の IP アドレスを割り当てるのを防ぎます。

404 ステータスコードを取得した場合

チャレンジの自己チェックが 404 Not Found エラーで失敗した場合。次の点を確認してください

  • パブリックインターネットから URL にアクセスできる
  • ACME ソルバーポッドが起動して実行されている
  • kubectl describe ingress を使用して、HTTP01 ソルバーイングレスのステータスを確認します。(acme.cert-manager.io/http01-edit-in-place を使用している場合は、ドメインと同じイングレスを確認してください)

DNS01 のトラブルシューティング

DNS プロバイダーに関するエラーイベントが表示されない場合は、次の点を確認できます。パブリックインターネットまたは DNS プロバイダーのインターフェースから _acme_challenge.domain TXT DNS レコードが表示されるかどうかを確認します。cert-manager は、クラスターの DNS ソルバーにクエリを実行して、DNS レコードが伝播されたかどうかを確認します。パブリックインターネットからは確認できるが、クラスター内からは確認できない場合は、一部のクラウドプロバイダーが内部で DNS を上書きするため、自己チェック用の DNS サーバーを変更することをお勧めします。

cert-manager がドメイン名のゾーンを誤って識別する

cert-manager はデフォルトで SOA (Start of Authority) レコードを使用して、DNS プロバイダーで使用するゾーン名を決定します。一部の DNS リゾルバーはこの情報をフィルタリングします。この場合、cert-manager はゾーンを決定できず、DNS01 自己チェック用の DNS サーバーを変更することをお勧めします。

DNSサーバーとしてdnsmasqを使用している場合、--filterwin2kフラグを使用していると、この問題が発生する可能性があります。 OpenWRTにはfilterwin2kという設定オプションがあります。また、LuCIには「Filter useless」オプションがあります。このフラグを有効にすると、dnsmasqはすべてのSOAレコードを破棄します。

2020年3月 Let's Encrypt CAA再チェックバグ

3月4日の発表に従い、Let's EncryptはCAAレコードの検証方法にバグがあったため、多数の証明書を失効させることになりました。このため、既存のcert-managerで管理された証明書を分析し、そのシリアル番号を公開された失効証明書リストと比較するツールを作成しました。Let's Encryptとcert-managerのすべてのユーザーは、このツールを使用してチェックを行い、クラスター内で無効な証明書エラーが発生しないようにすることをお勧めします。チェッカーツールのコピーはこちらにあります:https://github.com/jetstack/letsencrypt-caa-bug-checker