内部CAが発行する証明書を手動で管理し続けると、期限切れの見落としや更新漏れによるサービス断のリスクが高まります。Let’s EncryptがACMEプロトコル(RFC 8555)で証明書の自動発行・更新を広く普及させたように、内部CAに対して同じモデルを適用する動きが2024年以降に加速しています。本記事では、Smallstep社のStep CAをACMEエンドポイントとして構成し、cert-managerから自動更新を行う具体的な手順と、更新失敗時の切り戻し設計をまとめます。
内部CA運用における証明書ライフサイクルの課題
オンプレミスや閉域クラウド環境では、TLS証明書をプライベートCA(内部CA)から発行するケースが多くあります。この運用で繰り返し問題になるのは主に2点です。
- 有効期限の手動追跡:スプレッドシートやカレンダーで管理していると、更新忘れや担当者の異動による漏れが起きやすくなります。
- 有効期間の長期化による攻撃面拡大:手動更新の手間を避けて1年や2年の証明書を発行すると、鍵が漏洩しても長期間にわたって悪用されるリスクが残ります。
ACMEプロトコルを内部CAに組み込むと、クライアントが自律的に証明書を取得・更新するため、有効期間を短く(例:90日以下)設定しても運用コストが増えません。短い有効期間は鍵漏洩時の影響範囲を縮小し、証明書の健全性を維持しやすくなります。
ACMEプロトコルと内部CA ~ 仕組みの整理
ACMEはRFC 8555で標準化されたプロトコルで、ドメイン所有者(またはホスト運用者)がチャレンジを経由してCAからX.509証明書を取得・更新・失効させる手順を定義しています。Let’s Encryptが実装した外向きのACMEと同じ仕様を内部CAサーバーが実装すれば、既存のACMEクライアント(certbot・acme.sh・cert-manager等)をそのまま再利用できます。
内部CA向けACMEサーバーとして広く使われているのが Step CA(Smallstep) です。Step CAは単体バイナリで動作し、ACME v2エンドポイントを標準で提供します。チャレンジタイプはHTTP-01・TLS-ALPN-01・DNS-01を選択でき、クローズドネットワーク内でもHTTP-01チャレンジを用いた証明書発行が可能です。
Kubernetes環境では cert-manager が実質標準のACMEクライアントとして機能します。cert-managerは ClusterIssuer リソースにACMEエンドポイントを指定することで、Secretに保存された証明書の自動更新を担います。
Step CA × cert-manager の構築手順
Step CAにACMEプロビジョナーを追加する
Step CAを初期化済みの状態(step ca init 完了後)から始めます。ACMEプロビジョナーを追加するには step ca provisioner add コマンドを使います。
# ACMEプロビジョナーを追加(名称は任意)
step ca provisioner add acme --type ACME
# 設定を反映させるためStep CAを再起動
systemctl restart step-ca
追加後、ACMEエンドポイントは https://<step-ca-host>/acme/acme/directory の形式で提供されます。証明書チェーンをクライアントが検証できるよう、Step CAのルートCA証明書($(step path)/certs/root_ca.crt)をノードのトラストストアまたはKubernetesのSecretとして配布しておきます。
cert-managerのClusterIssuerを作成する
cert-managerをHelm等でインストール済みの前提で、Step CAのACMEエンドポイントを参照する ClusterIssuer を定義します。
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca-acme
spec:
acme:
server: https://step-ca.internal:9000/acme/acme/directory
# Step CAのルートCA証明書をBase64エンコードした値
caBundle: <base64エンコードしたroot_ca.crt>
email: ops@example.internal
privateKeySecretRef:
name: internal-ca-acme-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
caBundle フィールドを省略するとcert-managerが公的なルートストアで検証しようとして失敗します。内部CAを使う場合は必須の設定項目です。
Certificate リソースを作成すると、cert-managerがACMEフローを実行してSecretに証明書と秘密鍵を格納します。証明書の有効期限が残り1/3になると自動更新が始まる動作がデフォルトです(renewBefore の設定で変更できます)。
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: web-tls
namespace: production
spec:
secretName: web-tls-secret
issuerRef:
name: internal-ca-acme
kind: ClusterIssuer
dnsNames:
- web.internal.example.com
duration: 2160h # 90日
renewBefore: 720h # 30日前から更新
自動更新の検証とアラート設計
構成が完了したら、まず更新サイクルが実際に動くことを確認します。有効期間を短く設定して強制更新をテストする方法が確実です。
# Certificateリソースのステータスを確認
kubectl describe certificate web-tls -n production
# 証明書の有効期限をOpenSSLで直接確認
kubectl get secret web-tls-secret -n production \
-o jsonpath='{.data.tls\.crt}' | \
base64 -d | openssl x509 -noout -dates
# cert-managerに強制更新を促す(Annotationを削除して再評価させる)
kubectl annotate certificate web-tls -n production \
cert-manager.io/issueRequestName- \
--overwrite
cert-managerはPrometheusメトリクスを標準で公開しています。以下のメトリクスをGrafana等で可視化し、アラートを設定しておくことで更新失敗を即座に検知できます。
certmanager_certificate_expiration_timestamp_seconds:証明書の有効期限のUnixタイムスタンプ。certmanager_certificate_ready_status:証明書の発行状態(True/False/Unknown)。certmanager_http_acme_client_request_duration_seconds:ACMEエンドポイントへのリクエスト応答時間。
想定例として、有効期限まで15日以内かつ ready_status != True の組み合わせでAlertManagerに通知するルールを設定しておくと、更新停止をログ確認より先に検知できます。アラート閾値と有効期間・renewBefore のバランスは運用環境に合わせて調整が必要です。
失敗時の切り戻しと手動更新への降格手順
自動更新が失敗するパターンとして多いのは次の3つです。
- ACMEエンドポイント(Step CA)が応答しない:ネットワーク障害やStep CAのプロセス停止が原因になります。
- HTTP-01チャレンジが通らない:Ingressの設定変更やIPSによるブロックが典型的です。
- アカウント鍵のSecretが削除・破損している:Secretのバックアップ漏れで発生します。
いずれの場合も、cert-managerは CertificateRequest リソースを失敗状態のまま残します。まずステータスを確認します。
# 失敗したCertificateRequestの一覧を確認
kubectl get certificaterequests -A | grep -v Approved
# 詳細なエラーメッセージを確認
kubectl describe certificaterequest <name> -n <namespace>
緊急時の手動更新 ~ Step CA CLIを使う
cert-managerのACMEフローが復旧しない場合は、Step CA付属の step CLIを使って手動で証明書を発行し、既存のSecretを上書きする切り戻し手順を事前に整備しておくことが重要です。
# step CLIで手動発行(JWKプロビジョナー等を使う)
step ca certificate web.internal.example.com web.crt web.key \
--provisioner <provisioner-name> \
--not-after 720h
# 発行した証明書でKubernetesのSecretを更新
kubectl create secret tls web-tls-secret \
--cert=web.crt \
--key=web.key \
--namespace production \
--dry-run=client -o yaml | kubectl apply -f -
手動更新後はcert-managerによるSecretの再上書きを防ぐため、Certificate リソースに cert-manager.io/paused: "true" アノテーションを付与するか、cert-managerのコントローラーを一時停止する措置を取ります。ACMEエンドポイントが復旧したあとにアノテーションを外せば自動更新が再開されます。
アカウント鍵のバックアップと復旧
cert-managerのACMEアカウント鍵(internal-ca-acme-account-key Secret)が失われると、Step CA側のアカウントとの紐付けが取れなくなります。このSecretは定期的に外部のKMSやSealedSecretsでバックアップしておくことを推奨します。再作成が必要な場合は ClusterIssuer を削除・再作成すると新しいACMEアカウントが払い出されます。
2026年現行環境での差分と注意点
cert-manager v1.16以降の変更点
cert-manager v1.16(2024年末リリース)以降では、Certificate リソースの spec.keystores フィールドが安定版に昇格し、JKS・PKCS#12形式のキーストア出力が正式サポートになりました。Javaアプリケーションへ証明書を配布する際に追加の変換スクリプトが不要になります。また Issuer/ClusterIssuer の status.conditions がより詳細なメッセージを返すようになっており、エラー解析が容易になっています。
Step CA v0.27以降の変更点
Step CA v0.27以降では設定ファイル(ca.json)のスキーマが一部変更されています。旧バージョンから移行する場合は step ca migrate サブコマンドで設定を自動変換できます。また、ACMEチャレンジのTLS-ALPN-01実装がRFC 8737完全準拠となり、一部のNginxバージョンで発生していたハンドシェイク失敗が解消されています。
短命証明書戦略との組み合わせ
NIST SP 800-204DやCNCFのゼロトラスト推奨では、ワークロード証明書の有効期間を24時間以内にする短命証明書戦略が言及されています。ACMEによる自動更新が確立された後は、有効期間を段階的に短縮する運用ロードマップを描くことが長期的なセキュリティ成熟度向上につながります。ただし、有効期間を24時間以下にする場合は更新失敗のアラート閾値やcert-managerの renewBefore 設定を見直す必要があり、切り戻し手順の演習頻度も高めることが求められます。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
PR・広告
コマンド操作から基本運用までを手を動かして学べる定番書。記事で触れた技術の土台固めに。
※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。
