ACME(証明書の自動更新)手順書

cert-manager 導入&設定ガイド(FujiSSL ACME)

2025年10月20日

対象:Kubernetes(1.22+ 目安)。Helm or kubectl(CRDs)で導入。HTTP-01(Ingress) / DNS-01(Cloudflare/Route53)/ EAB(外部アカウントバインディング)対応。


外部公開における重要な注意
管理ツール「契約一覧」に表示される EAB KID / EAB HMAC Key機密情報です。GitHub 等の公開リポジトリやブログ、資料に絶対に記載しないでください。Kubernetes Secret は namespaced に作成し、RBAC/バックアップの取り扱いに注意してください。

0. 前提とゴール

  • FujiSSL 管理ツールで ACME 契約(OV) を作成済み、以下4点を控える:
    ACMEサーバURL(例:https://ユーザによって異なります)/ドメイン(FQDN)EAB KIDEAB HMAC Key
  • ゴール:Ingress の注釈だけで自動発行・自動更新(Let’s Encryptでは不可なOV + ACMEを FujiSSL で実現)。

1. cert-manager のインストール

方法A:Helm(推奨)

kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --set crds.enabled=true
# バージョン固定したい場合は --version v1.xx.x を付与

方法B:kubectl(CRDs マニフェスト)

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.crds.yaml
kubectl create namespace cert-manager
kubectl apply -n cert-manager -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

導入確認
kubectl -n cert-manager get podscert-manager/cainjector/webhook がすべて Running を確認。

2. EAB シークレットの作成

# 例:cert-manager 名前空間に保存(ClusterIssuer を使う想定)
kubectl -n cert-manager create secret generic fujissl-eab-secret \
  --from-literal=hmac-key='<EAB_HMAC_KEY>'
  • 名称は後続の Issuer/ClusterIssuer の externalAccountBinding.keySecretRef と一致させます。
  • KID は Secret ではなく、Issuer 定義内の keyID にそのまま記載します(リポジトリ公開は避けること)。

3. Issuer / ClusterIssuer の作成(FujiSSL ACME + EAB)

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: fujissl-ov-issuer
spec:
  acme:
    server: 管理画面で取得したACMEサーバURL
    email: admin@example.com
    privateKeySecretRef:
      name: fujissl-ov-account-key
    externalAccountBinding:
      keyID: <EAB_KID>                       # ← 管理ツール表示の KID
      keySecretRef:
        name: fujissl-eab-secret             # ← 先ほど作成した Secret
        key: hmac-key
    solvers:
    - http01:
        ingress:
          class: nginx                       # or "alb", "traefik" など
  • namespaced 運用にしたい場合は Issuer を使い、同一 namespace に Secret も配置。
  • HTTP-01 を使う場合は上記そのままでOK。DNS-01 を使う場合は後述の solver を追記します。

4. HTTP-01(Ingress)での発行

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-https
  annotations:
    cert-manager.io/cluster-issuer: fujissl-ov-issuer
    acme.cert-manager.io/http01-edit-in-place: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    - www.example.com
    secretName: tls-example-com                # ← 証明書が保存される Secret
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80
  • 80/TCP が到達可能であること(CDN を使う場合 /.well-known/acme-challenge/素通しに)。
  • 作成後、cert-manager が Order/Challenge を生成し、成功すれば tls-example-com Secret に証明書が入ります。

5. DNS-01(Cloudflare / Route53)での発行

5-1) Cloudflare(API Token)

# API Token を Secret 化(最小権限:Zone.DNS:Edit)
kubectl -n cert-manager create secret generic cloudflare-api-token \
  --from-literal=api-token='<CF_API_TOKEN>'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: fujissl-ov-issuer
spec:
  acme:
    server: 管理画面で取得したACMEサーバURL
    email: admin@example.com
    privateKeySecretRef:
      name: fujissl-ov-account-key
    externalAccountBinding:
      keyID: <EAB_KID>
      keySecretRef:
        name: fujissl-eab-secret
        key: hmac-key
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            name: cloudflare-api-token
            key: api-token
  • 以後、Ingress 側は tls.hosts*.example.com などを指定するとワイルドカードも自動発行されます。

5-2) AWS Route53(IRSA or Profile)

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: fujissl-ov-issuer
spec:
  acme:
    server: 管理画面で取得したACMEサーバURL
    email: admin@example.com
    privateKeySecretRef:
      name: fujissl-ov-account-key
    externalAccountBinding:
      keyID: <EAB_KID>
      keySecretRef:
        name: fujissl-eab-secret
        key: hmac-key
    solvers:
    - dns01:
        route53:
          region: ap-northeast-1                 # 変更
          hostedZoneID: ZXXXXXXXXXXXX            # 省略も可(自動検出に頼る運用も可)
          role: arn:aws:iam::123456789012:role/cert-manager-dns   # IRSA/RAM
  • 必要権限:route53:ChangeResourceRecordSets 等。IRSA(ServiceAccount にロール付与)がおすすめ。
  • アクセスキー直書きは避け、どうしても必要なら Secret で渡して RBAC・監査を徹底。

6. 動作確認とローテーション

# リソースの状態
kubectl get issuer,clusterissuer,certificate,order,challenge -A

# 失敗時の詳細
kubectl describe order <name> -n <ns>
kubectl describe challenge <name> -n <ns>
kubectl logs -n cert-manager deploy/cert-manager --tail=200

# 秘密鍵/証明書の中身確認(開発環境のみで)
kubectl get secret tls-example-com -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -text | less
  • cert-manager は期限が近い証明書を自動で更新し、secretName を上書きします(Ingress 側の再起動は不要)。
  • 周辺プロキシ/ゲートウェイが Secret をマウントしている場合は、サイドカーやリロード設定で追随させます。

7. DNS/ネットワークの実務メモ

  • HTTP-01:対象 FQDN が Ingress へ到達(80/TCP)。/.well-known/acme-challenge/ はリライト/リダイレクト/キャッシュしない。
  • DNS-01:TXT は _acme-challenge.<FQDN>。ワイルドカードは _acme-challenge.example.com に集約。TTL は 60–300 秒。
  • CDN:発行/更新時はチャレンジをバイパス(Cloudflare は灰色雲、または該当パスのキャッシュ無効)。
  • CAA:独自運用している場合のみ、使用 CA を許可。誤設定は発行失敗の主要因。

8. セキュリティ(EAB/DNSトークン)

  • EAB KID/HMAC・DNS API トークンは Kubernetes Secret に限定保存。GitOps 運用時は SealedSecrets/ExternalSecrets 等の仕組みで暗号化。
  • RBAC:get/list/watch のみ最小限付与。Secret の範囲は namespace 単位に分離。
  • バックアップ:Secret を含む etcd バックアップの暗号化・持ち出し防止に注意。

9. トラブルシュート(定型)

  • Order が失敗:kubectl describe order で理由を確認。CAA/到達性/権限の順で点検。
  • HTTP-01 失敗:80/TCP 未開放/リダイレクトで別FQDNへ/WAF がブロック。/.well-known/acme-challenge/ を素通しに。
  • DNS-01 失敗:TXT 値が反映前/委任ゾーンの編集漏れ。dig +short TXT _acme-challenge.example.com @1.1.1.1 で確認。
  • Rate limit:検証は SAN を絞って回数を節約。問題が解消したら本番SANで再実行。
  • Webhook 失敗:cert-manager の Pod/イベントログを確認。CRD のバージョン不整合に注意。

10. 追加例:Namespace ごとの Issuer、複数ドメイン

A) Namespace ローカル Issuer(隔離運用)

apiVersion: v1
kind: Secret
metadata:
  name: fujissl-eab-secret
  namespace: app
type: Opaque
data:
  hmac-key: <base64-encoded EAB_HMAC_KEY>

---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: fujissl-ov-issuer
  namespace: app
spec:
  acme:
    server: 管理画面で取得したACMEサーバURL
    email: app-admin@example.com
    privateKeySecretRef:
      name: fujissl-ov-account-key
    externalAccountBinding:
      keyID: <EAB_KID>
      keySecretRef:
        name: fujissl-eab-secret
        key: hmac-key
    solvers:
    - http01:
        ingress:
          class: nginx

B) 1つの Ingress で複数FQDN

spec:
  tls:
  - hosts: [example.com, www.example.com, api.example.com]
    secretName: tls-example-com

HTTP-01 の場合はすべての FQDN がこの Ingress に到達する必要あり。到達しない FQDN は DNS-01 を使う。

よく読まれている質問