悩ましい問題 Secretの管理
クラウドのサービスを利用するにあたり、アプリケーションの実行などに使うSecret(パスワードやトークン、各種キーなど)の管理は悩みのタネになると思います。
Kubernetesではこれらのような秘密情報は、KubernetesのSecretリソースで管理します。 しかし、Kubernetesの標準のSecretは元の秘密情報をbase64エンコードするだけであり、APIアクセス可能なユーザーであれば、データを取り出してエンコードした秘密情報を復元するのは容易です。
公式のドキュメントにあるように、データストア(etcd)に暗号化されずに保存されており、APIにアクセスできる人は誰でもSecretを取得または変更が可能です。そのため、安全にSecretを暗号化する仕組みが必要と書かれています。
今回は、通常のSecretに一手間加えてGitでSecretの管理を安心して行えるツールである、SealedSecretsについて取り上げてみようと思います。
概要
Sealed Secretsはつぎの2つで構成されています。
- クラスターサイドのコントローラー/オペレーター
- クライアントサイドのユーティリティ: kubesealツール
kubesealユーティリティは、非対称暗号を使用して、コントローラーのみが復号化できるシークレットを暗号化します。これは公開鍵を使用し情報を暗号化、対応する秘密鍵を使用して暗号テキストを復号化する仕組みです。
クライアントの導入
macOSの場合はbrew install kubeseal
、他のOSの場合はバイナリーをダウンロードしてインストールします。
2022/07/19現在、手元の環境ではv0.18.1がインストールされるのを確認しています。
Kubernetesクラスターの準備
試しに使って見る場合は手元の環境にKubernetesとhelm v3のCLIを準備しましょう。 Rancher Desktopなどを使うと、Rancher Desktopを起動するだけでCLIもクラスターもすぐ実行できるので便利です。
使い方はRancher Desktopドキュメントをご覧ください。
コントローラー/オペレーターの導入
Sealed Secretsのコントローラーの導入は、Helmチャートを使って次のように行います。 Helm V3が必要です。
指定するバージョンはHelmチャートリポジトリーの情報から、最新版を指定しました。
$ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets $ helm repo update $ helm upgrade --install sealed-secrets sealed-secrets/sealed-secrets \ --namespace sealed-secrets --create-namespace \ --version 2.4.0 \ --wait
次を実行して、Sealed Secrets Podの状況を確認します。
$ kubectl get pod -n sealed-secrets
次を実行して、Sealed Secretsのカスタムリソースが登録されたことを確認します。
$ kubectl get crd
次を実行して、暗号化・復号化を行う鍵(sealing key)がSecretリソースに登録されていることを確認します。この中に公開鍵と秘密鍵のペアが格納されています。
$ kubectl get secret -n sealed-secrets -l sealedsecrets.bitnami.com/sealed-secrets-key
Secretの登録
まず、Secretを作成してみます。「password=special-secret」の部分がSecretですね。
$ kubectl create secret generic test-secret -n default --dry-run=client \ --from-literal password=special-secret -o yaml > test-secret.yaml
そうすると、次のようなファイルが作成されます。これはKubernetes Secretのものです。データはBase64エンコードされているだけです。
% cat test-secret.yaml apiVersion: v1 data: password: c3BlY2lhbC1zZWNyZXQ= kind: Secret metadata: creationTimestamp: null name: test-secret namespace: default
試しにデコードしてみましょう(macOSの場合)。
$ echo c3BlY2lhbC1zZWNyZXQ= | base64 -D special-secret
あっという間にバレてしまいました。つまり標準のSecretで生成したYAMLファイルは、そのままのファイルをGitで管理するのはまずいということがわかりました。
ではこれをSealed Secretsで暗号化してみます。
$ cat test-secret.yaml | kubeseal --controller-name=sealed-secrets \ --controller-namespace=sealed-secrets \ --format yaml > sealed-secret.yaml
作成されたファイルを確認すると、bitnami.com/v1alpha1
というAPIを利用する「パスワード」が暗号化された状態のマニフェストファイルになっていると思います。
この暗号化されたデータを復号化するために必要なキーはクラスターにしかないため、万が一このマニフェストファイルが外部のユーザーの手に渡ったとしても復号化はできません。そのため、GitOps, CI/CDなどで利用するためにこの暗号化されたシークレットを記述したマニフェストをGitHubなどにおいたとしても安全であると言えます。
Secretを使ってアプリケーションを実行してみる
登録したSecretを使って、アプリケーションを実行してみます。 といっても、マニフェストファイルを使って暗号化されたSecretを登録した段階で、復号化済みのSecretがクラスターに登録されているため、扱い方はこれまでのSecretと同じように利用する感じです。
apiVersion: v1 kind: Pod metadata: name: sample-pod spec: containers: - name: nginx-container image: nginx:1.23.0-alpine env: - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: test-secret key: password
SecretがPodの環境変数に登録されていることを確認します。SECRET_PASSWORD
の変数にはBase64エンコードした値ではなく、デコード済みの値が設定されていることがわかります。
% kubectl exec -it sample-pod -- env|grep SECRET_PASSWORD SECRET_PASSWORD=special-secret
他のKubernetesクラスターに持って行った場合どうか
先程成功したシークレットが書き込まれているマニフェストファイルを使って、他のクラスターで実行した場合の挙動を確認します。
次のように実行すると、シークレットは作成できたと表示されます。
% kubectl apply -f test-sealed-secret.yaml sealedsecret.bitnami.com/test-secret created % kubectl get -f test-sealed-secret.yaml NAME AGE test-secret 90m
しかし、「test-secret」はないと出力されます。
% kubectl get secret test-secret -n default -o yaml Error from server (NotFound): secrets "test-secret" not found
これだけだとわからないので、kubectl describe
してみます。
全部貼り付けると長いので、必要なところだけ抜き出すと...
% kubectl describe -f test-sealed-secret.yaml ... Template: Data: <nil> Metadata: Creation Timestamp: <nil> Name: test-secret Namespace: default Status: Conditions: Last Transition Time: 2022-07-19T04:40:52Z Last Update Time: 2022-07-19T04:40:52Z Message: no key could decrypt secret (password) Status: False Type: Synced Observed Generation: 1 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning ErrUnsealFailed 41s (x6 over 42s) sealed-secrets Failed to unseal: no key could decrypt secret (password)
復号化できなかったようです。 暗号化・復号化を行う鍵(sealing key)は元のKubernetesクラスターのSecretにあるため、これと一致していないクラスターで暗号化済みマニフェストを適用しても、元の秘密情報は復元できないということです。
まとめ
SealedSecretを使うことで、安全にシークレット情報もGit管理できます。 これによりすべてのIaCをGitで管理するGitOpsが更にはかどります。
とはいえ使い方を誤ると秘密情報の漏洩につながるため、公式のベストプラクティスをみながら、適切にSealedSecretの運用管理をする必要がありそうです。
なお、この記事は次の情報を参考にしています。こちらも合わせてご覧ください。