cert-managerのワイルドカード証明書をreflectorで使い回す
複数namespaceでワイルドカード証明書を使いたい
kubernetesクラスタ上において cert-manager で生成したドメインごとの証明書を複数のnamespaceで利用したい場合、各namespace毎に証明書を生成して利用すればよい。
しかし、ワイルドカード証明書であれば1つの証明書で複数のサブドメインに対応できるため、同じ証明書を複数のnamespaceで使い回したい。利用の度にワイルドカード証明書を発行したくない。
cert-managerが生成する証明書はSecretとして保存されるが、Secretは作成されたnamespace内でのみ利用可能という制約がある。
すなわち、cert-managerのnamespaceに作成・保存されたワイルドカード証明書はcert-managerのnamespaceでしか利用できず、別のexample-app1 やexample-app2といったnamespaceで利用することができない。これが不便。
reflectorによる証明書の自動複製
この制約を回避する方法として、reflectorを利用してリソースを別のnamespaceに複製する方法がある。
secretも操作可能で、cert-managerでオリジナルとなる証明書を発行し、これをreflectorで必要となるnamespaceにコピーして利用する。
これにより、k8sクラスタ全体で一度発行したワイルドカード証明書を使いまわすことができるようになる。
cert-managerが証明書を随時更新した場合もreflectorが適宜反映してくれるので、証明書の更新毎の運用負荷が生じないのもうれしい。
実装手順
1. reflectorのセットアップ
reflectorはHelm提供されているのでこれを利用してインストールする。
replicaCount: 1
configuration:
logging:
minimumLevel: Information
watcher:
timeout: 60
helm repo add emberstack https://emberstack.github.io/helm-charts
helm repo update
helm install reflector emberstack/reflector \
--namespace kube-system \
--values reflector-values.yaml
2. cert-managerでワイルドカード証明書を生成
Let's Encryptを使用してワイルドカード証明書を生成する例は以下の通り。
ワイルドカード証明書を発行するにはDNSチャレンジ方式が必要。例えばcloudflareを利用していれば、cloudflareのapi tokenをsecretに格納しこれを指定すればよい。
これによりワイルドカード証明書が発行されて secretへと格納される。
# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
namespace: cert-manager
spec:
secretName: wildcard-cert-secret
secretTemplate:
annotations:
reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: ""
reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- "*.example.com"
---
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
selector:
dnsZones:
- example.com
特徴はCertificateリソースに spec.secretTemplate.annotations として
reflector.v1.k8s.emberstack.com/{reflection-allowed,reflection-allowed-namespaces,reflection-auto-enabled} のようなreflectorが利用する複製に関するアノテーションを指定しているところ。
アノテーションの意味は以下の通り:
-
reflection-allowed: "true": reflectorによる複製を許可 -
reflection-allowed-namespaces: "": 空文字列は全namespaceを意味、特定のnamespaceに限定する場合は"app1,app2"のように指定 -
reflection-auto-enabled: "true": reflectorによる自動複製を有効化
この設定であれば全namespaceに証明書が格納されたsecretが生成される。セキュリティ上、生成先を制御したい場合は allowed-namespacesを指定して制御する。
3. 動作確認
証明書が正常に複製されているか確認する:
# 元の証明書の確認
kubectl get secret wildcard-cert-secret -n cert-manager
# 複製された証明書の確認
kubectl get secret wildcard-cert-secret -n app1
kubectl get secret wildcard-cert-secret -n app2
# 証明書の詳細確認
kubectl describe secret wildcard-cert-secret -n app1
4. 手動reflection設定(オプション)
自動複製を使わない場合は、複製先のnamespaceで reflector.v1.k8s.emberstack.com/reflects アノテーションを指定したSecretを事前に作成する必要がある。
これにより、複製のタイミングなどを制御できるようになる。
apiVersion: v1
kind: Secret
metadata:
name: wildcard-cert-secret
namespace: app1
annotations:
reflector.v1.k8s.emberstack.com/reflects: "cert-manager/wildcard-cert-secret"
type: kubernetes.io/tls
data: {}
複製されたSecretの使用
各namespaceで複製された証明書を利用する場合は特に考慮すべき点は無い。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: app1
spec:
tls:
- hosts:
- app1.example.com
secretName: wildcard-cert-secret
rules:
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
注意点とセキュリティ考慮事項
secretTemplateでreflectorの設定を反映する
kind: Certificateのリソースは証明書を生成する設定なので、このリソースをreflectorで複製しても意味はなく、Certificateリソースが発行してsecretリソースに格納するTLS証明書にアノテーションを付与してたい。
これにより、生成した証明書が各namespaceに配布されるようになる。
secretTemplateはまさにそのような目的の設定で、ここに指定されたアノテーションが生成されたsecretリソースのアノテーションに付与される。
これによりreflectorにより複製して欲しいリソースに対して適切にアノテーションが付与できる。
DNS設定要件
Cloudflareを使用する場合、以下の権限が必要:
- Zone:Zone:Read
- Zone:DNS:Edit
APIトークンは適切なゾーンにスコープを限定する。
Ingress annotationのサポート
Ingressにて証明書をアタッチして利用したい場合は、個別にcertを生成・reflectして利用する設定を行わなくてもよい。
Ingressに直接アノテーションを付与することで自動で発行して関連付けることができる。
ただし、これはワイルドカード証明書ではなくホスト毎に発行している場合のみなので注意。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: app1
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
cert-manager.io/secret-template: |
{"annotations": {"reflector.v1.k8s.emberstack.com/reflection-allowed": "true", "reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces": ""}}
spec:
tls:
- hosts:
- app1.example.com
secretName: app1-tls-secret
rules:
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
参考
-
https://github.com/emberstack/kubernetes-reflector
- 基本的な情報はreflectorのREADMEにまとまっている。
-
https://cert-manager.io/v1.6-docs/faq/sync-secrets/
- cert-managerのドキュメントでも複数のNamespaceで利用するための手法としてreflectorが取り上げられている
-
https://letsencrypt.org/docs/rate-limits/
- 動作検証のため何度も証明書を発行しているとrate-limitにひっかかるので注意
-
https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/
- cert-managerでCloudflareを用いてDNSチャレンジで証明書を発行する場合はその設定方法にも注意する
Discussion