証明書の取得ができない!!

問題文

概要

k8sクラスターにcert-managerをインストールしhttp01認証で証明書を発行するissuerを設定した。
その上でhostのnginxのpodを展開するDeploymentと、それを公開するService、Ingressを記述した/home/user/manifests/web.yamlを作成しapplyした。
しかし、なぜか証明書が発行されない。その理由と解決方法を報告してほしい。

トポロジを以下の図で示す。

前提条件

  • cert-managerで取得する証明書は自己署名証明書を使用している
    • hostcert-managerなど必要なものにはルートCA証明書をインストール済み
  • hostuserユーザーでkubectlコマンドが利用できる
  • host/home/user/manifestsに今回applyしたmanifestが保存されている
    • cert-manager.yaml ingress-controller.yaml root-ca.yaml変更しないこと
    • web.yamlの再展開(kubectl delete, kubectl applyなど)可
  • acme-serverには、追加でDNSサーバーが稼働しており以下のレコードが設定されている
        IN      NS      ictsc.test.
        IN      A       192.168.20.125

ca IN A 192.168.20.125
www IN A 192.168.20.1
  • lbにはkeepalivedがインストールされている。
    • 変更しないこと

初期状態

  • hostcurl -L www.ictsc.testを実行したとき以下のようなエラーになる
curl: (60) SSL certificate problem: unable to get local issuer certificate
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.
  • hostkubectl get certを実行したときwww-ictsc-test-tlsのREADYがFalseになっている

終了状態

  • hostcurl -L www.ictsc.testを実行したときにnginxの初期ページが表示される
    • 証明書が設定されている
  • hostkubectl get certを実行したときにwww-ictsc-test-tlsのREADYがTrueになっている

接続情報

lbhostのみSSHログインできる。

VM名ホスト名ユーザパスワード
lb192.168.20.1userictsc2020
host192.168.20.20userictsc2020
acme-server192.168.20.125
k8s-master192.168.20.129
k8s-worker1192.168.20.130
k8s-worker2192.168.20.131

解説

原因

cert-managerはhttp01認証を行うときにacme challengeが行えるかセルフチェックを行います。その際、www.ictsc.testつまり192.168.20.1宛てにGETリクエストを行いますが、このリクエストの戻りのパケットがnginxのPODから直接cert-managerのPODに送られてしまいます。
その時の送信元アドレスが、リクエストの送信時の宛先と異なるためセルフチェックのリクエストは失敗してしまい証明書の取得が行われませんでした。

以下のようにセルフチェックが失敗していることを確認できます。

user@host:~$ kubectl describe challenges.acme.cert-manager.io
Name:         www-ictsc-test-tls-dhksj-696946275-1037365539
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  acme.cert-manager.io/v1
Kind:         Challenge
...
省略
...
Status:
  Presented:   true
  Processing:  true
  Reason:      Waiting for HTTP-01 challenge propagation: failed to perform self check GET request 'http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9': Get "http://www.ictsc.test/.well-known/acme-challenge/c4mi9je6PJ87H9ulTtAPOu1xSjXEyDo9": dial tcp 192.168.20.1:80: connect: connection timed out
  State:       pending
...
省略
...

解決策

解決策としては以下のことが考えられます。

  • LBにhaproxyやnginxなどのリバースプロキシを用いる
  • LBにヘアピンNATの設定を行う

今回は、keepalivedを変更できないのでヘアピンNATの設定を行います。

/etc/sysctl.confに以下の設定を追記し更新します。

$ sudo vim /etc/sysctl.conf
net.ipv4.vs.conntrack=1 # 最終行に追記

$ sudo sysctl -p

iptablesで内部からの通信をMASQUERADEする設定を行い永続化します。

$ sudo iptables -t nat -A POSTROUTING -s 192.168.20.128/25 -m ipvs --vaddr 192.168.20.1/32 -j MASQUERADE
$ sudo netfilter-persistent save

/home/user/manifests/web.yamlをapplyし直すとkubectl get certでREADYがTrueになることを確認できます。
またcurl -L www.ictsc.testで正常にnginxのページが表示されます。

user@host:~/manifests$ kubectl delete -f web.yaml
deployment.apps "web" deleted
service "web" deleted
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions "a-web" deleted

user@host:~/manifests$ kubectl apply -f web.yaml
deployment.apps/web created
service/web created
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/a-web created

user@host:~/manifests$ kubectl get cert
NAME READY SECRET AGE
www-ictsc-test-tls True www-ictsc-test-tls 35s

user@host:~/manifests$ curl -L www.ictsc.test
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

採点基準

  • 理由が説明できている 20%
  • 解決方法が記述されておりその解決方法で解決が可能 80%

さいごに

問題を解いていただきありがとうございました。

また上記のような想定解法での解答はなく、以下のような解答を頂きました。DNATの解法は、想定はしていたのですがCoreDNSは完全に想定しなかったので、確かにそういう方法もあるのかと思いました。(self checkとは?という感じではありますが、問題の終了条件を満たせるので満点としました。)

  • CoreDNSのレコードを変更しingress controllerのPODのIPアドレスを名前解決するようにする
  • lbでk8sのいずれかのノードにDNATをする