おうちKubernetesにCertManagerを入れてTLS接続できるようにする

宅内LANで動かしているKubernetesクラスタ上のサービスについて,MetalLBingress-nginxを使ってアクセスできるようにしていたんだけど,TLSでアクセスしたくなった.別にLAN内なのでそこまで気にする必要はないんだけど,アプリケーションとしてhttpsを前提にしているものもあるわけで,そういうものを無理やりhttpでアクセスするのは不便だ.

MetalLB + ingress-nginx

そもそもベースになるのはこの構成.まずはMetalLBを入れて,Service type LoadBalancerに任意のIPを振れるようにしておく.自宅のLANのIPアドレスレンジが /24 くらいあるのであれば,例えば 192.168.0.0 - 192.168.0.255 くらいのIPが使えるはずで,適当に使わないやつをServiceに割り振っておけば,LAN内からアクセスできる状態になる.

次に,ingress-nginxを入れるのだが,ingress-nginxが作るServiceをtype LoadBalancerにしておく.そうすることで,このServiceにIPが割り当てられる.するとこのIPにHost付きでアクセスすれば,あとはingress-nginxはHostヘッダーに従って必要なingress -> service -> podにリクエストを流してくれる.

ただし,この方式でやるなら,Hostヘッダー指定が必須になるので /etc/hosts に書き込んでおく必要がある.

Route53を/etc/hostsの代わりに使う

/etc/hosts をいじるのが面倒なので,こいつをRoute53にまかせてみるという案を思いついた.Route53はただのDNSなので,別にローカルのIPを返答していても問題ない.家の外からアクセスできないだけだ.

というわけでexternal-dnsを入れる.これが入った状態でIngressを作れば,そのIP(つまりはingress-nginxのService type LoadBalancerのIPだが)がDNSに登録される.これでLAN内であれば /etc/hosts の変更をしなくても各種サービスにドメインでアクセスできるようになる.

CertManagerで証明書を得る

ここまでやって気づいた.Route53をDNSとして使えるなら,Let's EncryptのDNS-01 challengeが成功するんじゃないか?HTTP-01 challengeは外部サーバからサービスにアクセスできる必要があるけど,DNSは所有権のみを確認するだけなので,サービス自体が外部公開されていなくても成功する.

letsencrypt.org

というわけでCertManagerをインストールして,

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-issuer
spec:
  acme:
    privateKeySecretRef:
      name: letsencrypt-privatekey
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - selector:
        dnsZones:
          - "home-cluster.mydomain"
      dns01:
        route53:
          region: ap-northeast-1
          accessKeyIDSecretRef:
            name: aws-secret
            key: AWS_ACCESS_KEY_ID
          secretAccessKeySecretRef:
            name: aws-secret
            key: AWS_SECRET_ACCESS_KEY

ClusterIssurerを作って,

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-issuer
  name: my-service
spec:
  ingressClassName: nginx
  rules:
  - host: my-service.home-cluster.mydomain
    http:
      paths:
      - backend:
          service:
            name: my-service
            port:
              number: 80
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - my-service.home-cluster.mydomain
    secretName: my-service-tls-certificate

Ingressにcert-managerの設定をちょっと追加すれば,無事証明書が取得できた.これで宅内LANであってもTLSでアクセスできるようになる.