cert-manager + Let's Encrypt を使って無料でTSL/SSL証明書を自動管理する
はじめに
WEBサイトを公開しようとすると、セキュリティの観点からHTTPSでの公開は必須となります。HTTPSを行うためにはSSL/TLS証明書が必要となりますが、お名前.comなどのサービスを利用すると証明書の発行にお金がかかってしまいます。
本ブログは無料のSSL/TLS証明書発行サービスであるLet’s Encryptと、KubernetesのツールであるCert-managerを組み合わせてHTTPS公開を行っています。
この2つを組み合わせることで、Kubernetesクラスタ内でHTTPS通信に必要な証明書を完全自動化する手法として紹介します。
Cert-managerとは?
cert-managerは、Kubernetes上でSSL/TLS証明書を自動的に取得・更新・管理するためのツールです。これによって証明書運用が簡単になります。
公式ドキュメント https://cert-manager.io/docs/
Let’s Encryptとは?
米国の非営利団体であるISRGにより運営され、90日間有効なDV証明書を無料で自動発行してくれる認証局です。WebサイトのHTTPS化を行う事ができます。
仕組み
1. Certificate リソース作成
↓
2. cert-manager が検知
↓
3. CertificateRequest 作成
↓
4. Let's Encrypt に証明書発行リクエスト (ACME プロトコル)
↓
5. ドメイン所有確認 (Challenge)
├─ HTTP-01: Webサーバー経由で確認
└─ DNS-01: DNS レコード経由で確認
↓
6. Let's Encrypt が証明書発行
↓
7. cert-manager が Secret に保存
↓
8. Ingress が Secret を参照して TLS 有効化
この仕組みによって、一度設定すればあとは何もしなくても証明書が自動的に更新され続けます。
利用するまでの手順
1. cert-managerのインストール
# インストール
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.2/cert-manager.yaml
# インストール確認
kubectl get pods -n cert-manager
以下のような感じで cert-manager のPodが生成されます。
$ kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-79559475b4-r8bdm 1/1 Running 0 29d
cert-manager-cainjector-966fc8fbc-gmk85 1/1 Running 0 29d
cert-manager-webhook-854cf5f458-7knns 1/1 Running 0 29d
2. ClusterIssuer の作成
ClusterIssuer は Let’s Encrypt から証明書を取得するための設定です。クラスター全体で共有されます。
cat <<'EOF' | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Let's Encrypt の本番サーバー
server: https://acme-v02.api.letsencrypt.org/directory
# 証明書の期限切れ通知を受け取るメールアドレス
email: your-email@example.com
# ACME アカウントの秘密鍵を保存する Secret
privateKeySecretRef:
name: letsencrypt-prod
# HTTP-01 チャレンジの設定
solvers:
- http01:
ingress:
ingressClassName: traefik
EOF
確認
# 確認
kubectl get clusterissuer
# 出力例
NAME READY AGE
letsencrypt-prod True 29d
3. Ingress での証明書設定
Ingress リソースにアノテーションを追加するだけで、cert-manager が自動的に証明書を取得します。
単一ドメインの場合
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: default
annotations:
# cert-manager に証明書を取得させる
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- app.example.com
# 証明書を保存する Secret 名
secretName: my-app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
複数ドメインの場合
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- blog.example.com
- example.com
secretName: blog-tls
rules:
- host: blog.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blog
port:
number: 80
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blog
port:
number: 80
証明書発行の流れ
1. Ingress 作成
↓
2. cert-manager が Ingress を検知
↓
3. Certificate リソースを自動作成
↓
4. CertificateRequest を作成
↓
5. Order を作成(Let's Encrypt への注文)
↓
6. Challenge を作成(HTTP-01 検証)
↓
7. 一時的な Ingress/Pod を作成
(/.well-known/acme-challenge/xxx に応答)
↓
8. Let's Encrypt が検証
↓
9. 証明書発行
↓
10. Secret に保存(tls.crt, tls.key)
4. 証明書の確認コマンド
- Certificate リソースの状態の確認
$ kubectl get certificate -A
私の運用環境では以下のようになっています。
# kubectl get certificate -A
NAMESPACE NAME READY SECRET AGE
argocd argocd-cert True argocd-tls 29d
default blog-tls True blog-tls 29d
default n8n-tls True n8n-tls 28d
- 発行中の Challenge 確認
kubectl get challenges -A
証明書発行中は Challenge リソースが表示されます。発行完了後は削除されます。
- Certificate の詳細
kubectl describe certificate blog-tls -n default
- 証明書更新タイミングの確認
kubectl describe certificate <certificate name> -n default | grep -E "(Not After|Renewal Time)"
まとめ
cert-managerによる証明書取得・利用の流れを簡単に紹介しました。 割と手軽に無料でHTTPS運用ができるのと、証明書運用の勉強になると思いました。
「HTTPS化したいけど証明書管理が面倒」と思っている方は、ぜひcert-manager + Let’s Encryptを試してみてください。