目次

Introduction

前回の記事([kubernetes] GKEにElasticsearch環境を構築する)で、GKE上にElasticsearchを構築しました。しかしElasticsearchをただ構築しただけなのでElasticsearchにアクセスするためにはipを直指定しなければいけない状態になっています。
流石にipを指定してやり取りするのは不便すぎるのでドメインを登録して疎通するようにしたいと思います。また今回のメインであるhttps化することによって安全にアクセスできるようにしたいと思います。 cert-managerに出てくるリソースの役割などの詳しい説明は、cert-manager基礎知識cert-manager のカスタムリソースの関連性について調べてみたをご覧ください。とてもわかりやすく解説されています。もちろん公式をしっかり読むでもいいでしょう。

今回作成するもの

  • route53でのドメイン登録
  • cert-managerでの証明書自動更新システム構築

完成系

環境

前回の環境が整っている

cert-manager: v1.11.0

前提

  • AWSアカウントを登録している
  • Elasticsearchに登録するドメインを取得している
  • 前回の環境が整っている

handsOn

ドメイン登録をする

今回は、AWSのRoute53でドメイン登録をした場合のcert-manager構築になります。
では、まずRoute53にドメインを登録します。ここは特に説明することがないので省きます。また今回のドメインはes.ki-ta.blogとサブドメインで登録します。
前回作成したLoadBalancerの外部IPは、34.146.210.75でしたね。
Route53に登録したらドメインのIPがあっているか確認してください。

$ dig es.ki-ta.blog +short
34.146.210.75

これでドメイン登録は完了しました。 http://es.ki-ta.blog:9200で叩くとElasticsearchにアクセスできるはずです。

cert-managerのInstall

次に登録したドメインに安全にアクセスできるようにhttps化していきたいと思います。
cert-managerをInstallします。
versionは1.11.0を指定します。

$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml

$ kubectl get pods -n cert-manager
cert-manager-6dd9658548-h6g2k              1/1     Running   0          51s
cert-manager-cainjector-5987875fc7-clx8d   1/1     Running   0          51s
cert-manager-webhook-7b4c5f579b-wsgsp      1/1     Running   0          51s

cert-managerのpodが立っているのが確認できればOKです。

検証に使用するSecret作成

まず、GKE上のcert-managerとAWSのRoute53が疎通できるように認証情報を作成していきます。
AWSのIAMアクセスキーの発行の仕方は、今回詳しく説明しません。多くの記事や公式で説明されているので調べながら発行してください。
cert-managerの公式から持ってきた以下の認証情報でIAMユーザーを作成し、アクセスキーを発行します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "route53:GetChange",
      "Resource": "arn:aws:route53:::change/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets",
        "route53:ListResourceRecordSets"
      ],
      "Resource": "arn:aws:route53:::hostedzone/*"
    },
    {
      "Effect": "Allow",
      "Action": "route53:ListHostedZonesByName",
      "Resource": "*"
    }
  ]
}

発行したらダウンロードしアクセスキーとシークレットキーでkubernetesのSecretを作成します

k8s
 L cert-manager
  L secret.yaml(new)
apiVersion: v1
kind: Secret
metadata:
  name: route53-credentials-secret
  namespace: cert-manager
stringData:
  access-key: AKIAXXXXXXXXXXXXXXXX
  secret-key: xxxxxxxxxxxxxxxxxxxxxxxxx
$ kubectl apply -f k8s/cert-manager/secret.yaml

$ kubectl get secret -n cert-manager
NAME                       TYPE     DATA   AGE
route53-credentials-secret Opaque   2      4s

注意事項としては、後ほどClusterIssuerでChallengeをする際、作成したSecretを参照するのですが、namespaceをcert-managerと同じにしないとerror getting route53 secret access key: secret "route53-credentials-secret" not foundというエラーでSecretが見つからないと言われます。

Issuer

次に実際のcert-managerでのリソースをcert-manager - Route53を参考にしながら実際のリソースを作成していきます。
まずcert-managerでIssuerからです。
route53でドメイン検証を行う設定を記載します。route53のプロパティの詳細はACMEIssuerDNS01ProviderRoute53をご覧ください。

k8s
 L cert-manager
  L secret.yaml
    issuer.yaml(new)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: es-sample-letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: sample@gmail.com
    privateKeySecretRef:
      name: es-sample-letsencrypt-secret
    solvers:
    - selector:
        dnsZones:
        - '*.ki-ta.blog'
      dns01:
        route53:
          hostedZoneID: Z012345689ABCDEFG
          accessKeyIDSecretRef:
            name: route53-credentials-secret
            key: access-key
          secretAccessKeySecretRef:
            name: route53-credentials-secret
            key: secret-key
          region: ap-northeast-1
$ kubectl apply -f k8s/cert-manager/issuer.yaml

$ kubectl get clusterissuer
NAME             READY   AGE
es-sample-letsencrypt   True    23s

今回は、namespaceを考えたくないのでClusterIssuerで作成します。また、route53のregionはどれでもいいと思いますが今回はap-northeast-1にしておきます。 他にも注意していただきたいところは、accessKeyIDSecretRefを使用しているところです。2023/03現在の公式Getting Startedでは1.8.2のcert-managerを利用していますが、accessKeyIDSecretRefは1.8系ではバグがあり1.9系でまともに使用できるようになったのでcert-managerのversionには注意してください。この記事ではver.1.11.0を使用しています。

Certificate

続いてCertificateを作成します。

k8s
 L cert-manager
  L secret.yaml
    issuer.yaml
    cert.yaml(new)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: es-sample-cert
spec:
  secretName: es-sample-cert-secret
  issuerRef:
    name: es-sample-letsencrypt
    kind: ClusterIssuer
  dnsNames:
  - '*.ki-ta.blog'
$ kubectl apply -f k8s/cert-manager/cert.yaml

$ kubectl get cert
NAME             READY   SECRET                  AGE
es-sample-cert   False   es-sample-cert-secret   2s

作成してすぐは、READYがFalseですが少し待つとTrueに切り替わります

$ kubectl get cert
NAME             READY   SECRET                  AGE
es-sample-cert   True    es-sample-cert-secret   1m

LoadBalancerをtls化させる

Let’sEncryptの証明書ができたので、それをLoadBalancerに紐づけます

k8s
 L cert-manager
  L secret.yaml
    issuer.yaml
    cert.yaml
 L es.yaml(modify)
   es-realm-secret.yaml
   monstache.yaml
   kibana.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: es-sample
spec:
  version: 8.6.2
  auth:
    fileRealm:
    - secretName: es-realm-secret
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
  http:
    service:
      spec:
        type: LoadBalancer
    tls:
      certificate:
        secretName: es-sample-cert-secret
$ kubectl apply -f k8s/es.yaml

これでElasticsearchが再起動するのを待ちます。なかなか再起動しない場合はpodを一回削除してください

$ kubectl delete pods es-sample-es-default-0

これで、今回作成するリソースは全て完了です。httpsで接続できるか確認してみましょう

$ curl -u "elastic:5Esma90y3459ppeh7ubsnU78" "https://es.ki-ta.blog:9200"
{
  "name" : "es-sample-es-default-0",
  "cluster_name" : "es-sample",
  "cluster_uuid" : "YNw1s2ywRoKukHh9mWdiTA",
  "version" : {
    "number" : "8.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "2d58d0f136141f03239816a4e360a8d17b6d8f29",
    "build_date" : "2023-02-13T09:35:20.314882762Z",
    "build_snapshot" : false,
    "lucene_version" : "9.4.2",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

httpsで接続できましたね!逆にhttpでは接続できなくなっています

$ curl -u "elastic:5Esma90y3459ppeh7ubsnU78" "http://es.ki-ta.blog:9200"
curl: (52) Empty reply from server

httpで接続できなくなったので、monstacheもhttpsでの接続に変更しておきましょう

k8s
 L cert-manager
  L secret.yaml
    issuer.yaml
    cert.yaml
 L es.yaml
   es-realm-secret.yaml
   monstache.yaml(modify)
   kibana.yaml
# ConfigMapのみ変更
apiVersion: v1
kind: ConfigMap
metadata:
  name: monstache-config
data:
  elasticsearch-url: https://es.ki-ta.blog:9200
$ kubectl apply -f k8s/monstache.yaml

はまった時のデバッグ方法

今回のcert-managerの構築で一番はまってしまうポイントがCertificateを作成してREADYがTrueにならずFalseのままになってしまうことではないでしょうか? 考えられるミスとして

  1. ClusterIssuerで作成しているのにCertificateのissuerRefがIssuerを指定してしまっている
  2. ClusterIssuerで参照するSecretのnamespaceの格納場所が違う
  3. Route53のアクセスキーのIAMの権限が不足している

などが挙げられますが、作成したClusterIssuerやCertificateを見てもあまり解決にはつながりません。
そのためデバッグ方法は主に以下の2種類があげられます

  • cert-managerのpodのログを確認する
  • CertificateRequest, Order, Challengeを確認する

例として、ClusterIssuerが参照するSecretのnamespaceが違う場合を想定します。
CertificateのReadyがFalseのままです

$ kubectl get cert
NAME             READY   SECRET                  AGE
es-sample-cert   False   es-sample-cert-secret   9s

cert-managerのpodのログを確認する

こちらは単純にcert-manager自体にエラーが記録されているのでログを確認します

$ kubectl get pods -n cert-manager
cert-manager-6dd9658548-h6g2k              1/1     Running   0          1h
cert-manager-cainjector-5987875fc7-clx8d   1/1     Running   0          1h
cert-manager-webhook-7b4c5f579b-wsgsp      1/1     Running   0          1h

$ kubectl logs cert-manager-6dd9658548-h6g2k -n cert-manager
E0325 12:10:38.277375       1 controller.go:167] cert-manager/challenges "msg"="re-queuing item due to error processing" "error"="error getting route53 secret access key id: secret \"route53-credentials-secret\" not found" "key"="default/es-sample-cert-8p8pn-3897637060-3630231849"

Secretのroute53-credentials-secretが見つからないというエラーが出ていることがわかりますね

CertificateRequest, Order, Challengeを確認する

ドメイン検証のためのチャレンジを行うため、Certificateを作成するとCertificateRequest → Order → Challengeの順に作成されていきます。リソースの詳細は最初に記載した記事をご覧ください。 そのためいずれかのリソースでエラーが出ている可能性があるため、確認しエラーを起こしているリソースの詳細を見てみましょう。

$ kubectl get certificaterequest,order,challenge
NAME                                                      APPROVED   DENIED   READY   ISSUER                  REQUESTOR                                         AGE
certificaterequest.cert-manager.io/es-sample-cert-8p8pn   True                False   es-sample-letsencrypt   system:serviceaccount:cert-manager:cert-manager   5m37s

NAME                                                         STATE     AGE
order.acme.cert-manager.io/es-sample-cert-8p8pn-3897637060   pending   5m37s

NAME                                                                        STATE     DOMAIN                   AGE
challenge.acme.cert-manager.io/es-sample-cert-8p8pn-3897637060-3630231849   pending   development-mood.space   5m35s

Orderとchallengeがpendingになってますね Orderを見てみましょう

$ describe order es-sample-cert-8p8pn-3897637060
Events:
  Type    Reason   Age   From                 Message
  ----    ------   ----  ----                 -------
  Normal  Created  7m1s  cert-manager-orders  Created Challenge resource "es-sample-cert-8p8pn-3897637060-3630231849" for domain "ki-ta.blog"

Orderではエラーが出ていないですね。次にChallengeを見てみましょう

$ kubectl describe challenge es-sample-cert-8p8pn-3897637060-3630231849
Events:
  Type     Reason        Age                    From                     Message
  ----     ------        ----                   ----                     -------
  Normal   Started       7m33s                  cert-manager-challenges  Challenge scheduled for processing
  Warning  PresentError  2m27s (x7 over 7m32s)  cert-manager-challenges  Error presenting challenge: error getting route53 secret access key id: secret "route53-credentials-secret" not found

Warningでcert-managerのログと同じ"route53-credentials-secret" not foundが出ていることが確認できました。

終わりに

cert-managerでkubernetesのリソースをhttps化する方法を解説しました。cert-managerは出てくるリソースが多く最初は理解しづらいかもしれませんが、一回全てを通してhttps化させるとなんとなく概要がわかってくるので根気強く勉強してみてください。

参考