Helmでインストールした Cert Manager をk8sマニフェストで再セットアップ
Let’s Encrypt 証明書の期限管理(最新化)は Cert Manager に任せっきりだったのですが、あるとき一部サイトの証明書更新がされないまま失効してしまいました。どうやら Helm
を用いてセットアップしていた Cert Manager が古くなりすぎていたようで、アップデートをする必要がありました。
で、今回久しぶりに Helm
を触ったため操作方法に戸惑ってしまいました。利用頻度が低いツールを使ってしまうと後々苦労するので、今回は Helm
を利用せずにKubernetes へ直接デプロイする手順でセットアップすることにします。
その後、Cert Manager を複数バージョン一気にアップデートした記事を書きましたので、あわせて参考にしてください。
セットアップの前提
本記事のマニフェストにで登場する変数について、ここでは以下の通りとします。 なお、ここではKubernetes上で動かしていた「Redmine」を対象に操作しているためそれっぽい文字が頻出しますが、Redmineを前提とした手順ではなく汎用的なものです。アプリ名や、ツール名相当の意味と捉えてください。
値 | 意味 |
---|---|
mynamespace |
アプリケーションをデプロイするためのネームスペース |
mysite.example.com |
サイトの公開FQDN |
redmine-tls |
証明書。Ingressへ適用するためのSecret |
redmine-cert |
redmine-tls を作成するための、cert-managerのリソース |
your-main@example.com |
Let’s Encrypt で SSL 証明書作成要求をするための自身のメールアドレス |
アップデート前の状態
再さっとアップに着手する前に現在の状態をメモ。
$ kubectl -n mynamespace describe Certificate redmine-tls
Spec:
Acme:
Config:
Domains:
mysite.example.com
Http 01:
Ingress:
Ingress Class: nginx
Dns Names:
mysite.example.com
Issuer Ref:
Kind: ClusterIssuer
Name: letsencrypt-prod
Secret Name: redmine-tls
Status:
Acme:
Order:
URL:
Conditions:
Last Transition Time: 2019-12-18T07:33:25Z
Message: Failed to create new order: acme: urn:ietf:params:acme:error:rateLimited: Your ACME client is too old. Please upgrade to a newer version.
Reason: ValidateError
Status: False
Type: Ready
Last Transition Time: 2019-10-19T08:33:22Z
Message: Order validated
Reason: OrderValidated
Status: False
Type: ValidateFailed
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ErrCreateOrder 2m22s (x757 over 12h) cert-manager Error creating order: acme: urn:ietf:params:acme:error:rateLimited: Your ACME client is too old. Please upgrade to a newer version.
動かしていた Cert Manager は v0.5.2
.
Upgrading | cert-manager によると v0.12
くらいまで出ているので、かなり古くなってしまっていました。
Upgrading from v0.5 to v0.6 | cert-manager
一度クリーン削除したほうがいいらしい。
$ helm search cert-manager
NAME CHART VERSION APP VERSION DESCRIPTION
stable/cert-manager v0.5.2 v0.5.2 A Helm chart for cert-manager
$ helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
cert-manager 1 Sat Jan 12 23:34:05 2019 DEPLOYED cert-manager-v0.5.2 v0.5.2 kube-system
$ helm delete --purge cert-manager
release "cert-manager" deleted
$ helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
(関係ないやつなので非表示)
$ kubectl delete crd \\
certificates.certmanager.k8s.io \\
issuers.certmanager.k8s.io \\
clusterissuers.certmanager.k8s.io
customresourcedefinition.apiextensions.k8s.io "certificates.certmanager.k8s.io" deleted
customresourcedefinition.apiextensions.k8s.io "issuers.certmanager.k8s.io" deleted
customresourcedefinition.apiextensions.k8s.io "clusterissuers.certmanager.k8s.io" deleted
続いて Fresh Install すればいい様子。なお、この操作をしても、説明にある通り各サービスに適用している(稼働中の)SSL証明書は消えなかったので、安心して実行可能。
以下公式の説明通りですが、一部コメントしつつ記載。
Installing with regular manifests
$ kubectl create namespace cert-manager
Install the CustomResourceDefinitions and cert-manager itself
$ kubectl apply --validate=false -f <https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml>
... created のような実行ログが数十行でてくる
ここで CRD (Custom Resource Definition) が追加されるので、kubectl get certificate --all-namespaces
とかが使えるようになる。
Permissionエラーが発生することがあるようで、以下の記載の通りにコマンドを打つ必要があるそう(GKE環境の場合)
Note: When running on GKE (Google Kubernetes Engine), you may encounter a ‘permission denied’ error when creating some of these resources. This is a nuance of the way GKE handles RBAC and IAM permissions, and as such you should ‘elevate’ your own privileges to that of a ‘cluster-admin’ before running the above command. If you have already run the above command, you should run them again after elevating your permissions:
$ kubectl create clusterrolebinding cluster-admin-binding \\
--clusterrole=cluster-admin \\
--user=$(gcloud config get-value core/account)
(今回、自分のケースにおいては不要でした。もともとGKEに対していろんな操作をしてきた経緯もあり。)
Verifying the installation
cert-manager
関連の Pod が起動しているかを確認します。
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-cainjector-58f48c4cb9-pkdzq 1/1 Running 0 11m
cert-manager-cb5f48858-gsbx4 1/1 Running 0 11m
cert-manager-webhook-74d98fdc7b-flx6v 1/1 Running 0 11m
Configuration
Issuer
と ClusterIssuer
の違い
An Issuer is a namespaced resource, and it is not possible to issue certificates from an Issuer in a different namespace. This means you will need to create an Issuer in each namespace you wish to obtain Certificates in.
If you want to create a single Issuer that can be consumed in multiple namespaces, you should consider creating a ClusterIssuer resource. This is almost identical to the Issuer resource, however is non-namespaced so it can be used to issue Certificates across all namespaces.
Issuer
は Namespace ごとに作成する必要がある(裏返すと、Namespace ごとに異なるものを利用することができる)。一方で ClusterIssuer
は Namespace をまたがって利用される。
その対象の Kubernetes クラスタがどのような編成(企業・組織・チーム・個人等)のポリシーで運営しているかだと思いますので、自身の用途に合うものを選べばいいと思います。今回は個人用クラスタなので ClusterIssuer
で記載していきます。
$ kubectl apply -f ingress/cluster-issuer.yml
clusterissuer.cert-manager.io/letsencrypt-staging created
clusterissuer.cert-manager.io/letsencrypt-prod created
ingress/cluster-issuer.yml
の中身:
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-staging # テスト用
spec:
acme:
email: "your-main@example.com"
server: <https://acme-staging-v02.api.letsencrypt.org/directory>
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod # 本番用
spec:
acme:
email: "your-main@example.com"
server: <https://acme-v02.api.letsencrypt.org/directory>
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
詳細はここを参考に ACME | cert-manager
Certificate
次は、Issuer に基づいて過各種サービス用の証明書を適用(作成)するもの。
$ kubectl -n mynamespace get certificate
No resources found.
まだ何もしていないので存在しない。
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: redmine-tls
namespace: mynamespace
spec:
commonName: mysite.example.com
secretName: redmine-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
spec.issuerRef
にある kind
については、消すと正しく動かず、20分くらい様子を見ていたものの Requested ステータスから繊維しなかった。(はず。いろいろ試行錯誤した部分があり記録が曖昧)。
ドキュメントにはデフォルト Issuer
となっているので、意図的に変更する必要があるみたい。
Certificate Resources | cert-manager
manifestをデプロイ。
$ kubectl -n mynamespace apply -f redmine/certificate.yml
certificate.cert-manager.io/redmine-tls created
$ kubectl -n mynamespace get certificate
NAME READY SECRET AGE
redmine-tls False redmine-tls 5s
redmine/certificate.yml
の中身(ドキュメントの通りだけど一応):
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: redmine-cert
namespace: mynamespace
spec:
commonName: mysite.example.com
secretName: redmine-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- mysite.example.com
dnsNames をしっかり書かないといつまでも証明書作成が完了しないので注意
Helm利用時(移行前)のコンフィグには記載していたのが、再セットアップ用にコンフィグを見直した再、commonName
だけあればいいんじゃないかと思いこんで削除してしまったのですが、それが理由で作成要求が完了せずに時間を要してしまいました。ドキュメントには以下の通り記載されています。
The dnsNames field specifies a list of Subject Alternative Names to be associated with the certificate. If the commonName field is omitted, the first element in the list will be the common name.
Certificates — cert-manager documentation
ほか、
Certificate Resources | cert-manager
# At least one of a DNS Name, USI SAN, or IP address is required.
dnsNames:
- example.com
- www.example.com
結果、ログがこうなればOK
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal GeneratedKey 15m cert-manager Generated a new private key
Normal Requested 15m cert-manager Created new CertificateRequest resource "redmine-cert-114396219"
Normal Requested 7m46s cert-manager Created new CertificateRequest resource "redmine-cert-387630196"
Normal Requested 3m19s cert-manager Created new CertificateRequest resource "redmine-cert-2198555332"
Normal Issued 2m54s (x2 over 7m20s) cert-manager Certificate issued successfully
まとめ
cert-manager 関連の Pod のセットアップは1,2行で完了してしまい、それ以降のコンフィグレーションは Helm 利用時とも変わらないため、これについてはむしろ Helm を使わないほうが楽でさえありました。
今度はどこかの機会でアップデートも試さないといけないですね。
(参考)トラブルシュート
最初いつまでも証明書作成が完了せず(要求のままでストップする)、様々な切り分けをしたので順序が前後してしまっている可能性あり。 上記の手順でうまく完了しない場合は以下の点もチェックしてみましょう。
certificate イベント状況を見る(describe
)
$ kubectl -n mynamespace describe -f redmine/certificate.yml
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Requested 14s cert-manager Created new CertificateRequest resource "redmine-tls-61386848"
既存の証明書を消してみる
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal GeneratedKey 15s cert-manager Generated a new private key
Normal Requested 15s cert-manager Created new CertificateRequest resource "redmine-tls-3517813469"
Helm でセットアップしたときにできている kube-system
内の secret を消してみる
$ kubectl get secret --all-namespaces | grep letsencrypt
cert-manager letsencrypt-prod Opaque 1 140m
cert-manager letsencrypt-staging Opaque 1 140m
kube-system letsencrypt-prod Opaque 1 369d
kube-system letsencrypt-staging Opaque 1 369d
$ kubectl -n kube-system delete secret letsencrypt-prod
$ kubectl -n kube-system delete secret letsencrypt-staging
証明書作成要求のステータスを見る
$ kubectl -n mynamespace describe CertificateRequest redmine-cert-114396219
(注)certificateの名前の語尾にくっつく数字は作成要求の都度違う様子。対象のEventログから確認可能(上述の通り)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning InvalidOrder 6m44s cert-manager The CSR PEM requests a commonName that is not present in the list of dnsNames. If a commonName is set, ACME requires that the value is also present in the list of dnsNames: "mysite.example.com" does not exist in []