cert-manager について学ぶ

Ikki Shoka

2022.5.10

ACME challenges [HTTP01]

概念が掴みにくい用語

  • チャレンジ (challenges)
    ACME クライアント(cert-manager)がドメインを所有しているのを確認すること
  • Issuer (発行者)
    証明書を発行(issue, issuance)する認証局 (CA; Certificate Authority) のこと
    → 種類は SelfSigned, CA, Vault, Venafi(ベナファイ), External, ACME(HTTP01, DNS01)
  • SAN (Subject Alternative Name; サン)
    1つの証明書に2つ以上のホスト (CN; Common Name)
    [e.g. www.example.com, example.com] を含めることができる追加名のこと

cert-manager

特徴

  • 証明書の管理更新を自動化できるクラウドネイティブな OSS
  • APIを利用する場合は acme.cert-manager.io.io/v1, cert-manger.io/v1 等がある
  • ユースケースとしては自動的に Let’s Encrypt を発行して、ローテションしてくれる Issuer である ACME(HTTP01, DNS01) が使用されることが多いように思う

参照: https://cert-manager.io/docs/configuration/acme/

簡単な証明書作成の流れ

概念図

概念図 ※正確には CertificateRequest リソースは Certificate リソースから作成される
  1. cert-manager の CRD 一式をデプロイ
  2. Issuer を発行
  3. 認証したいドメイン用の Certificate を作成
  4. 発行された Secret を Ingress(SSL終端) で利用

HTTP01

  • HTTP01 チャレンジでは、ACME サーバのリクエストに対応するチャレンジソルバー Pod(Pod), Service, Ingress が一時的に作成される
    → pods 内の /.well-known/acme-challenge/XYZ を使用してドメインの所在確認(チャレンジ)を行う
  • 上記 Ingress は、https://cert-manager.io/docs/configuration/acme/http01/#options Issuer の設定を変更することで既存の Ingress がチャレンジリゾルバー Pod との通信を担うことも可能
  • 一時的 Ingress には kubernetes.io/ingress.class: [ingressClass field] のアノテーションがつく

【NOTE】HTTP01 チャレンジは https 通信ではなく http 通信であるため、http 等を拒否している場合は、チャレンジの通信を別途パススルーする必要がある。下記に参考までに VCL (Varnish Custom Controller の設定を載せておく

sub vcl_recv {
  #リクエストパスが、./well-known/acme-challenge/XXXだった場合、HTTPリクエストをパススルーするる
  if (req.url.path ~ "^/\.well-known/acme-challenge/.+") {
    if (table.lookup(custom_domains, req.http.host) == "dev") {
      set req.backend = F_dev_http;
    } elseif (table.lookup(custom_domains, req.http.host) == "stg") {
      set req.backend = F_stg_http;
      ...
    }
}

証明書の期限に関して

デフォルトの期限 (duration) は90日 (15日前に更新)、 renewBefore (defalut: durationの2/3) か duration の2/3のいずれか遅い方で更新される

ハマりどころ
証明書更新の検証を行いたい場合、Certificate リソースの spec.renewBefore を記述する必要があるが、作成時間から起算して、spec.renewBefore10m と記述すると作成されない。
なぜなら、証明書の期限から逆算して計算されるため、10分後に更新したい場合は、 60(分) × 24(時間) × 90(日) = 129600 – 10 = 129590 で 129590m と記述する必要がある

ただし、あまりに短くしすぎてしまうと HTTP01 チャレンジのレート制限がかかってしまうため、こちら利用したい場合は予め HTTP01 の レート制限 を確認する必要がある。

【NOTE】ACME サーバはステージング用 (https://acme-staging-v02.api.letsencrypt.org/directory) と本番用(https://acme-v02.api.letsencrypt.org/directory) があるが、レート制限があるためステージングはステージング用で利用することを強く勧める

Ingress SSL終端とCertificateの自動作成

Ingress の annotation に cert-manager.io/issuer: [Issuer名] を付与することで Issuer から Ingress での SSL 終端用の証明書 (Certificate; 厳密には Secret に証明書が内包されている) を自動的に発行することができる

→ 自動的に作成されるのは便利だが、Self で Certificate を用意する際に利用できる spec.secretTemplate のように一部 Ingress 経由作成の Certificate では利用できないパラメータが存在するので、利用用途として注意する必要がある

もし、一部の Certificate の設定をIngressで利用したい場合は Securing Ingress Resources

証明書の運用に関して

  • Certificates を発行したい namespace にそれぞれ Issuer リソースを作る必要がある
    → もし、namespace に依存しない Issuer を作成したい場合は ClusterIssuer リソースを作成することで問題を回避することができる
  • Certificates リソースが sepc.rotationPolicy: Always ならば証明書が更新されるたびに tls.key も更新される
    → 言い換えると、更新されるのは証明書だけで、秘密鍵は更新されないようになっている
  • 上記に併せて Certificates リソースを削除しても、対応する Secret は削除されない
    → Certificate を削除すると Secret が自動的に削除するにはコントローラーに --enable-certificate-owner-ref フラグを追加する必要がある
  • ACME チャレンジで発行される証明書 (Secretリソース)には tls.crt (leaf と intermediate 証明書がある) とtls.key (チャレンジ時に発行される一時的 Secret 由来) が含まれている
  • CertificateRequest は Certificate が作られるか、仕様が変わるか、更新が必要になると自動的に作成される

証明書の設定項目(抜粋)

  • spec.dnsNames
    DNSNames is a list of DNS subjectAltNames to be set on the Certificate.
  • spec.secretName
    Certificats リソースによって作成される tls.key, tls.crt が含まれた Secret リソース名

その他細かい仕様

  • HTTP01 (ingress, contour) と +α dnsZones セレクタの cloudDNS がある
  • DNS01 のセレクタ(満たすべき要件)は matchLabel >dnsNames (ワイルドカード利用不可) > dnsZones (ワイルドカード)の順で優先される
    e.g. www.sys.example.com のために example.com ではなくて sys.example.com が指定される。また、example.com を設定するとサブドメイン *.example.com もリゾルブしてくれる

間違いポイント
Issuer が発行する秘密鍵 (privateKeySecretRef) と、Certificate が作成する証明書 (secretName) は、前者は ACME/Let’s Encrypt のアカウント秘密鍵で、secretName は作成されるドメインの証明書と秘密鍵が含まれている

ACME チャレンジ (protocolの仕組み)

特徴

  • 正式名称: ACME (Automated Certificate Management Environment) protocol
  • 人的介入なしに CA から証明証を得れる, Let’s Encrypt 使用, 80 port のみ許可, 2019 IETF 標準
  • ACME client – Web server, JSON over HTTPS ※ cert-manager では Pod が作られる
  • ACME server – チャレンジ(ドメインが本人のものであるか検証)を発行(issue)する
    ※ cert-manager の HTTP01 チャレンジでは Let’s Encrypt のサーバを指す

証明書作成プロセス

  1. [Web Server: クライアント] 特定のドメインの証明書を求める
  2. [ACME Server: サーバ] チャレンジ (HTTP-01: http://example.com/.well-known/acme-challenge/XXXToken, DNS-01; DNS record) の開始
  3. [Web Server: クライアント] ファイルを作成, HTTP resource を返して、validation する
  4. [ACME Server: サーバ] GET http://example.com/.well-known/acme-challenge/XXXToken リクエスト
  5. [Web Server: クライアント] キーペア (privateKeySecretRef) とそれを利用した CSR を作成、CSR を ACME Server に送る (全ての CSR はアカウントの秘密鍵でサインされている)
  6. [ACME Server: サーバ] 署名を確認し、 署名入りの証明書を発行 (issue) する

備考

HTTP01 チャレンジ

  • 途中で自己署名証明書や有効期限切れの証明書が存在する可能性があるため
    HTTP01 チャレンジでは http(80) → https(443) のリダイレクトのみ許可 IPアドレス✗
  • HTTP01 チャレンジではワイルドカード証明書が発行できない (DNS-01は可能)
    全てのウェブサーバで同じファイルを設定する必要がある

DNS01 チャレンジ

  • DNS01 チャレンジでは TXT レコード(トークンとアカウント鍵から)を使用し(クライアント)、_acme-challenge.<YOUR_DOMAIN> の値として設定(DNS server)する必要がある
  • DNS01 チャレンジでは Web サーバ上に API のクレデンシャルを置くリスクがある

TLS-ALPN01 チャレンジ

  • Caddy では実装されているが、nginx, Apache でサポートされていない, 443 利用上記のやりとりを cert-manager の CertificateRequest (Order, Challenge) で実施しているよう

デバック

デバックするときは大体以下のコマンドから始めている

$ kubectl get cert,cr,order,challenge
  • 大体、Certificate か Challenge リソースを見て原因調査を始めることが多い
  • 失敗しているときは残っている一時的な Ingress や Service や Pod も
  • 加えて、challengeのリクエストが帰ってくるか curl で確認したりもよい
  • トークンは Challenge の spec.token を確認すればよい
$ curl -sv --resolve challenge-test.example.com:80:XX.XX.XX.XX(IngressのグローバルIP) \
  http://challenge-test.example.com/.well-known/acme-challenge/hogehogehogehoeghoehogehoge

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ