【K3s】cert-manager を使って Traefik Ingress のLet's Encrypt 証明書発行を自動化する

こんにちは。今回はタイトルにもあるように、K3sで動作する Traefik Ingress の証明書発行を cert-manager で自動化します。 証明書の発行にはお馴染みの Let’s Encrypt くんにやってもらいます。また、今回は Cloudflare の DNS-01 Challenge を使用します。

環境

Name Version
K3s v1.30.6+k3s1 (1829eaae)
cert-manager v1.16.1
OS Ubuntu 24.04.1 LTS

K3s のインストール

K3s のインストールについては、公式ドキュメントもしくはその他の記事が丁寧に説明されているので、ここでの説明は省略し、コマンドだけ紹介します。

1
2
$ sudo apt install wireguard
$ curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server" sh -s - --flannel-backend wireguard-native

なぜ cert-manager を使うのか

Docker では以下のような Traefik Conifg を反映させることで、自動証明書発行ができます。

1
2
3
4
5
6
7
8
certificatesResolvers:
  cloudflare:
    acme:
      dnsChallenge:
        provider: cloudflare
      email: [email protected]
      storage: /letsencrypt/acme.json
      keyType: EC384

そのため、「cert-manager なんか使わなくとも Traefik 単体で証明書発行できるんじゃないの?」と思いましたが、どうやら負荷分散や高可用性のためにKubernetes向けにはサポートされていないようでした。(Enterprise の方はされているらしい)

つまり、上記の Traefik Config をそのままコピペして移行することはできないようです。

余談ですが、K3s に付属する Traefik の設定は HelmChartConfig リソースを定義した以下のようなマニフェストを Apply するだけで変更されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
      certificatesResolvers:
      cloudflare:
        acme:
          dnsChallenge:
            provider: cloudflare
          email: [email protected]
          storage: /letsencrypt/acme.json
          keyType: EC384      

上記のことから、Traefik 単体での証明書発行は行わず、今回は cert-manager を使って証明書発行します。

cert-manager のインストール

cert-manager のインストールは kubectl apply コマンドでやる方法と、helm からやる方法などがありますが、今回は kubectl apply でやりました。

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

詳細は以下のドキュメントから確認してください。

Issue / Cluster Issuer を作成する

Issuers, and ClusterIssuers, are Kubernetes resources that represent certificate authorities (CAs) that are able to generate signed certificates by honoring certificate signing requests.

Issuer と Cluster Issuer は Ingress から証明書発行のリクエストを送信したときに、その処理を担ってくれる Kubernetes リソースです。

今回は DNS-01 チャレンジを Cloudflare に対して実行する ACME の Cluster Issuer を作成します。

Cloudflare プロバイダーを使用するためには、 API Token をSecret リソースとして登録しておきましょう。

ここで使用する API Token の Scope は以下のような状態である必要があります。

image

ゾーンリソースについては、特定のゾーンでも問題ありません。

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: cert-manager
type: Opaque
stringData:
  api-token: <API_TOKEN>

以下は Let’s Encryptサーバーを対象とした Cluster Issuer リソースです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: [email protected]
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-issuer-account-key
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            name: cloudflare-api-token-secret
            key: api-token

おまけにステージング用の Cluster Issuer リソースを作成しておくと良いでしょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: [email protected]
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: example-issuer-account-key
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            name: cloudflare-api-token-secret
            key: api-token

Ingress から証明書発行を要求する

Ingress リソースの annotations から cert-manager.io/cluster-issuer: letsencrypt と書くことで指定された Issuer が証明書発行要求を行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sample-ingress
  labels:
    name: sample-ingress
  annotations:
    ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
      - sample.example.com
    secretName: sample-tls-secret
  rules:
  - host: sample.example.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: sample
            port:
              number: 80

証明書発行状態の確認

証明書が発行されているかどうかは、以下のコマンドで確認することができます。

READY が True になっていると使用可能な状態になっています。Trueになるまでには数分から数十分かかることがあるので少し待ちましょう。

1
2
3
$ kubectl get cert -A
NAMESPACE   NAME                 READY   SECRET               AGE
default     sample-tls-secret   True    sample-tls-secret   13h

いつまで経っても True にならない場合は、cert-manager の pod のログを見て確認しましょう。

1
2
$ kubectl get pod -n cert-manager
$ kubectl logs -f -n cert-manager pod/cert-manager-6796d554c5-fsjmn

おわりに

かなりお手軽に証明書発行できてよかったです。