注意: kube-cert-manager より洗練された cert-manager が開発されているため、この記事の内容の採用はオススメしません。
この記事は Google Cloud Platform(1) Advent Calendar 2016 の17日目の記事です。
@apstndb です。好きな GCP のサービスは GKE です。
今回は Let's Encrypt から自動的に TLS 証明書を取得する実装の1つとして、Kubernetes 上で動作する
kube-cert-manager を使って、TLS 証明書が取得できることを確認します。
kube-cert-manager の特徴
Kubernetes で証明書を扱うソフトウェアには他にも kube-lego があります。
実は私も GKE でサービスを HTTPS と HTTP/2 に対応する(kube-lego 編) として記事を書いたので、下記では kube-lego と比較した kube-cert-manager の特徴を紹介します。
- Let's Encrypt の ACME プロトコルでのドメイン証明のために DNS-01 を使用
- kube-lego は Ingress を通して HTTP-01 を使用
- kube-cert-manager からドメインに対応する DNS ゾーンに TXT レコードを自動的に追加/削除
- Google Cloud DNS 対応
- 他の DNS ネームサーバには DNS Provider Plugins で対応可能
- HTTP(S) ロードバランサ以外で TLS を終端可能と自由度が高い
- 設定に Certificate という名前の ThirdPartyResource を使用
- kube-lego は Ingress で指定するドメイン名と Secret の名前以外はプロセスの環境変数に渡す必要があり、複数の設定を使い分けられなかった
- kube-cert-manager では証明書ごとに別の設定が可能
なお、ThirdPartyResource については Kubernetes Advent Calendar 2016 の17日目(この記事を書いている今日!)の記事である「Kubernetesを拡張しよう」がオススメです。
検証環境
- Kubernetes 1.5.1
- kube-cert-manager 0.2.0
なお、今回の記事では下記の状態を前提としています。
- GKE クラスタが既に存在
- 対象のドメイン名に対応する Managed Zone が Cloud DNS 上に既に存在
- 記事中ではドメイン名
example.cf
を使用
- 記事中ではドメイン名
注意: kube-cert-manager のバージョンについて
この記事を書くために動作確認をした時点でソースツリー上に Deployment が用意されていた最新バージョンである kube-cert-manager 0.2.0 を使用しましたが、これはサブドメイン対応がまだマージされていない頃のリリースだったようです。
既に DockerHub に 0.5.0 のタグが存在しているため、サブドメインを使いたい場合は適宜新しいバージョンを使ってください。
(一応プルリクエストは出しておきました。)
準備
kube-cert-manager を使うためにクラスタで一度だけ必要な準備をします。
Cloud DNS アクセス権のあるサービスアカウントの用意
kube-cert-manager は内部的にサービスアカウントを使って Cloud DNS にアクセスします。ここでは Service Account を準備します。
Cloud DNS アクセスに使うサービスアカウントを作成します。
$ gcloud iam service-accounts create dns-admin
Created service account [dns-admin].
サービスアカウントに Cloud DNS へのレコードの追加に必要な権限を設定します。2016年12月17日現在 roles/dns.admin が最も適切なロールのようです。
$ gcloud projects add-iam-policy-binding [PROJECT_ID] --member=serviceAccount:dns-admin@[PROJECT_ID].iam.gserviceaccount.com --role=roles/dns.admin
サービスアカウントの認証に使う JSON ファイルを取得します。
$ gcloud iam service-accounts keys create service-account.json --iam-account=dns-admin@[PROJECT_ID].iam.gserviceaccount.com
Certificate ThirdPartyResource の登録
kube-cert-manager では、ドメインと各種リソースの関連を表現するために ThirdPartyResource を使います。
下記のようにして ThirdPartyResource である Certificate を作成できるようにします。
$ kubectl create -f kube-cert-manager/extensions/certificate.yaml
thirdpartyresource "certificate.stable.hightower.com" created
kube-cert-manager が使用する GCE 永続ディスクの作成
kube-cert-manager は Let's Encrypt のアカウント情報や鍵を保存するために BoltDB をファイルシステム上で使用します。よって kube-cert-manager の Pod が終了しても消えないボリュームをマウントする必要があります。
今回は サンプルの kube-cert-manager/deployments/kube-cert-manager.yaml
で指定されている kube-cert-manager
という名前の GCE 永続ディスクを作成します。
$ gcloud compute disks create kube-cert-manager --size 10GB
WARNING: You have selected a disk size of under [200GB]. This may result in poor I/O performance. For more information, see: https://developers.google.com/compute/docs/disks#pdperformance.
Created [https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/zones/asia-northeast1-a/disks/kube-cert-manager].
NAME ZONE SIZE_GB TYPE STATUS
kube-cert-manager asia-northeast1-a 10 pd-standard READY
New disks are unformatted. You must format and mount a disk before it
can be used. You can find instructions on how to do this at:
https://cloud.google.com/compute/docs/disks/add-persistent-disk#formatting
kube-cert-manager のデプロイ
kube-cert-manager を GKE にデプロイします。
Deployment のマニフェストが用意されているので、そのまま使用します。
$ kubectl create -f kube-cert-manager/deployments/kube-cert-manager.yaml
deployment "kube-cert-manager" created
サービスアカウントの JSON ファイルの登録
先程作成した dns-admin
サービスアカウントを kube-cert-manager から使えるように、 JSON ファイルを Kubernetes Secret としてデプロイします。
$ kubectl create secret generic example-domain --from-file=service-account.json
secret "example-domain" created
秘密鍵と証明書の自動生成
準備が終わったので、ここから実際に kube-cert-manager の動作を確認します。
Certificate オブジェクトの作成
kubernetes/certificates/hightowerlabs-com.yaml
を元に Certificate リソースを作成します。
$ cat > example-cert.yaml <<EOF
apiVersion: "stable.hightower.com/v1"
kind: "Certificate"
metadata:
name: "example-dot-cf"
spec:
domain: "example.cf"
email: "hostmaster@example.cf" # Let's Encrypt に登録するメールアドレス
provider: "googledns"
secret: "example-domain" # 先程作成したサービスアカウントの JSON を含む Secret 名
secretKey: "service-account.json" # secret の中のサービスアカウントのファイル名
EOF
$ kubectl create -f example-cert.yaml
certificate "example-dot-cf" created
取得できていることの確認
kube-cert-manager の出力を見ると、下記のようにして証明書の取得が行われていることがわかります。
$kubectl logs -f $(kubectl get pod -l app=kube-cert-manager -o name) kube-cert-manager
(中略)
2016/12/17 10:10:24 Creating new Let's Encrypt account: example.cf
2016/12/17 10:10:33 Monitoring _acme-challenge.example.cf. DNS propagation: ns-cloud-a1.googledomains.com.:53 n
s-cloud-a2.googledomains.com.:53 ns-cloud-a3.googledomains.com.:53 ns-cloud-a4.googledomains.com.:53
2016/12/17 10:10:56 example.cf DNS-01 challenge complete on ns-cloud-a1.googledomains.com.:53
2016/12/17 10:10:59 example.cf DNS-01 challenge complete on ns-cloud-a2.googledomains.com.:53
2016/12/17 10:11:02 example.cf DNS-01 challenge complete on ns-cloud-a3.googledomains.com.:53
2016/12/17 10:11:03 example.cf DNS-01 challenge complete on ns-cloud-a4.googledomains.com.:53
2016/12/17 10:11:33 _acme-challenge.example.cf. DNS propagation complete.
2016/12/17 10:11:37 example.cf secret missing.
2016/12/17 10:11:37 example.cf secret created.
2016/12/17 10:11:41 Syncing Kubernetes secret: example.cf
実際に Secret の中に証明書と秘密鍵ができています。
$ kubectl describe secrets example.cf
Name: example.cf
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1785 bytes
tls.key: 1679 bytes
下記のようにすると、秘密鍵が正しく読めることも確認できます。(要 jq)
$ kubectl get secret example.cf -o json | jq -r '.data."tls.crt"' | base64 -D
-----BEGIN CERTIFICATE-----
(以下略)
まとめ
今回は kube-cert-manager を使用して GKE で TLS の秘密鍵と証明書を自動的に扱うことができることを確認しました。
プラグインさえ書けば Cloud DNS 以外にも対応可能な作りになっており、 Kubernetes のレイヤで TLS 対応することがかなり現実的に見えてきた気がしますね。
来年には HTTPS が必須な場面が更に増えてくるとされていますが、これなら乗り切れるんじゃないでしょうか。