モチベーション
k8sならマルチテナントを1クラスタで束ねる事もお手の物なんですが、そんな時に課題になるのがSSL証明書の発行です。
グローバル証明書はそれなりにお高いです。グローバル証明書を持っていない。もしくは取得するまでもない用途の場合、無料で証明書を発行しまくる事ができたら嬉しくないですか?k8sで。
自分の場合は、無限にステージング環境を構築する必要があったので、その為の証明書発行のソリューションが必要でした。
cert-manager
cert-managerは、さまざまな発行元からのTLS証明書の管理と発行を自動化するKubernetesアドオンです。(Google翻訳。。)
現在、cert-managerはLet's encryptをサポートしています。cert-managerを使うとLet's encryptからの証明書の取得および管理をk8s上で自動で行えます。もちろん自動更新も。
cert-managerの導入方法は以下の記事が参考になります。
GKE で TLS 証明書を自動管理(cert-manager DNS-01 編)
※ 本記事ではcert-managerの導入については割愛します。
導入はあっさり、だけど。。
導入は簡単でした。
ただ、発行する頻度によりきりですが、自分の場合は無限にサブドメインを利用する必要があり、その要件を満たすには一筋縄ではいきませんでした。
やってみたこと
その1 各テナント毎にグローバル証明を発行する
1テナントで複数のサブドメインを発行する必要がありました。
なので管理が楽なグローバル証明書でいく事にしました。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: certificate
spec:
secretName: cert-manager-tls
issuerRef:
name: cluster-issuer
kind: ClusterIssuer
commonName: "*.example.com"
dnsNames:
- "*.example.com"
acme:
config:
- dns01:
provider: provider-xxx
domains:
- "*.example.com"
これを各namespaceで使い回せば実質無限に証明書発行できると踏んだんです。
しかし、、、
Let's encryptにはRate Limitsというものが存在します。
制限と仕様からLet's Encrypt(ACMEv1)の話 でも丁寧にまとめられています。
同一ドメインの発行回数に制限があります。
週に数十テナントを立てないといけなかったため、これに引っかかりました。
その2 グローバル証明書じゃなくて個別に発行
そもそもいくつでも発行できるんだったらグローバル証明書じゃなくてもいいじゃん。
という事で個別に発行してみる事にしました。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: certificate
spec:
secretName: cert-manager-tls
issuerRef:
name: cluster-issuer
kind: ClusterIssuer
dnsNames:
- x.example.com
- x-a.example.com
- x-b.example.com
acme:
config:
- dns01:
provider: xxx-provider
domains:
- x.example.com
- x-a.example.com
- x-b.example.com
そしたらまた証明書発行の制限に引っかかりました。
サブドメインの発行も制限があります。
Rate Limits にちゃんと書いてありました。よく読め!って話でした。。
結局
自分的には敗北でしたが、、もう諦めて取得したグローバル証明書を使い回す事にしました。
以下のようにグローバル証明書をネームスペース kube-system
で取得しそれを各テナントで使い回すって方針にしました。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: certificate
namespace: kube-system
spec:
secretName: cert-manager-tls
issuerRef:
name: cluster-issuer
kind: ClusterIssuer
commonName: "*.example.com"
dnsNames:
- "*.example.com"
acme:
config:
- dns01:
provider: provider-xxx
domains:
- "*.example.com"
k8sでは基本コンセプトとしてネームスペースを跨いだリソースの共有を許さないので、そのままこれを各テナントで利用する事はできません。
secretを共有する方法
ここがキモなんですが、原始的な方法で対応しました。
前述のyamlではcert-managerが取得した証明書は cert-manager-tls
という名前のsecretとして保存されます。
これを新しいテナントを立てる時に、ダウンロードして一緒に含めるという方法です。
kubectl get secret cert-manager-tls -n kube-system -o yaml > cert-manager-tls.yml
これで既に取得済みのグローバル証明書が手元にダウンロードされるので、これを含めて
kubectl apply -f <dir name>
を決めます。
これで一件落着。
ただし
この方法で作ったテナントは当然ながらcert-managerの自動更新の恩恵を受ける事はできません。
Let's encryptの証明書の有効期限は90日なので、これ以上長いライフサイクルのアプリケーションには適用できません。
今回の自分のケースでは、ステージング環境なので長くても1-2週間で破棄される環境な為、自動更新は必要なかったのでこの方法がはまりました。
自動更新する手段はないのか?
あります。
方法としては、CronJob
を使って、テナント側から定期的に証明書をダウンロードして差し替えるという技を使えます。
以下のコメントにその例が記載されています。
https://github.com/jetstack/cert-manager/issues/273#issuecomment-370292348
必要であればこの対応でいけると思います。
最後に
k8sはアドオンが物凄い勢いで開発されていています。ただ、しっくりフィットしなくて手を加えなければいけなかったり何かしらの対応に迫られるものも少なくありません。
それでもk8sの更なる発展に期待してやみません。
しがみついていきます。
この記事が誰かの役に立ったら幸いです。
もし、もっとイケてる方法があったらコメントいただけると嬉しいです。