ストイックに生きたい
Create External Issuer for cert-manager
cert-managerExternal Issuer を実装する方法
このエントリーをはてなブックマークに追加

さて

Implementing External Issuersを見ると証明書を発行するためにACME等を使わずに自分で実装できるらしいが何をすればいいかよくわからなかったので実装して確認した。
cloudflare/origin-ca-issuerを参考にしたのでこっちを読めば基本的には何をすればいいかはわかる。

どういう時にこれを実装するかというと例えば社内に証明書を管理しているAPIサーバーがあったとしてそこから証明書を取得するとか。

実装

一部エスパーしている部分もあるが大まかにはこのような流れで動いている。

flow.png

参考実装はryota-sakamoto/cert-manager-external-samplekubebuilderを使っている

実装するべき点は3つある

1: external issuerのreconcile

customissuer_controller.go#L58

yamlではユーザー名とかパスワードを指定してreconcileすればいいと思う。
(Secret を使うとかそこはよしなにやる)

apiVersion: cert-manager.k8s.sakamo.dev/v1
kind: CustomIssuer
metadata:
  name: customissuer-sample
spec:
  user: user
  password: password

3, 5: CertificateRequestのreconcile

CertificateRequest をreconcileし下記の点を実装すると目的が達成できる

  1. issuerRef が一致するかどうか #L53
  2. CertificateRequestStatusCertificate が空かどうか #L61
  3. issuerRef で指定したIssuerのStatus等の確認 #78
  4. 証明書の取得、発行等 #85
  5. CertificateRequestStatus の更新 #91

まず CertificateissuerRef を指定してapplyする。
そうすると CertificateRequestSecret が生成される。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-certificate
spec:
  secretName: example-com-tls
  dnsNames:
  - example.com
  - www.example.com
  duration: 2160h
  renewBefore: 720h
  issuerRef:
    group: cert-manager.k8s.sakamo.dev
    kind: CustomIssuer
    name: customissuer-sample
$ kubectl get certificate
NAME                  READY   SECRET            AGE
example-certificate   False   example-com-tls   9s

$ kubectl get certificaterequest
NAME                        READY   AGE
example-certificate-crbmv           11s

$ kubectl get secret
NAME                        TYPE                                  DATA   AGE
default-token-lhhdl         kubernetes.io/service-account-token   3      14d
example-certificate-g6gn6   Opaque                                1      17s

CertificateRequest には Certificate で指定した値やCSRが含まれる。
また、Secret の名前はannotationsから取得できる。

$ kubectl get certificaterequest/example-certificate-crbmv -o go-template="{{ .spec.request }}" | base64 --decode
-----BEGIN CERTIFICATE REQUEST-----
(snip)
-----END CERTIFICATE REQUEST-----
$ kubectl get certificaterequest/example-certificate-crbmv -o jsonpath="{.metadata.annotations.cert-manager\.io\/private-key-secret-name}"
example-certificate-g6gn6

Secret には秘密鍵の情報が含まれている。

$ kubectl describe secret/example-certificate-g6gn6
Name:         example-certificate-g6gn6
Namespace:    default
Labels:       cert-manager.io/next-private-key=true
Annotations:  <none>

Type:  Opaque

Data
====
tls.key:  1708 bytes

Status の更新は cert-manager にutilがあるのでそれを使うだけでいい。certificate_request_controller.go#L91

import (
    cmutil "github.com/jetstack/cert-manager/pkg/api/util"
)

cmutil.SetCertificateRequestCondition(cr, certmanager.CertificateRequestConditionReady, metav1.ConditionTrue, certmanager.CertificateRequestReasonIssued, "Certificate issued")

4: 証明書の取得、発行等

ここは要件によって実装は変わるが上記で取得できる値を使ってAPIを叩いたりする。
参考実装では tls.key を使ってオレオレ証明書を作成している。certificate_request_controller.go#L117

実装後

ここまで実装して CertificateRequest から証明書を発行したりすると READYTRUE になり、TLS証明書の Secret が作成される。
これを Ingress 等で指定すると使うことができる。

$ kubectl get certificaterequest
NAME                        READY   AGE
example-certificate-crbmv   True    27m

$ kubectl get certificate
NAME                  READY   SECRET            AGE
example-certificate   True    example-com-tls   27m

$ kubectl get secret/example-com-tls
NAME              TYPE                DATA   AGE
example-com-tls   kubernetes.io/tls   2      46s

下記のようにアクセスすると証明書が発行できていることが確認できる。

certificate.png

© Ryota Sakamoto, 2023