はじめに
はじめまして、UZABASE SPEEDA SREの鈴木(@sshota0809)です。
今回は、Sealed Secretsを利用したKubernetesのSecretリソースをセキュアに管理する方法を紹介します。
TL;DR
KubernetesのSecretリソースはBase64の形式にエンコードされるため、エンコードされた内容が確認できれば簡単にデコードできてしまいます。 そのため、SecretリソースをGitHubなどで公開してしまうと機密情報を盗まれてしまうリスクがあります。
一方で、昨今はGitOpsが流行っていたりとKubernetesのリソース(マニュフェストファイル)をGitHub等など管理する需要は高く、当然Secretリソースもその例外ではありません。
- Bitnamiが開発するOSSであるSealed Secretsを利用することで、Secretリソースをセキュアに管理することが可能となる
Sealed Secretsとは
概要
Bitnamiが開発している、KubernetesのSecretリソースをセキュアに管理することを目的としたOSSです。
Sealed Secrets以外にもSecretリソースのセキュアな管理を実現するツールは存在しますが、Sealed Secretはアーキテクチャやできる事がシンプルで学習コストが極めて低いという部分が魅力だと思います。
アーキテクチャ
まず、Sealed Secretsのアーキテクチャを構成しているメインとなるリソースは下記となります。
- sealed-secretsコントローラー(を管理するDeployment)
- Sealed Secretsのコアとなるコントローラー。
kind: SealedSecret
のCRDをCreate/Update/Deleteを監視する
- Sealed Secretsのコアとなるコントローラー。
- 公開鍵/秘密鍵のペアを格納したSecretリソース
- Secretリソースを暗号化/復号化するために利用するキー。
上記のリソースを利用し、下記のような流れでSecretリソースを管理します。
- Sealed Secretsによって生成された公開鍵を利用し
kubeseal
コマンドによってSecretリソースをパースkubeseal
コマンドによってパースすることでkind: SealedSecret
リソースという機密情報が公開鍵で暗号化されたマニュフェストファイルが生成される
kubectl
コマンドでSealedSecretリソースをKubernetesクラスタにデプロイsealed-secretsコントローラー
がSealedSecretリソースのデプロイを検知sealed-secretsコントローラー
がSealed Secretsによって生成された秘密鍵を利用しSealed SecretsリソースをSecretリソースにパースしデプロイ- 1の手順で利用した公開鍵のペアとなっている秘密鍵を利用しパースすることで暗号化された内容が復号化されたマニュフェストファイルがデプロイされる
このように、暗号化されたSealedSecretリソースをKubernetesクラスタにデプロイすることで、sealed-secretsコントローラがそれを検知し、自動的に暗号化された内容が復号化されたSecretリソースをクラスタ内部にデプロイしてくれます。
また、SealedSecretリソースは暗号化されているため、GitHub等に公開したとしても対となる秘密鍵を知っている人にしか復号化することができません。 これによりセキュアにSecretリソースをGitHub等で管理することが可能になります。
インストール〜リソースデプロイ
アーキテクチャを説明したところで、実際にインストールからSecretリソースのデプロイまでを行いたいと思います。
インストール
今回はhelmを利用してインストールを行います。
helmのチャートは下記リポジトリにstable
なものが公開されています。
$ git clone https://github.com/helm/charts.git $ helm template charts/stable/sealed-secrets --name sealed-secrets --namespace sealed-secrets > sealed-secrets.yaml $ kubectl apply -f sealed-secrets.yaml
helmによって必要なリソースがすべてデプロイされます。 それではデプロイされたリソースの一覧を見てみます。
$ kubectl get all -n sealed-secrets NAME READY STATUS RESTARTS AGE pod/sealed-secrets-fff45fbcf-29mrg 1/1 Running 0 4d10h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/sealed-secrets ClusterIP 10.60.174.242 <none> 8080/TCP 77d NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/sealed-secrets 1/1 1 1 77d NAME DESIRED CURRENT READY AGE replicaset.apps/sealed-secrets-fff45fbcf 1 1 1 33d $ kubectl get secret -n sealed-secrets NAME TYPE DATA AGE default-token-94w7c kubernetes.io/service-account-token 3 77d sealed-secrets-keysmg5q kubernetes.io/tls 2 77d sealed-secrets-token-59zdg kubernetes.io/service-account-token 3 77d
重要なのはSecretリソースの一覧に表示されているsealed-secrets-keysmg5q
というリソースです。
こちらが公開鍵と秘密鍵のペアを定義しているSecretリソースとなります。
リソースの中身は、確かに下記のようにtls.crt
とtls.key
が定義されているのが確認できます。
apiVersion: v1 data: tls.crt: ......... tls.key: ......... kind: Secret ...
インストール手順の一貫として、SecretリソースをSealedSecretリソースにパースする際に利用するkubeseal
コマンドをインストールします。
筆者の環境はmacOSのため、それじ準じた手順としております。
$ brew install kubeseal
Secretリソースのパース
それでは、インストールが完了し準備が整ったのでSecretリソースをパースしてSealedSecretリソースを生成します。
繰り返しにはなりますが、kubeseal
コマンドによってSecretリソースをパースする際、Sealed Secretsによって生成された公開鍵が必要となります。
そのため、何らかの方法でそれを抜き出す必要があります。
やり方は自由なのですが、kubeseal
コマンドでは公開鍵のexport機能があるため、それを利用します。
$ kubeseal --fetch-cert \ --controller-namespace=sealed-secrets \ --controller-name=sealed-secrets \ > pub-cert.pem
こうすることで、kubeseal
コマンドを実行しているローカルの環境にpub-cert.pem
という形で公開鍵をexportすることができます。
それでは、この公開鍵を使ってSecretリソースをパースしたいと思います。
今回利用するテスト用のSecretリソースは下記となります。
test-secret.yaml
apiVersion: v1 data: test.txt: cGFzc3dvcmQK kind: Secret metadata: name: test-secret namespace: sealed-secrets type: Opaque
下記コマンドでパースを行います。
$ kubeseal --format=yaml --cert=pub-cert.pem < test-secret.yaml > test-sealedsecret.yaml
すると下記のようなtest-sealedsecret.yaml
が生成されました。
test-sealedsecret.yaml
apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: creationTimestamp: null name: test-secret namespace: sealed-secrets spec: encryptedData: test.txt: AgCKSFmH7nARRrUmQSMsJs5YuRiaFe43eG7ynScc3yKAsJaOUSWyCAyGHauyU0jGlySqhXV2LNnhEtzR1gVIsi2ScWb1+fLOqsw7ronOkmVO8XwddII4ytE9Cb8UA5plZgy6ujRaea++Zr85UuXwLavsQRSMp/rne4bJ7bvng1a4znjSRN5JXPoAIpf4zUWKoLUrhOt6HHhmS8VZ8tM7yf+uS3t3dqDLYfuGStH6ECKszQaCFlNUdokTBv0th1V2rzbzgeRTT0eA3inThsFCa+cnVGqUeM3EdvTLxIltRCQsLBQJmkmGug8xATkPFDWAvn9SLvow4jHcQolzVrRSXZCVs3oyceriK6f2k/Umohs2g78u9lCGRWzrBp+kWsdfQh+cv7+mZvr46evYbAQUJ7pbRk6axXwPJmD3GCg9oIk6RtB/ug8AIUjdkI81RELfMcliQYuySSb2GF8+hkCAgMXLBRZkS1oxCHinbn0AOeRQbm87QwcXFpICBWlyliLtjZ7oa9QB53zRf3pbxsNcxYSTzA6fZEH8GFopADjuZ8OvS2qT83I9ULPhONb2Y7wJkDrpuKQIBqzRsMgv7zMQD16TilzFcXtEVWkDz+Zjwsoio/+lJ83QLJLkGZw5Y8p4rW9D0lfqOo2W0l1q6BpiH4TmZawLmP5TOxQ+xwcWfHtDf6nzJLwvfzsZRhqrmjXD3j+pexf3QeqI3aA= template: metadata: creationTimestamp: null name: test-secret namespace: sealed-secrets type: Opaque status: {}
Kubernetesクラスタへのデプロイ
SealedSecretリソースが生成できたら、最後にこれをKubernetesクラスタにデプロイします。
$ kubectl apply -f test-sealedsecret.yaml
すると下記のようにSealedSecretリソースとコントローラーがパースしたSecretリソースそれぞれがデプロイされたことが確認できます。
$ kubectl get sealedsecret -n sealed-secrets NAME AGE test-secret 49s $ kubectl get secret -n sealed-secrets NAME TYPE DATA AGE test-secret Opaque 1 55d
後はこのSecretリソースを通常通り各種リソースで利用するだけです。
おわりに
Sealed Secretsは学習コストも低く、できる事もSecretリソースを暗号化/復号化という部分にフォーカスされているのでとてもシンプルです。 GitOpsを実践しようと思っているけどSecretリソースの管理をどうしよう、と悩んでる方ぜひ利用してみてください。
弊社SPEEDAチームではGitOpsはまだ実践できていませんが、アプリケーションに利用する機密情報を一部Sealed Secretsを利用し管理し始めたりしています。