Red Hat OpenShift on IBM Cloud on VPCに追加したNLB DNSでTLSがオレオレ証明書になってしまう件への対応

# 経緯

過去記事「[Red Hat OpenShift on IBM Cloud on VPCへのプライベート接続方式を検証してみた（設定編）](https://qiita.com/teruq/items/ab85f9443a9594ce34a0)」の検証中に発見したのですが、IBM提供ドメインを追加した際に、IBM Cloudのガイド通りに行うとTLS証明書がオレオレ（自己署名）証明書になってしまうことがわかりました。その原因と対処をまとめます。

# IBM提供ドメインとは

IBM Cloud Kubernetes ServiceやRed Hat OpenShift on IBM Cloudでは、IngressやルーターでIBM Cloudが提供するドメイン名とTLS証明書を使うことができます。

```
$ ibmcloud oc cluster get -c roks-tok
...
Ingress サブドメイン:                         roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp
...
```

TLS証明書はLet's Encryptのものが使われています。

```
$ curl https://hoge.roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud/
...
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=roks-tok.jp-tok.containers.appdomain.cloud
*  start date: Dec 29 19:27:37 2020 GMT
*  expire date: Mar 29 19:27:37 2021 GMT
*  subjectAltName: host "hoge.roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud" matched cert's "*.roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
```

IBM提供ドメインとTLS証明書は利用者がドメイン名の準備やTLS証明書の購入・更新を行う必要がないので、とても便利です。

クラスターがVPCの場合、IBM Cloud提供ドメインは必要に応じて追加することができます。例えばIngressやルーターをパブリックとプライべート両方で使いたい場合などです。

> 参考）https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_routes#private-routes

```
$ ibmcloud oc nlb-dns create vpc-gen2 -c roks-tok --lb-host ****6e91-jp-tok.lb.appdomain.cloud --type private --secret-namespace openshift-ingress
NLB DNS を作成中...
OK
NLB ホスト名が roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud として作成されました。

$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン                                                                       ロード・バランサーのホスト名         SSL 証明書の状況   SSL 証明書の秘密名                               秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud   ****a523-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-0000   openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud   ****6e91-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-i001   openshift-ingress
```

しかし、追加されたドメイン名では、TLS証明書がオレオレ（自己署名）証明書になってしまいます。

```
$ curl https://hoge.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud/
curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

$ openssl s_client -connect hoge.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud:443 --showcerts
CONNECTED(00000003)
depth=1 CN = ingress-operator@1609275818
verify error:num=19:self signed certificate in certificate chain
verify return:1
depth=1 CN = ingress-operator@1609275818
verify return:1
depth=0 CN = *.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud
verify return:1
---
Certificate chain
 0 s:CN = *.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud
   i:CN = ingress-operator@1609275818
...
```

追加ドメインでは自己署名証明書にならざるを得ないのかと思いきや、そうではありません。IBM Cloudはドメイン追加時にそれに対応したLet's Encryptの証明書を用意してくれているのですが、それを使うことを明示しない限り、代わりに自己署名証明書を使ってしまいます。

# 追加したIBM提供ドメインでTLS証明書を正しく使う方法

実は``ibmcloud oc nlb-dns create``をした時点で正しいLet’s EncryptのTLS証明書は作られており、シークレットに存在しています。

```
$ oc get secrets -n openshift-ingress
...
roks-tok-********81bb6d6d7afea007d1a8cafd-0000    kubernetes.io/tls                     2      2d11h
roks-tok-********81bb6d6d7afea007d1a8cafd-i001    kubernetes.io/tls                     2      115m
...
```

このとき該当のシークレットが見つからない場合、``ibmcloud oc nlb-dns create``時に``--secret-namespace``を指定しなかったか、誤ったネームスペースを指定してしまった可能性があります。実はIBM Cloudのガイドでは``--secret-namespace``を指定するよう書かれていないため、そのまま実行するとシークレットはdefaultネームスペースに作られてしまいます。もしそうなってしまった場合は<a href="#nlb-dnsのsecret-namespaceとingress-controllerのネームスペースが異なってしまった場合の対処">NLB DNSのsecret-namespaceとingress-controllerのネームスペースが異なってしまった場合の対処</a>に従ってください。

さて、シークレットがあっても、追加ドメインを処理するIngress ControllerまたはルーターのPodがそのsecretを参照してくれていません。下例の場合、router-private-*は追加ドメインを処理するルーターのPod、router-certs-privateはIngress Controller Operatorが勝手に作ったオレオレ証明書です。

```
$ oc get pods router-private-8c8bf88d5-47zzv -o yaml
...
  volumes:
  - name: default-certificate
    secret:
      defaultMode: 420
      secretName: router-certs-private
...
```

デフォルトのルーターは正しくLet's Encryptの証明書を使えているので、オペレーター定義を確認します。

```
$ oc get ingresscontroller/default-n openshift-ingress-operator -o yaml
...
spec:
  defaultCertificate:
    name: roks-tok-********81bb6d6d7afea007d1a8cafd-0000
...
```

このdefaultCertificateでsecretを指定しているようです。

それでは、追加したルーターのオペレータ定義にdefaultCertificateを追加し、正しいシークレットを参照するようにします。下例ではprivateが追加ルーターを管理するオペレーターです。

```
$ oc get ingresscontroller -n openshift-ingress-operator
NAME                         AGE
default                      2d19h
private                      2d18h
...

$ oc patch ingresscontroller/private -n openshift-ingress-operator --type merge -p '{"spec":{"defaultCertificate":{"name":"roks-tok-********81bb6d6d7afea007d1a8cafd-i001"}}}'
ingresscontroller.operator.openshift.io/private patched
```

これで、Let’s Enryptの証明書が使われるようになります。

```
$ openssl s_client -connect hoge.roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp
-tok.containers.appdomain.cloud:443 --showcerts
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = roks-tok.jp-tok.containers.appdomain.cloud
verify return:1
---
Certificate chain
 0 s:CN = roks-tok.jp-tok.containers.appdomain.cloud
   i:C = US, O = Let's Encrypt, CN = R3
...
```

# NLB DNSのsecret-namespaceとingress-controllerのネームスペースが異なってしまった場合の対処

``ibmcloud oc nlb-dns create``時に``--secret-namespace``を付けなかったか、誤ったネームスペースを指定してしまい、IngressのPodとシークレットが別のネームスペースになってしまった場合の対処方法です。

この例ではドメインi002の登録時に``secret-namespace``をつけ忘れたため、シークレットがdefaultに作られてしまいました。

```
$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン                                                                       ロード・バランサーのホスト名         SSL 証明書の状況   SSL 証明書の秘密名                               秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud   ****a523-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-0000   openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud   ****6e91-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-i001   openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   ****aad0-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-i002   default
```

一方、このドメインを処理するルーターのPodであるrouter-private-i002はopenshift-ingressネームスペースにあります。そのため、使えるシークレットがありません。

```
$ oc get pods -n openshift-ingress | grep router-private-i002
router-private-i002-67cc7f5f87-4txjx   1/1     Running   0          132m
router-private-i002-67cc7f5f87-5tmdd   1/1     Running   0          132m

$ oc get secret -n openshift-ingress | grep i002
（なし）
```

defaultにあるシークレットをopenshift-ingressネームスペースに作り直すことができます。まず、シークレットの一覧を確認します。

```
$ ibmcloud oc ingress secret ls -c roks-tok | grep i002
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   ibm-cert-store      crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   default             crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
```

ibm-cert-storeにあるものはIBM Cloudが自動で管理しているものなので無視します。

openshift-ingressネームスペースでシークレットを再登録します。先ほどのコマンドで表示されたCRNとネームスペースを正しく指定します。

```
$ ibmcloud oc ingress secret create --cert-crn crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3 -c roks-tok --name roks-tok-********81bb6d6d7afea007d1a8cafd-i002 --namespace openshift-ingress
OK

$ ibmcloud oc ingress secret ls -c roks-tok | grep i002
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   ibm-cert-store      crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   default             crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   openshif-ingress    crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
```

openshift-ingress名前空間にシークレットが作成されました。

```
$ oc get secret -n openshift-ingress | grep i002
roks-tok-********81bb6d6d7afea007d1a8cafd-i002    kubernetes.io/tls                     2      83s
```

誤ってdefaultネームスペースに作ってしまったシークレットを削除します。

```
$ oc get secret -n default | grep i002
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   kubernetes.io/tls                     2      2d15h

$ ibmcloud oc ingress secret rm -c roks-tok --name roks-tok-********81bb6d6d7afea007d1a8cafd-i002 --namespace default
OK

$ ibmcloud oc ingress secret ls -c roks-tok | grep i002
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   ibm-cert-store      crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created
roks-tok-********81bb6d6d7afea007d1a8cafd-i002   openshif-ingress    crn:v1:bluemix:public:cloudcerts:jp-tok:a/********463e2e8dbefbd3fa223c9de3:96b72cf4-e009-4f0e-93e1-4e9cd332e916:certificate:d984a504ef9c27a25f6c8b8cfd978af3   2021-03-29T20:51:10+0000   *.roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   created

$ oc get secret -n default | grep i002
（なし）
```

これで、シークレットとPodが同じネームスペースになりました。

CLIでの表示は残念ながら直らないようです。そこは諦めましょう。

```
$ ibmcloud oc nlb-dns ls -c roks-tok
OK
サブドメイン                                                                       ロード・バランサーのホスト名         SSL 証明書の状況   SSL 証明書の秘密名                               秘密の名前空間
roks-tok-********81bb6d6d7afea007d1a8cafd-0000.jp-tok.containers.appdomain.cloud   ****a523-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-0000   openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i001.jp-tok.containers.appdomain.cloud   ****6e91-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-i001   openshift-ingress
roks-tok-********81bb6d6d7afea007d1a8cafd-i002.jp-tok.containers.appdomain.cloud   ****aad0-jp-tok.lb.appdomain.cloud   created            roks-tok-********81bb6d6d7afea007d1a8cafd-i002   default
```