Kubernetes で設定値や機密情報を管理する場合,ConfigMap と Secret が代表的な選択肢として挙げられる.しかし Secret は暗号化ではなく Base64 でエンコードをする仕様になっているため,Git リポジトリで直接マニフェストを管理できないという懸念点がある.ドキュメントにも Base64 エンコーディングは「平文と同様であると判断すべき (Base64 encoding is not an encryption method and is considered the same as plain text.)」と書いてある.
Sealed Secrets とは
今回は Secret の暗号化ソリューションとして「Kubernetes 完全ガイド 第2版」の「第13章」に載っていた「Sealed Secrets」を試す.「Sealed Secrets」を使うと,公開鍵暗号方式で Secret を暗号化できるため,Git リポジトリで管理できるようになる.仕組みとしては Kubernetes クラスターにデプロイをした Sealed Secret Controller によって復号されて Secret になる.よって,最終的には「Base64 でエンコードされた Secret」になり,設定変更なしで Pod から参照できる.「Sealed Secrets」を開発した Bitnami の記事に載っている構成図を見るとイメージしやすいと思う.
検証環境
今回は Docker Desktop for Mac "Edge" を使って,以下の Kubernetes 環境で試す.
$ kubectl version --short Client Version: v1.19.2 Server Version: v1.19.2
なお,10/19 にリリースされた v2.4.2.0
から Kubernetes 1.19.2 が使えるようになった!
インストール(クライアント側)
今回は Homebrew で kubeseal
コマンドをインストールする.10/29 にリリースされた「Sealed Secrets」の最新バージョン v0.13.1
を使う.実は最初に試したときはまだ v0.12.6
だったこともあり,記事のセルフレビューも兼ねて v0.13.1
でやり直した.
$ brew install kubeseal $ kubeseal --version kubeseal version: v0.13.1
インストール(Kubernetes クラスター側)
次に Kubernetes クラスターに Sealed Secret Controller をインストールする.インストール方法は Kustomize や Helm Chart や Operator Framework など,いろいろ用意されているけど,今回は GitHub Releases に載っている方法で直接インストールする.kubectl apply
でマニフェストを適用したログを確認すると,Sealed Secret Controller の Deployment などがデプロイされている.
$ kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/controller.yaml Warning: rbac.authorization.k8s.io/v1beta1 Role is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 Role role.rbac.authorization.k8s.io/sealed-secrets-key-admin created Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding clusterrolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole clusterrole.rbac.authorization.k8s.io/secrets-unsealer created service/sealed-secrets-controller created Warning: rbac.authorization.k8s.io/v1beta1 RoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 RoleBinding rolebinding.rbac.authorization.k8s.io/sealed-secrets-service-proxier created role.rbac.authorization.k8s.io/sealed-secrets-service-proxier created rolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created serviceaccount/sealed-secrets-controller created deployment.apps/sealed-secrets-controller created Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition customresourcedefinition.apiextensions.k8s.io/sealedsecrets.bitnami.com created $ kubectl get deployments.apps -n kube-system sealed-secrets-controller NAME READY UP-TO-DATE AVAILABLE AGE sealed-secrets-controller 1/1 1 1 50s
ちなみに Sealed Secret Controller をインストールする前に kubeseal
コマンドを使ったらエラーになった.
$ kubeseal -o yaml < kakakakakku-secret.yaml error: cannot fetch certificate: services "sealed-secrets-controller" not found
Secret を暗号化する
まず,単純な Secret マニフェストとして kakakakakku-secret.yaml
を作った.
apiVersion: v1 kind: Secret metadata: name: kakakakakku-secret type: Opaque data: username: YWRtaW4= password: cGFzc3dvcmQ=
値は Base64 でエンコードされているため,base64 --decode
コマンドを使えば簡単にデコードできる.今回の値はあくまでサンプルとして admin
と password
にした.
$ echo 'YWRtaW4=' | base64 --decode admin $ echo 'cGFzc3dvcmQ=' | base64 --decode password
次に kubeseal
コマンドを使って kakakakakku-secret.yaml
を kakakakakku-sealed-secret.yaml
として暗号化する.
$ kubeseal -o yaml < kakakakakku-secret.yaml > kakakakakku-sealed-secret.yaml
まず,kind
は Secret
から SealedSecret
に変わった.また spec.encryptedData
には暗号化された username
と password
が設定されている(以下の例では xxxxx
で埋めた).Sealed Secret Controller によって管理された公開鍵で暗号化されているため,鍵を安全に管理しているという前提においては復号されることはなく,Git リポジトリで管理できる.
$ cat kakakakakku-sealed-secret.yaml apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: creationTimestamp: null name: kakakakakku-secret namespace: default spec: encryptedData: password: xxxxx(実際にはもっと長い) username: xxxxx(実際にはもっと長い) template: metadata: creationTimestamp: null name: kakakakakku-secret namespace: default type: Opaque
公開鍵 と 秘密鍵
「Sealed Secrets」では Kubernetes クラスターの中に Secret として「公開鍵」と「秘密鍵」を管理している.以下のように kube-system
Namespace の中にある sealed-secrets-xxxxxxxx
Secret を取得すると「公開鍵(Base64 エンコード)」と「秘密鍵(Base64 エンコード)」を確認できる.同じく今回も xxxxx
で埋めた.GitHub の README.md
を読むと,バックアップを取る場合は kubectl get secret
の結果を YAML で出力しておくと書いてあった.
$ kubectl get secrets -n kube-system sealed-secrets-xxxxxxxx -o json | jq '.data' { "tls.crt": "xxxxx(実際にはもっと長い)", "tls.key": "xxxxx(実際にはもっと長い)" }
また kubeseal --fetch-cert
コマンドを使えば「公開鍵」を取得できる.
$ kubeseal --fetch-cert > mycert.pem
暗号化した Secret を適用する
さっそく「Sealed Secrets」で暗号化した kakakakakku-sealed-secret.yaml
を kubectl
で適用する.すると SealedSecret リソースだけではなく Secret リソースも適用されている!内部的には Sealed Secret Controller によって復号されて Secret まで適用されている.
$ kubectl apply -f kakakakakku-sealed-secret.yaml sealedsecret.bitnami.com/kakakakakku-secret created $ kubectl get sealedsecret kakakakakku-secret NAME AGE kakakakakku-secret 20s $ kubectl get secrets kakakakakku-secret NAME TYPE DATA AGE kakakakakku-secret Opaque 2 30s
結果的に Secret は Base64 でエンコードされているため,値も確認できる.
$ kubectl get secrets kakakakakku-secret -o json | jq -r '.data.username' YWRtaW4= $ kubectl get secrets kakakakakku-secret -o json | jq -r '.data.username' | base64 --decode admin% $ kubectl get secrets kakakakakku-secret -o json | jq -r '.data.password' cGFzc3dvcmQ= $ kubectl get secrets kakakakakku-secret -o json | jq -r '.data.password' | base64 --decode password%
Sealed Secret Controller のログを確認すると SealedSecret unsealed successfully
と出力されていた.
$ kubectl logs -n kube-system sealed-secrets-controller-xxxxxxxxxx-xxxxx controller version: v0.13.1 2020/10/30 15:00:00 Starting sealed-secrets controller version: v0.13.1 2020/10/30 15:00:00 Searching for existing private keys (中略) 2020/10/30 15:30:00 Event(v1.ObjectReference{Kind:"SealedSecret", Namespace:"default", Name:"kakakakakku-secret", UID:"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", APIVersion:"bitnami.com/v1alpha1", ResourceVersion:"4381", FieldPath:""}): type: 'Normal' reason: 'Unsealed' SealedSecret unsealed successfully
リソース削除
最後にリソースを削除しておく.
$ kubectl delete -f kakakakakku-sealed-secret.yaml $ kubectl delete -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.13.1/controller.yaml
Kubeseal extension for VS Code
関連ツールとして VS Code でも拡張機能「Kubeseal extension for VS Code」を使えば「Sealed Secrets」を使った Secret の暗号化ができる.既に紹介した kubeseal --fetch-cert
コマンドを使って「公開鍵」を取得して,VS Code で Shift + Command + P(コマンドパレット)
➔ Seal Kubernetes Secret - File
➔ clusterWide
➔ 公開鍵
と操作をすると以下のように右側に暗号化されたマニフェストが自動生成される.便利なのかは判断できないけど,試してみた!
まとめ
今回は「Sealed Secrets」を試した.Sealed Secret Controller によって暗号化されたマニフェストから Base64 でエンコードされた Secret に複合される仕組みは現場でも使えそう.GitHub の README.md
を読むと他にも「ローテーション機能」もあった.継続的に運用していくための機能などはまた別途試そうと思う.