kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Secret を暗号化して Git 管理を可能にする「Sealed Secrets」を試した

Kubernetes で設定値や機密情報を管理する場合,ConfigMapSecret が代表的な選択肢として挙げられる.しかし Secret は暗号化ではなく Base64 でエンコードをする仕様になっているため,Git リポジトリで直接マニフェストを管理できないという懸念点がある.ドキュメントにも Base64 エンコーディングは「平文と同様であると判断すべき (Base64 encoding is not an encryption method and is considered the same as plain text.)」と書いてある.

kubernetes.io

Sealed Secrets とは

今回は Secret の暗号化ソリューションとして「Kubernetes 完全ガイド 第2版」「第13章」に載っていた「Sealed Secrets」を試す.「Sealed Secrets」を使うと,公開鍵暗号方式で Secret を暗号化できるため,Git リポジトリで管理できるようになる.仕組みとしては Kubernetes クラスターにデプロイをした Sealed Secret Controller によって復号されて Secret になる.よって,最終的には「Base64 でエンコードされた Secret」になり,設定変更なしで Pod から参照できる.「Sealed Secrets」を開発した Bitnami の記事に載っている構成図を見るとイメージしやすいと思う.

github.com

f:id:kakku22:20201030235212p:plain
Bitnami Engineering: Sealed Secrets: Protecting your passwords before they reach Kubernetes から引用

検証環境

今回は 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 が使えるようになった!

docs.docker.com

インストール(クライアント側)

今回は Homebrewkubeseal コマンドをインストールする.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 をインストールする.インストール方法は KustomizeHelm ChartOperator Framework など,いろいろ用意されているけど,今回は GitHub Releases に載っている方法で直接インストールする.kubectl apply でマニフェストを適用したログを確認すると,Sealed Secret ControllerDeployment などがデプロイされている.

$ 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 コマンドを使えば簡単にデコードできる.今回の値はあくまでサンプルとして adminpassword にした.

$ echo 'YWRtaW4=' | base64 --decode
admin

$ echo 'cGFzc3dvcmQ=' | base64 --decode
password

次に kubeseal コマンドを使って kakakakakku-secret.yamlkakakakakku-sealed-secret.yaml として暗号化する.

$ kubeseal -o yaml < kakakakakku-secret.yaml > kakakakakku-sealed-secret.yaml

まず,kindSecret から SealedSecret に変わった.また spec.encryptedData には暗号化された usernamepassword が設定されている(以下の例では 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.yamlkubectl で適用する.すると 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 CodeShift + Command + P(コマンドパレット)Seal Kubernetes Secret - FileclusterWide公開鍵 と操作をすると以下のように右側に暗号化されたマニフェストが自動生成される.便利なのかは判断できないけど,試してみた!

f:id:kakku22:20201031005735p:plain

marketplace.visualstudio.com

まとめ

今回は「Sealed Secrets」を試した.Sealed Secret Controller によって暗号化されたマニフェストから Base64 でエンコードされた Secret に複合される仕組みは現場でも使えそう.GitHub の README.md を読むと他にも「ローテーション機能」もあった.継続的に運用していくための機能などはまた別途試そうと思う.

関連記事

kakakakakku.hatenablog.com