<-- mermaid -->

Sealed Secretsを利用したKubernetes Secretリソースのセキュアな管理

はじめに

はじめまして、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です。

github.com

Sealed Secrets以外にもSecretリソースのセキュアな管理を実現するツールは存在しますが、Sealed Secretはアーキテクチャやできる事がシンプルで学習コストが極めて低いという部分が魅力だと思います。

アーキテクチャ

Sealed Secretsアーキテクチャ

まず、Sealed Secretsのアーキテクチャを構成しているメインとなるリソースは下記となります。

  • sealed-secretsコントローラー(を管理するDeployment)
    • Sealed Secretsのコアとなるコントローラー。kind: SealedSecretのCRDをCreate/Update/Deleteを監視する
  • 公開鍵/秘密鍵のペアを格納したSecretリソース
    • Secretリソースを暗号化/復号化するために利用するキー。

上記のリソースを利用し、下記のような流れでSecretリソースを管理します。

  1. Sealed Secretsによって生成された公開鍵を利用しkubesealコマンドによってSecretリソースをパース
    • kubesealコマンドによってパースすることでkind: SealedSecretリソースという機密情報が公開鍵で暗号化されたマニュフェストファイルが生成される
  2. kubectlコマンドでSealedSecretリソースをKubernetesクラスタにデプロイ
  3. sealed-secretsコントローラーがSealedSecretリソースのデプロイを検知
  4. sealed-secretsコントローラーがSealed Secretsによって生成された秘密鍵を利用しSealed SecretsリソースをSecretリソースにパースしデプロイ
    • 1の手順で利用した公開鍵のペアとなっている秘密鍵を利用しパースすることで暗号化された内容が復号化されたマニュフェストファイルがデプロイされる

このように、暗号化されたSealedSecretリソースをKubernetesクラスタにデプロイすることで、sealed-secretsコントローラがそれを検知し、自動的に暗号化された内容が復号化されたSecretリソースをクラスタ内部にデプロイしてくれます。

また、SealedSecretリソースは暗号化されているため、GitHub等に公開したとしても対となる秘密鍵を知っている人にしか復号化することができません。 これによりセキュアにSecretリソースをGitHub等で管理することが可能になります。

インストール〜リソースデプロイ

アーキテクチャを説明したところで、実際にインストールからSecretリソースのデプロイまでを行いたいと思います。

インストール

今回はhelmを利用してインストールを行います。 helmのチャートは下記リポジトリにstableなものが公開されています。

github.com

$ 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.crttls.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を利用し管理し始めたりしています。

Page top