cert-manager と Cloudflare DNS で Envoy Gateway の証明書を自動管理する
Table of Contents
1. はじめに
みなさん、Kubernetes クラスターの証明書管理してますか?
前回の記事では、Envoy Gateway を使って argocd-server に HTTPS でアクセスする環境を構築しました。 その際、証明書は Ansible で自己署名の CA/Server 証明書を作成して Kubernetes Secret に登録するという方法を取っていたのですが、 記事の最後に「もしかしたら cert-manager とかを使った方が楽だったかもしれませんが、終わってから気づきました…」と書いていました。
というわけで今回は、cert-manager を使って Let’s Encrypt の証明書を Cloudflare DNS01 チャレンジ で自動発行・更新する方法を紹介します。 これにより、自己署名証明書からの脱却と、証明書の自動更新を実現します。
2. cert-manager について
cert-manager は、Kubernetes クラスター内で TLS 証明書の発行・更新を自動化してくれるツールです。 Let’s Encrypt などの ACME 対応の CA(認証局)と連携して、証明書のライフサイクルを自動で管理してくれます。
証明書の発行には ACME プロトコルを使用しますが、ドメインの所有を証明するためのチャレンジ方法がいくつかあります。 今回は DNS01 チャレンジ を使います。 DNS01 チャレンジは、ドメインの DNS レコードに特定の TXT レコードを追加することでドメインの所有を証明する方法です。
3. 前提環境
- Kubernetes クラスター: 自宅サーバー上の環境(kubeadm + OVN-Kubernetes)
- Envoy Gateway: v1.5.0(前回の記事でインストール済み)
- ドメイン管理: Cloudflare(
home.tomokon.net) - cert-manager: v1.18.2
Envoy Gateway の GatewayClass や Gateway リソースは前回の記事で作成済みの前提で進めます。
4. cert-manager のインストール
cert-manager のインストールは kustomize で管理しています。
4.1 kustomize の構成
kustomize/cert-manager/core/
├── kustomization.yaml
└── patch-enable-gateway-api.yaml
kustomization.yaml では、cert-manager の公式マニフェストをリモートリソースとして参照し、
Gateway API を有効化するためのパッチを当てています。
# kustomize/cert-manager/core/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml
patches:
- path: patch-enable-gateway-api.yaml
4.2 Gateway API 有効化パッチ
cert-manager はデフォルトでは Gateway API のサポートが無効になっています。
今回は Envoy Gateway(Gateway API 実装)と連携させたいので、--enable-gateway-api フラグを有効にします。
# kustomize/cert-manager/core/patch-enable-gateway-api.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cert-manager
namespace: cert-manager
spec:
template:
spec:
containers:
- name: cert-manager-controller
args:
- --v=2
- --cluster-resource-namespace=$(POD_NAMESPACE)
- --leader-election-namespace=kube-system
- --acme-http01-solver-image=quay.io/jetstack/cert-manager-acmesolver:v1.18.2
- --max-concurrent-challenges=60
# Additional arguments
- --enable-gateway-api
- --dns01-recursive-nameservers-only
- --dns01-recursive-nameservers=1.1.1.1:53,1.0.0.1:53
cert-manager は DNS01 チャレンジの際に、TXT レコードが正しく設定されているかを self-check します。
筆者の環境では自宅サーバー上の DNS サーバー(BIND9)が tomokon.net の権威サーバーとして動いています。
そのため、cert-manager が home.tomokon.net の TXT レコードを確認しようとすると、
ローカルの権威サーバーが Cloudflare に聞きに行かずに自分で解決してしまい、
DNS01 チャレンジ用の TXT レコードが見つからず self-check が失敗し続けてしまいます。
--dns01-recursive-nameservers-only を指定すると /etc/resolv.conf を無視して、
--dns01-recursive-nameservers で指定したネームサーバーのみを使うようになります。
ここでは Cloudflare のパブリック DNS(1.1.1.1 と 1.0.0.1)を指定することで、
Cloudflare 側に追加された TXT レコードを正しく確認できるようにしています。
5. Cloudflare API Token の準備
cert-manager が Cloudflare の DNS レコードを操作するためには、API Token が必要です。
5.1 API Token の作成
Cloudflare のダッシュボードから API Token を作成します。必要な権限は以下の通りです。
- Zone - DNS - Edit: DNS レコードの追加・削除(TXT レコードの操作に必要)
- Zone - Zone - Read: ゾーン情報の読み取り(ゾーン ID の取得に必要)
Zone Resources は Include - All Zones に設定するか、
特定のゾーンだけに絞りたい場合は Include - Specific zone で対象のゾーン(例: tomokon.net)を指定します。
最小権限の原則に従うなら後者がおすすめです。
5.2 Kubernetes Secret の作成
作成した API Token を Kubernetes Secret として登録します。
kubectl create secret generic cloudflare-api-token \
--from-literal=api-token=<YOUR_CLOUDFLARE_API_TOKEN> \
-n cert-manager
6. ClusterIssuer の作成
ClusterIssuer は、 cert-manager が証明書を発行するための設定をクラスター全体で共有するリソースです。 Issuer と違ってクラスタースコープなので、どの Namespace からでも利用できます。
# kustomize/cert-manager/config/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-cloudflare
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-cloudflare
solvers:
- dns01:
cnameStrategy: Follow
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
各フィールドを簡単に説明します。
server: Let’s Encrypt の ACME v2 本番エンドポイントemail: Let’s Encrypt のアカウントに紐づけるメールアドレス(証明書の有効期限通知などに使われます)privateKeySecretRef: ACME アカウントの秘密鍵を保存する Secret の名前solvers: DNS01 チャレンジに Cloudflare を使用cnameStrategy: Follow: CNAME レコードがある場合にそれを辿って解決する設定apiTokenSecretRef: 先ほど作成した Cloudflare API Token の Secret を参照
7. Gateway リソースの更新
前回の記事では自己署名証明書を Secret に手動で登録していましたが、 今回は cert-manager に証明書の発行を任せます。
cert-manager は Gateway API と連携する際、Gateway リソースに付与された annotation を読み取り、 自動的に Certificate リソースを作成してくれます。
# kustomize/envoy-gateway/config/gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: envoy-http
namespace: gateway-system
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cloudflare
spec:
gatewayClassName: envoy
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.home.tomokon.net"
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: cert-home-tomokon-net
前回の記事からの変更点は annotations に cert-manager.io/cluster-issuer: letsencrypt-cloudflare を追加した部分です。
この annotation があることで、cert-manager が自動的にこの Gateway の listener を監視してくれます。
cert-manager が Certificate リソースを自動作成するために、listener は以下の条件を満たす必要があります。
hostnameが指定されていること(空は不可)tls.modeがTerminateであることtls.certificateRefs[].nameが指定されていること(この名前で Secret と Certificate が作成される)
つまり、cert-home-tomokon-net という名前で Certificate リソースと Secret が自動的に作成され、
Let’s Encrypt から *.home.tomokon.net のワイルドカード証明書が発行されるという流れです。
8. 動作確認
すべてのリソースを apply した後、証明書が正常に発行されたか確認します。
8.1 Certificate リソースの確認
$ kubectl get certificate -n gateway-system
NAME READY SECRET AGE
cert-home-tomokon-net True cert-home-tomokon-net 5m
READY が True になっていれば、証明書の発行が成功しています。
もし False のままの場合は、以下のコマンドでトラブルシューティングできます。
# Certificate の詳細を確認
kubectl describe certificate cert-home-tomokon-net -n gateway-system
# CertificateRequest の確認
kubectl get certificaterequest -n gateway-system
# ACME Order の確認
kubectl get order -n gateway-system
# ACME Challenge の確認(DNS01 チャレンジの状態)
kubectl get challenge -n gateway-system
# cert-manager のログ確認
kubectl logs -n cert-manager deployment/cert-manager
8.2 HTTPS アクセスの確認
$ curl -I https://argocd.home.tomokon.net
HTTP/2 200
content-type: text/html; charset=utf-8
...
ブラウザで確認すると、証明書の発行元が Let’s Encrypt になっていることが確認できます。 自己署名証明書のときはクライアント側に CA 証明書をインポートする必要がありましたが、 Let’s Encrypt の証明書であればその手間も不要です。
9. まとめ
今回は cert-manager + Let’s Encrypt + Cloudflare DNS01 を使って、 Envoy Gateway の TLS 証明書を自動管理する環境を構築しました。
前回の Ansible での手動管理と比べると、以下の点が改善されました。
- 証明書の自動発行: Gateway リソースに annotation を追加するだけで証明書が自動発行される
- 証明書の自動更新: cert-manager が有効期限を監視して自動的に更新してくれる
- 信頼された証明書: Let’s Encrypt の証明書なのでクライアント側に CA 証明書をインポートする必要がない
前回の記事で「cert-manager を使った方が楽だったかも」と書いていましたが、実際にやってみて本当にそうでした。 特に証明書の自動更新は、自己署名証明書のときには手動で管理する必要があったので、めっちゃ助かります。
最後まで読んでいただき、ありがとうございました!