Sealed Secretsでk8s上で利用する機密情報をGitなどでセキュアに管理する
はじめに
k8sには、Secretという機密情報を扱うリソースがありますが、こいつは基本情報をBase64エンコードしたもので扱われます。例えばSecretのリソース定義ファイルをGitで管理したいとなった場合定義ファイルに書かれる機密情報はただBase64されてるだけなのでそのままでは管理できない(やりにくい)という問題があります。
こういった問題にたいして、様々な対象方法はあるかと思いますが、その中の1つであるSealed Secretsを試してみたいと思います。
Sealed Secretsとは
Sealed SecretsのGitHubには以下のように書かれています。
Problem: "I can manage all my K8s config in git, except Secrets."
Solution: Encrypt your Secret into a SealedSecret, which is safe to store - even to a public repository. The SealedSecret can be decrypted only by the controller running in the target cluster and nobody else (not even the original author) is able to obtain the original Secret from the SealedSecret.
わかりやすいProblemですね。前述の通りSecretではBase64しただけの機密情報を扱うことになるので、Gitでの管理がなかなか難しくなってきます。それに対して、Sealed Secretでは機密情報を暗号化し、その複合ができるのはクラスターで動くコントローラーのみとなります。
機密情報は暗号化されるため、パブリックのGitリポジトリにPushすることも可能になります。
Sealed Secretsは以下の2つのパートから成り立ちます。
- クラスターサイド: controller / operator
- クライアントサイドユーティリティ: kubeseal
kubeseal
は公開鍵暗号方式で情報を暗号化し、その複合を行えるのはコントローラーだけとなります。
SealedSecretとSecretは、完全に同じではありませんがDeploymentとPodの関係に似ていると説明されています。
kubeseal
は公開鍵をk8sのAPIサーバーから取得し、情報を暗号化します。kubeseal --cert mycert.pem
のようにして、Pemファイルを直接指定してオフラインでの暗号化も可能なようです。この場合は、kubeseal --fetch-cert >mycert.pem
でPemファイルを取得できるようです。鍵はコントローラーの起動時にログにも出力されるようです。
動かしてみる
環境
今回のk8sクラスターはminikubeを用いて作成します。
$ uname -srvmpio Linux 5.4.0-99-generic #112-Ubuntu SMP Thu Feb 3 13:50:55 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a LSB Version: core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal $ docker version Client: Docker Engine - Community Version: 20.10.12 API version: 1.41 Go version: go1.16.12 Git commit: e91ed57 Built: Mon Dec 13 11:45:33 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.12 API version: 1.41 (minimum version 1.12) Go version: go1.16.12 Git commit: 459d0df Built: Mon Dec 13 11:43:42 2021 OS/Arch: linux/amd64 Experimental: true containerd: Version: 1.4.12 GitCommit: 7b11cfaabd73bb80907dd23182b9347b4245eb5d runc: Version: 1.0.2 GitCommit: v1.0.2-0-g52b36a2 docker-init: Version: 0.19.0 GitCommit: de40ad0 $ go version go version go1.17.6 linux/amd64 $ minikube version minikube version: v1.25.1 commit: 3e64b11ed75e56e4898ea85f96b2e4af0301f43d $ helm version version.BuildInfo{Version:"v3.8.0", GitCommit:"d14138609b01886f544b2025f5000351c9eb092e", GitTreeState:"clean", GoVersion:"go1.17.5"} $ kubectl version -o yaml clientVersion: buildDate: "2021-12-16T11:41:01Z" compiler: gc gitCommit: 86ec240af8cbd1b60bcc4c03c20da9b98005b92e gitTreeState: clean gitVersion: v1.23.1 goVersion: go1.17.5 major: "1" minor: "23" platform: linux/amd64 serverVersion: buildDate: "2021-12-16T11:34:54Z" compiler: gc gitCommit: 86ec240af8cbd1b60bcc4c03c20da9b98005b92e gitTreeState: clean gitVersion: v1.23.1 goVersion: go1.17.5 major: "1" minor: "23" platform: linux/amd64
今後実行するコマンドは、特に指定がない場合はminikubeのコンテキストを指しています。
インストール
インストールはおおきく以下の2つのことを行う必要があります。
- Sealed Secretsのクラスターへのデプロイ
- kubesealedのインストール
先ずは、Sealed Secretsのクラスターへのデプロイを行います。
READMEのInstallationによるとこれを行うには以下の3つの方法が提供されているようです。
- Kustomize
- Helm Chart
- Operator Framework
GKEなどでセットアップする場合はこちらを確認してください。
今回はHelm Chartを使ったデプロイの方法を試してみたいと思います。Sealed SecretsのHelm ChartはGitHubの公式レポジトリでホスティングされています。以下のコマンドを実行し、リポジトリの追加を行います。
$ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets "sealed-secrets" has been added to your repositories
ここで少し注意が必要なのは、バージョニングのスキーマで、このHelm Chartはもともとはコミュニティで作成されていたもので、メジャーバージョンが1.x.y
で始まっています。しかし、Sealed SecretsのVersionhaまた0
なので、以下のような対応関係を持ちます。
- Sealed SecretsのコントローラーのVersion: 0.X.Y
- Helm ChatのVersion: 1.X.Y-rZ
と、思いましたが....どうもChartの方は2系がすでに出ているみたいですね...しかもバージョニングスキーマも説明されるものとは少し違うようです。
$ helm search hub sealed-secrets URL CHART VERSION APP VERSION DESCRIPTION https://artifacthub.io/packages/helm/bitnami-la... 2.1.2 v0.17.3 Helm chart for the sealed-secrets controller. https://artifacthub.io/packages/helm/wenerme/se... 2.1.2 v0.17.3 Helm chart for the sealed-secrets controller. https://artifacthub.io/packages/helm/openinfrad... 1.16.1 v0.16.0 Helm chart for the sealed-secrets controller. https://artifacthub.io/packages/helm/cloudnativ... 1.0.2 0.7.0 A Helm chart for Sealed Secrets https://artifacthub.io/packages/helm/redhat-cop... 1.10.2 0.12.1 A Helm chart for Sealed Secrets
この辺は、もしかしたらドキュメントが少し古くなってるのかも知れません。ひとまずSealedSecretsのバージョンとHelm Chartのバージョンは完璧には対応づかないようなので注意が必要です。
今回は最新のChartを使ってデプロイを行いたいと思います。デプロイはこちらを参考に以下のコマンドを実行します。
$ helm install --namespace kube-system my-release sealed-secrets/sealed-secrets NAME: my-release LAST DEPLOYED: Sat Feb 12 10:26:21 2022 NAMESPACE: kube-system STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: (省略)
これでデプロイは完了です。my-release
のところは任意のものを入れます。もしクリーンアップしたい場合はhelm --namespace kube-system delete my-release
のコマンドで実行できます。
kubectl
コマンドで諸々がデプロイされていることを確認します。
$ kubectl -n kube-system get all NAME READY STATUS RESTARTS AGE pod/coredns-64897985d-qfl8f 1/1 Running 0 34m pod/etcd-minikube 1/1 Running 1 34m pod/kube-apiserver-minikube 1/1 Running 1 34m pod/kube-controller-manager-minikube 1/1 Running 1 34m pod/kube-proxy-vlq5q 1/1 Running 0 34m pod/kube-scheduler-minikube 1/1 Running 1 34m pod/my-release-sealed-secrets-559446f98f-52szw 1/1 Running 0 83s pod/storage-provisioner 1/1 Running 1 (33m ago) 34m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 34m service/my-release-sealed-secrets ClusterIP 10.101.162.188 <none> 8080/TCP 83s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 34m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/coredns 1/1 1 1 34m deployment.apps/my-release-sealed-secrets 1/1 1 1 83s NAME DESIRED CURRENT READY AGE replicaset.apps/coredns-64897985d 1 1 1 34m replicaset.apps/my-release-sealed-secrets-559446f98f 1 1 1 83s
これでデプロイまでは完了しました。
次にクライアント側のインストールです。
READMEにとると、kubeseal
をインストールするには以下のようなやり方が用意されているようです。
- Homebrew
- MacPorts
- Installation from source
私の環境はLinuxなので最後のInstallation from source
の方法を試そうかと思ったのですが、ちょっとうまく行かなかったのでバイナリを直接ダウンロードしてパスを通そうと思います。
現状の最新である0.17.3のリリースページを確認し、以下のコマンドでインストールします。
$ wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.17.3/kubeseal-0.17.3-linux-amd64.tar.gz $ sudo mkdir /opt/kubeseal $ sudo tar -zxvf kubeseal-0.17.3-linux-amd64.tar.gz -C /opt/kubeseal
あとはええ感じにパスを通して、OKです。
$ kubeseal --version kubeseal version: 0.17.3
kubesealで作ったSecretsを暗号化する
kubesealでSecretsを暗号化します。
今回はAPIサーバ経由で鍵を取得して、暗号化する方法を試したいと思います。もし、この方法が使えない場合は以下のようなコマンドを実行してPemファイルを取得します。
$ kubeseal --fetch-cert \ --controller-name=my-release-sealed-secrets \ --controller-namespace=kube-system \ > pub-cert.pem
少し脇道にそれましたが、話を戻します。
適当なSecretsを作って、そのJsonファイルを出力します。今回はfoo=bar
というSecretsを作成します。
$ echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o json >mysecret.json
次にそのSecretsのJsonを暗号化します。
$ kubeseal --controller-name=my-release-sealed-secrets --controller-namespace=kube-system <mysecret.json >mysealedsecret.json
ここでのポイントはコントローラーの名前(今回の場合はDeploymentの名前で行けました)とネームスペースをきちんと指定してやることです。
出力されたファイルは暗号化されたデータとなっているので、Gitでも管理できます(Twitterに上げることもできるよ!って書いてあった)。
$ cat mysealedsecret.json { "kind": "SealedSecret", "apiVersion": "bitnami.com/v1alpha1", "metadata": { "name": "mysecret", "namespace": "default", "creationTimestamp": null }, "spec": { "template": { "metadata": { "name": "mysecret", "namespace": "default", "creationTimestamp": null }, "data": null }, "encryptedData": { "foo": "AgCHg3oahao+sLw2gPlH+9SaxWlYdG06/M5CZAOZ2hOHXZ9deWFF/bMpt+YRlThK5c1mNIj0rf/NsVxWzWL3N4/LFeNFYmJ/orjSYln3Qu4+2F03kKH30kcz23X8CeTQjpRoIYYsy6S0bLMn+Svs5EB669K/n+nEWNjXb5BmO3438GamQ5jodLlcv5zjZLjEpwrqb31HTs44r3NKhzp7sJZ5DaU5Q28r9IkGQneDDi6Y4dRwF/Kp80uiA9DGXRPcG39l0xfsljdEdwTF9NucazQEZ14eI3VUQD/ofQ2gdpkAUaOKW4nr9pHsQk+KErifBGZQOtvaCxqp2NoFwAG9lXcW0PntzR3m0VY4bqYCslr+Ma5D0kyuYojsOZHWJEmVPnrBu18sSzgdQSHgK5lj85hHgWzMWTB9LCifzyjUMzpHpOkVqvf0RmWAVTIGC5KrT4lbN/w0rCMW6mKfcszYUtUshVZYgaTcTMi+MPNAmSknDUVU2owOIzYTMzyz7iGXaM8zhS3q95h98rJiXiVzJIxLKxxbbmSI6bNuTYalstwXLqx3V5kZqnfLNAfWIS5+Hz2FSjrRlXyZCrCnTKrbij4Q+G97URlKTgekjOKyMfT7XVUpIr6R7dxMwTVDBXveSuvXwqPDsNsyHdF6aSPdUHpkCpAdzCxJLN0F6rzbNbZO4RJmA6kP2PVG90vySur/5sBuMIc=" } } }
できたSealed Secretsをクラスターにデプロイします。
$ kubectl create -f mysealedsecret.json sealedsecret.bitnami.com/mysecret created
これで完了です。デプロイされたSealed Secretsはコントローラーによって複合されてSecretsが作成されます。
$ kubectl describe secret mysecret Name: mysecret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== foo: 3 bytes
後は、普通にSecretsを使う要領で利用できます。