cert-manager基礎知識
cert-managerとは
kubernetesクラスタ上でSSL/TLS証明書をシンプルに扱うためのツールです。
証明書の取得・更新・利用が簡単になります。
公式ドキュメント
この記事の内容
この記事では、cert-managerを利用していくにあたり、最低限おさえておきたい「証明書発行・利用の流れ」と、「登場人物」だけを簡単にまとめます。
証明書まわりは分かりづらいし、事故ったら大変なことになるので、あまり触りたくない箇所かもしれませんが、基礎知識を知っておくことで最初の足がかりになると思います。
cert-manager v1.6.1での話をしていきます。
また、Let's EncryptをIssuerとした流れを説明しますので、他のIssuer Typeでは内容が異なる箇所があります。
証明書発行・利用の簡単な流れ
まずざっくりとした流れを書いておきます。
実際のインストール方法や、操作などの詳細は公式ドキュメントに譲ります。
- cert-managerをインストールします。
-
Issuer
コンポーネントを作成します。 -
Certificate
コンポーネントを作成します。 -
Issuer
,Certificate
コンポーネントの設定に応じて、指定した証明書発行者に対して指定したドメインの証明書要求が行われます。 - 指定されたドメインが、証明書要求者のコントロール下にあるか確認するため、証明書発行者は
チャレンジ
を行います。 -
チャレンジ
が完了したら、発行された証明書と鍵がSecretリソースに保存されます。 -
Ingress
等から証明書・鍵を保存したSecretを参照することで、証明書を利用した暗号通信が可能となります。
登場人物
cert-managerが証明書を取得・利用する際に、関わってくる登場人物を紹介します。
Issuer
- 証明書発行者(CA: Certification Authority. 日本語では認証局)を表現しています。
- 文字通り、証明書を発行する人です。
- cert-managerにおいて、
Issuer
コンポーネントはCustom Resource Definition(CRD)で定義されるリソースです。-
Issuer
コンポーネントは同じnamespaceにしか、Certificate
を発行できません。- 複数namespaceにまたがって発行したい場合は
ClusterIssuer
コンポーネントを利用します。
- 複数namespaceにまたがって発行したい場合は
-
- エンドユーザーがmanifestファイルを作成し、管理します。
Certificate
- X.509に基づいた証明書を表現しています。
- こちらもCRDで定義されたKubernetes上のリソースです。
- X.509は証明書の規格です。
- コチラの記事に分かりやすく・詳しく書かれています。
- エンドユーザーがmanifestファイルを作成し、管理します。
ACME Orders and Challenges
- ACME(Automate Certificate Management Environment)
- 自動で証明書を管理してくれる環境のことです。
- Let’s Encryptが代表的なACME Issuerです。
- 証明書署名要求を通すために、ACMEクライアント(つまりcert-managerであり、エンドユーザー)は指定したドメインを所有していることを、ACME Issuerに対して証明しなければならない。
- これがACMEプロトコルにおける
チャレンジ
と呼ばれる仕組み。 - この
チャレンジ
を完了するために、cert-managerは以下の2つのCRDを導入します。Order
Challenge
- これがACMEプロトコルにおける
Order
-
チャレンジ
実行の要求を表現する。-
チャレンジ
の詳細はこちら。
-
- エンドユーザーが作成することはない。一度作ったら変更されない。
Challenge
-
チャレンジ
を表現する。- 複数ドメインに対する要求を行う場合、
Order
ひとつにつき複数のChallenge
が作成される。
- 複数ドメインに対する要求を行う場合、
- Challengeライフサイクル
-
Challenge
は、キューにいれられて順次処理される。 -
チャレンジ
が完了したらChallenge
リソースはKubernetesクラスタ上から消える。
-
- エンドユーザーが作成することはない。一度作ったら変更されない。
Ingress
- Kubernetesクラスタ内へのHTTP(S)アクセスを管理するリソース。
- NginxなどのWebサーバーとして提供される場合や、GCP, AWSなどのマネジメントサービスにおけるロードバランサとして提供される場合がある。
- SSL/TLS証明書を参照し、SSL終端としての機能を持つ。
- cert-managerを利用して取得した証明書は、最終的には
Ingress
が参照・利用することになる。
- cert-managerを利用して取得した証明書は、最終的には
- エンドユーザーがmanifestファイルを作成し、管理します。
-
Ingress
はcert-managerがCRDとして定義しているリソースではありません。
各登場人物のmanifestファイルサンプル
上述のとおり、以下3つのリソースはユーザーがmanifestファイルを作成・管理する必要があります。
Issuer
Certificate
Ingress
これら3つのリソースのmanifestファイルのサンプルと、その関係性を以下に記載していきます。
Issuer manifestファイルサンプル
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: YOUR_ISSUER_NAME
spec:
acme:
email: "hoge@example.com"
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: lets-encrypt
solvers:
- dns01:
cloudDNS:
project: example-com
serviceAccountSecretRef:
name: prod-clouddns-svc-acct-secret
key: service-account.json
Issuer
のmanifestで大切なのはmetadata.name
とspec以下
です。
metadata.name
はそのまんま、このリソースの名前となります。他のリソースからこのリソースを参照するので重要です。
spec以下
はIssuer
のTypeによって変わります。今回はLet's Encryptを使う前提ですので、spec.acme
です。
このmanifestだと、staging環境のLet's Encryptのサーバーに対して、DNS01チャレンジ
を実行しますよ、GCPのCloudDNSを使いますよ、example-com
というGCPプロジェクトのCloudDNSを使うためにprod-clouddns-svc-acct-secret
というSecretリソースにサービスアカウントのクレデンシャルを置いていますよ、という内容になります。
他のIssuerTypeを使う場合や、ACMEでも別の設定を選択する場合は公式ドキュメントを参照してください。
Certificate manifestファイルサンプル
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: YOUR_CERTIFICATE_NAME
spec:
secretName: YOUR_SECRET_NAME
dnsNames:
- example.com
issuerRef:
name: YOUR_ISSUER_NAME
kind: Issuer
Certificate
コンポーネントのmanifestファイルには、secretName
,dnsNames
,issuerRef
のフィールドが存在します。
dnsNames
で指定したドメインに対する証明書を、issuerRef
で指定したIssuer
から取得します。
secretName
はKubernetesのSecretリソースの名前を指定します。このSecretに実際の鍵と証明書が保持されることになります。
このmanifestファイルの設定であれば、example.com
に対する証明書が、YOUR_ISSUER_NAME
という名前のIssueer
から取得され、それぞれの証明書と鍵がYOUR_SECRET_NAME
という名前のSecretリソースに保存されることになります。
実際のSecretに保持される内容の例を以下に示します。
$ kubectl get secret ${YOUR_SECRET_NAME}
apiVersion: v1
kind: Secret
data:
tls.crt: LS0tLaa.....
tls.key: LS0tLOU....
creationTimestamp: "2022-00-00T00:00:00Z"
labels:
certmanager.k8s.io/certificate-name: YOUR_SECRET_NAME
name: YOUR_SECRET_NAME
namespace: YOUR_NAMESPACE_NAME
...
...
data.tls.crt
には証明書、data.tls.key
には鍵の値が入ります。
これらの値を使って暗号通信を実現します。
ちなみに、実際の証明書の値はSecretにだけ存在します。
紛らわしいですが、Certificate
リソースに証明書の値は存在しません。
代わりに証明書の失効・更新予定日などの情報を含んでいます。
$ kubectl get Certificate ${YOUR_CERTIFICATE_NAME}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
creationTimestamp: "2022-01-13T12:19:50Z"
generation: 1
name: YOUR_CERTIFICATE_NAME
namespace: YOUR_NAMESPACE_NAME
spec:
commonName: example.com
dnsNames:
- example.com
issuerRef:
kind: Issuer
name: YOUR_ISSUER_NAME
secretName: YOUR_SECRET_NAME
status:
conditions:
- lastTransitionTime: "2022-01-13T12:19:50Z" # 2022/1/13に作成
message: Certificate is up to date and has not expired
observedGeneration: 1
reason: Ready
status: "True"
type: Ready
notAfter: "2022-04-13T11:21:25Z" # 90日後の2022/4/13に失効
notBefore: "2022-01-13T11:21:26Z"
renewalTime: "2022-03-14T11:21:25Z" # 60日後の2022/3/14に更新予定
revision: 1
Ingress manifestファイルサンプル
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/issuer: YOUR_ISSUER_NAME
name: YOUR_INGRESS_NAME
namespace: YOUR_INGRESS_NAME
spec:
rules:
- host: example.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: YOUR_SERVICE_NAME
port:
number: 80
tls:
- hosts:
- example.com
secretName: YOUR_SECRET_NAME
tls.hosts[0].secretName
に、証明書と鍵を保持しているSecretの名前を入れてやることで、Ingressは証明書を参照して、暗号通信を実現します。
つまり、Certificate
のmanifest内のspec.secretName
と同じ値を入れてやればOKです。
まとめ
簡単ですが、cert-managerによる証明書取得・利用の流れをmanifestファイルのサンプルを含めて紹介しました。
cert-managerの公式ドキュメントは比較的充実していて、勉強になると感じます。
興味がある方はじっくり読んでみると面白いかもしれません。
Discussion