Skip to main content

cert-manager で Kubernetes の TLS 証明書を自動管理する

Kubernetes 環境でサービスを公開する際、TLS 証明書の管理は避けて通れない課題です。手動での証明書発行・更新は、運用負荷の増大と期限切れによる障害リスクを招きます。CNCF Graduated プロジェクトである cert-manager は、TLS 証明書のライフサイクルを完全に自動化し、この課題を根本的に解決します。Kubo のような K3s ベースの Kubernetes プラットフォームでは、cert-manager の導入によりゼロタッチの証明書管理が実現できます。

cert-manager の基本概念とアーキテクチャ

cert-manager は、Kubernetes および OpenShift クラスタ上で TLS 証明書の発行と更新を自動化するアドオンです。GitHub リポジトリで活発に開発が続けられており、クラウドネイティブ環境における証明書管理のデファクトスタンダードとなっています。

主要コンポーネント

cert-manager は以下のコンポーネントで構成されます:

  • Controller: Certificate リソースの状態を監視し、証明書の発行・更新を実行
  • Webhook: CRD のバリデーションと Admission Control を担当
  • CA Injector: Webhook やカスタムリソースに CA バンドルを自動注入
  • ACME Solver: Let's Encrypt 等の ACME プロバイダーとのチャレンジ処理を実行

サポートする発行元(Issuer)

cert-manager のドキュメントによると、以下の認証局に対応しています:

  • Let's Encrypt (ACME プロトコル): 無料の TLS 証明書を自動発行
  • HashiCorp Vault: エンタープライズ PKI との連携
  • CyberArk Certificate Manager: 企業向け証明書管理
  • プライベート PKI: 自社認証局による証明書発行
  • Self-signed: テスト環境向けの自己署名証明書

Captain.AI は Kubernetes 環境全体のセキュリティ状態を AI で監視し、証明書の期限切れリスクを事前に検知する運用支援を提供します。

インストールと初期設定

Helm によるインストール

bash
# cert-manager の Helm リポジトリを追加
helm repo add jetstack https://charts.jetstack.io
helm repo update

# CRD を含めてインストール
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.18.1 \
  --set crds.enabled=true

インストールの確認

bash
# Pod の状態を確認
kubectl get pods -n cert-manager

# 出力例:
# cert-manager-5c6866597-zw7kh          1/1     Running   0
# cert-manager-cainjector-577f6d9fd7-b   1/1     Running   0
# cert-manager-webhook-56f8b4f8d-hsqvg   1/1     Running   0

ClusterIssuer の作成

Funky Si's Blog の実践ガイドを参考に、Let's Encrypt 用の ClusterIssuer を設定します:

yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        ingress:
          class: nginx

ステージング環境でのテスト用 Issuer も用意しておくことを推奨します:

yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
    - http01:
        ingress:
          class: nginx

Kubo では K3s に標準搭載される Traefik Ingress Controller との連携も可能です。

ACME チャレンジの仕組みと選択

Let's Encrypt は ACME(Automatic Certificate Management Environment)プロトコルを使用して、ドメインの所有権を検証します。KodeKloud の解説によると、cert-manager は主に 2 つのチャレンジタイプをサポートしています。

HTTP-01 チャレンジ

yaml
solvers:
- http01:
    ingress:
      class: nginx
  • let's encrypt がドメインの -.well-known-acme-challenge- エンドポイントにアクセスして検証
  • メリット: 設定がシンプル、追加の DNS 権限不要
  • デメリット: ポート 80 が外部からアクセス可能である必要がある、ワイルドカード証明書は非対応

DNS-01 チャレンジ

yaml
solvers:
- dns01:
    cloudflare:
      email: admin@example.com
      apiTokenSecretRef:
        name: cloudflare-api-token
        key: api-token
  • DNS レコードにトークンを設定して検証
  • メリット: ワイルドカード証明書をサポート、ポート 80 不要
  • デメリット: DNS プロバイダーの API 認証情報が必要

The Dougie Chronicles が詳しく解説しているように、Cloudflare を使った DNS-01 チャレンジは、特にワイルドカード証明書が必要な場合に最も一般的な選択肢です。

サポートされる DNS プロバイダー:

Ingress との連携と自動証明書発行

cert-manager の最も強力な機能の一つが、Ingress リソースとの自動連携です。

アノテーションによる自動発行

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - app.example.com
    secretName: app-tls-secret
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app
            port:
              number: 80

cert-manager.io/cluster-issuer アノテーションを追加するだけで、cert-manager が自動的に Certificate リソースを作成し、Let's Encrypt から証明書を取得して Kubernetes Secret に保存します。

Gateway API との連携

Kubernetes Gateway API を使う場合も同様に対応しています:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app-route
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  parentRefs:
  - name: my-gateway
  hostnames:
  - "app.example.com"
  rules:
  - backendRefs:
    - name: my-app
      port: 80

自動更新の仕組み

Medium の詳細記事が解説するように、cert-manager は証明書の有効期限を常に監視し、renewBefore で指定された期間前に自動的に更新を実行します。Let's Encrypt の証明書は 90 日間有効で、デフォルトでは期限の 30 日前に更新が開始されます。

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: app-tls
spec:
  secretName: app-tls-secret
  duration: 2160h    # 90 日
  renewBefore: 720h  # 30 日前に更新
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - app.example.com
  - www.app.example.com

Captain.AI と連携すれば、証明書の更新失敗を AI が検知し、自動的に対処策を提案するワークフローの構築が可能です。

トラブルシューティングとベストプラクティス

よくある問題と解決策

oneuptime のガイドF5 のベストプラクティスを参考に:

bash
# Certificate のステータス確認
kubectl describe certificate app-tls -n default

# CertificateRequest の確認
kubectl get certificaterequest -n default

# Order の確認(ACME の場合)
kubectl get order -n default

# Challenge の確認
kubectl get challenge -n default

# cert-manager のログ確認
kubectl logs -n cert-manager -l app=cert-manager

よくあるエラーと対処

エラー原因対処
Waiting for HTTP-01 challengeポート 80 がブロックファイアウォール設定を確認
DNS record not yet propagatedDNS 伝播の遅延--dns01-recursive-nameservers を設定
rate limitedLet's Encrypt のレート制限ステージング環境でテスト
account is not registeredACME 登録の失敗email フィールドを確認

セキュリティのベストプラクティス

  • ステージング Issuer でテストしてから本番 Issuer に切り替え
  • RBAC で Certificate リソースへのアクセスを制限
  • Apurv のテックノートが推奨するように、API トークンは Kubernetes Secret で安全に管理
  • 証明書の監視アラートを設定(Prometheus メトリクス certmanager_certificate_expiration_timestamp_seconds

まとめ

cert-manager は Kubernetes 環境における TLS 証明書管理の労力を劇的に削減します。本記事のポイントをまとめると:

  1. cert-manager は証明書の発行・更新・管理を完全に自動化
  2. Let's Encrypt との連携で無料の TLS 証明書を自動取得
  3. HTTP-01 / DNS-01 チャレンジの選択で様々な環境に対応
  4. Ingress アノテーション で既存のワークロードに最小限の変更で導入
  5. 自動更新 により証明書期限切れのリスクをゼロに

Kubo は K3s ベースで CNCF エコシステムとの高い親和性を持ち、cert-manager の導入により安全なサービス公開を簡単に実現できます。Kubernetes 環境のセキュリティ強化に取り組む方は、ぜひ Kubo をご検討ください。

AI を活用した Kubernetes 運用の自動化については、Captain.AI の詳細をご確認ください。導入のご相談はお問い合わせからお気軽にどうぞ。

← Back to all posts