cert-managerで生成した証明書をIstioのGatewayに設定してHTTPS対応する

istiokubernetes

cert-managerはTLSの証明書を自動で生成し管理するK8sのアドオン。 Istioにも含まれていて、これを使ってLet’s Encryptで証明書を生成しGatewayに設定することでHTTPS対応することができる。

デフォルトではcert-managerは入らないのでenabled=trueにしてインストールする。 最初に入るLet’s EncryptのClusterIssuerはエラーになったので消す。

IstioをHelmでインストールしてRoutingとTelemetryを行いJaeger/Kialiで確認する - sambaiz-net

$ helm install install/kubernetes/helm/istio --name istio --namespace istio-system --set certmanager.enabled=true
$ kubectl delete ClusterIssuer --all

確認用にBookInfoを動かす。

$ kubectl label namespace default istio-injection=enabled
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

Let’s Encryptで使われているACMEプロトコルのドメイン認証(Challenge)には /.well-known/acme-challenge/{token}でHTTPレスポンスを返すHTTP Challenge(http-01)と DNSのTXTレコードに書き込むDNS Challenge(dns-01)がある。 HTTP Challengeは手軽に達成できる一方、CAからアクセスできるようにする必要がある。今回はDNS Challengeでやる。 cert-managerはCloud DNSやRoute53などに対応していて、今回はCloudflareを使う。

DNSに書き込めるようにするためCloudflareのMy ProfileからGlobal API Keyを持ってきてBase64デコードしSecretに入れる。 改行コードが含まれないように-nを付ける。

$ echo -n ***** | base64
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-key
  namespace: istio-system
type: Opaque
data:
  api-key: *****

Let’s EncryptのClusterIssuerと証明書を生成するドメインのCertificateを作成する。 serverのURLはStatusのページから確認できる。 本番のURLはレート制限があるので、まずはFakeの証明書が生成されるstgで試すとよい。

証明書を書くsecretNameはistio-systemネームスペースのistio-ingressgateway-certsにする必要がある

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-dns01
  namespace: istio-system
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-dns01
    dns01:
      providers:
      - name: cloudflare
        cloudflare:
          email: [email protected]
          apiKeySecretRef:
            name: cloudflare-api-key
            key: api-key
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: xxx-example-com
  namespace: istio-system
spec:
  secretName: istio-ingressgateway-certs
  issuerRef:
    name: letsencrypt-dns01
    kind: ClusterIssuer
  commonName: xxx.example.com
  dnsNames:
  - xxx.example.com
  acme:
    config:
    - dns01:
        provider: cloudflare
      domains:
      - xxx.example.com

これをapplyするとcertmanagerのログが流れ始める。何か間違いがあるとエラーになるのでなんとかする。

$ kubectl -n istio-system logs -f $(kubectl -n istio-system get pods -l app=certmanager -o jsonpath='{.items[0].metadata.name}')

証明書と秘密鍵のSecretはこんな感じ。

$ kubectl describe secret istio-ingressgateway-certs -n istio-system
Name:         istio-ingressgateway-certs
Namespace:    istio-system
Labels:       <none>
Annotations:  certmanager.k8s.io/alt-names=xxx.example.com
              certmanager.k8s.io/common-name=xxx.example.com
              certmanager.k8s.io/issuer-kind=ClusterIssuer
              certmanager.k8s.io/issuer-name=letsencrypt-dns01

Type:  kubernetes.io/tls

Data
====
tls.key:  1675 bytes
tls.crt:  3805 bytes

ingressgateway-certsは自動で/etc/istio/ingressgateway-certsにマウントされる。 このGatewayをapplyするとHTTPでアクセスした場合はリダイレクトし、HTTPSで正常にアクセスできるようになる。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - xxx.example.com
    tls:
      httpsRedirect: true
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    - xxx.example.com
    tls:
      mode: SIMPLE
      serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
      privateKey: /etc/istio/ingressgateway-certs/tls.key