k8sでcert-managerを使ってサブドメインの証明書を発行しまくる

# モチベーション
k8sならマルチテナントを1クラスタで束ねる事もお手の物なんですが、そんな時に課題になるのがSSL証明書の発行です。
グローバル証明書はそれなりにお高いです。グローバル証明書を持っていない。もしくは取得するまでもない用途の場合、無料で証明書を発行しまくる事ができたら嬉しくないですか？k8sで。

自分の場合は、無限にステージング環境を構築する必要があったので、その為の証明書発行のソリューションが必要でした。

# cert-manager
[cert-manager](https://github.com/jetstack/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 編)](https://qiita.com/apstndb/items/3a39a1e6acacbbc30765)

※ 本記事ではcert-managerの導入については割愛します。
# 導入はあっさり、だけど。。
導入は簡単でした。
ただ、発行する頻度によりきりですが、自分の場合は無限にサブドメインを利用する必要があり、その要件を満たすには一筋縄ではいきませんでした。

# やってみたこと
## その１　各テナント毎にグローバル証明を発行する
１テナントで複数のサブドメインを発行する必要がありました。
なので管理が楽なグローバル証明書でいく事にしました。

```yml
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](https://letsencrypt.org/docs/rate-limits/)というものが存在します。

[制限と仕様からLet's Encrypt(ACMEv1)の話](https://qiita.com/sawanoboly/items/8e3c57aa2e30fc58c4e3) でも丁寧にまとめられています。

同一ドメインの発行回数に制限があります。
週に数十テナントを立てないといけなかったため、これに引っかかりました。

## その２ グローバル証明書じゃなくて個別に発行
そもそもいくつでも発行できるんだったらグローバル証明書じゃなくてもいいじゃん。
という事で個別に発行してみる事にしました。

```yaml
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](https://letsencrypt.org/docs/rate-limits/) にちゃんと書いてありました。よく読め！って話でした。。

# 結局
自分的には敗北でしたが、、もう諦めて取得したグローバル証明書を使い回す事にしました。
以下のようにグローバル証明書をネームスペース `kube-system` で取得しそれを各テナントで使い回すって方針にしました。

```yml
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として保存されます。
これを新しいテナントを立てる時に、ダウンロードして一緒に含めるという方法です。

```shell
kubectl get secret cert-manager-tls -n kube-system -o yaml > cert-manager-tls.yml

```

これで既に取得済みのグローバル証明書が手元にダウンロードされるので、これを含めて

```shell
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の更なる発展に期待してやみません。
しがみついていきます。

この記事が誰かの役に立ったら幸いです。
もし、もっとイケてる方法があったらコメントいただけると嬉しいです。