[cert-manager] kubernetesをhttps化させる
目次
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のままになってしまうことではないでしょうか? 考えられるミスとして
- ClusterIssuerで作成しているのにCertificateのissuerRefがIssuerを指定してしまっている
- ClusterIssuerで参照するSecretのnamespaceの格納場所が違う
- 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化させるとなんとなく概要がわかってくるので根気強く勉強してみてください。