Kubernetes における秘密情報の管理方法

Sreake事業部

2023.9.25

目次

自己紹介

竹下

2023年8月21日からインターンに参加している早稲田大学基幹理工学研究科 M1 竹下です。
SRE関連の技術と,自身が研究しているセキュリティ分野との関係性を学びたいと思い、インターンに参加しました。

中林

2023年8月21日からインターンに参加している秋田県立大学大学院 システム科学技術研究科 博士前期1年 中林です。
Kubernetes とコンテナセキュリティの部分に興味があり、インターンに参加しました。

引野

2023年8月28日からインターンに参加している新潟大学大学院 医歯学総合研究科 医科学専攻1年 引野です。
データ分析の環境構築をきっかけにコンテナ技術に興味を持ち、インターンに参加しました。

1. はじめに

はじめまして、スリーシェイクの Sreake 事業部インターン生の竹下、中林、引野です。

Sreake 事業部は SRE 技術に強みを持つエンジニアによるコンサルテーションサービスを提供する事業部であり、SRE 技術の調査、研究を行う目的で 2023年 8月 21日から 2週間の短期インターンに参加しました。本記事では、Kubernetes で秘密情報を扱うためのいくつかのツールについて調査を行い、比較を行ったことをまとめます。

2. kubernetes における秘密情報について

秘密情報とは、パスワードやトークン、キーなどの少量の機密データを含むオブジェクトのことです。Kubernetes における秘密情報を管理する手法は大まかに分けて3種類あります。本記事では、Kubernetes における秘密情報を管理する手法として3つのツールを検証し、それらの仕組みを説明した上で、検証結果とメリット・デメリットについてまとめ、比較しました。

Ⅰ. ユーザーが秘密情報を直接エンコード/デコードする
⇒本記事ではⅠの手法の一つとして Secrets Operations (以下、SOPS )を検証しました。

Ⅱ. 秘密情報をファイルとして直接マウントする
⇒本記事ではⅢの手法の一つとして Secrets Store CSI Driver (以下、SSCD)を検証しました。

Ⅲ. 外部の秘密情報と Kubernetes Secret を紐付ける
⇒本記事ではⅡの手法の一つとして External Secrets Operator(以下、ESO)を検証しました。

3. バージョン情報

動作検証に使用したツールは以下のとおりです。

  • kubectl:v1.27.4
  • helm:v3.9.3
  • Kubernetes:v1.27.3-gke.100
  • getsops/sops:Commit ID 35039c363ab5adb5cff1ef99ce8305eec5b5b7d8
  • Secrets Store CSI Driver:v1.3.4
  • GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp:v1.3.0
  • external-secrets/external-secrets:v0.9.3
  • Google Cloud Secret Manager
  • Google Cloud KMS

4-1. SOPS の仕組み

SOPS とは

SOPS とは、Mozilla(Firefox などの開発元)が公開している暗号化ツールで、エンベロープ暗号化を利用してファイルを暗号化することができます。また、暗号化したファイルを復号することも可能で、SOPS を用いると、秘密情報を含むファイルをその他の情報を含むファイルと一元管理することができます。
エンベロープ暗号化とは、平文データをデータキーで暗号化し、そのデータキーをさらに別のキーで暗号化する方法です。SOPS ではデータキーを利用する KMS のマスターキーで暗号化します。また、暗号化したデータキーは SOPS によって暗号化したファイルに保存されます。

SOPS の新規ファイル作成手順

①SOPS がランダムなデータキーを生成
②KMS のマスターキーでデータキーを暗号化
③暗号化されたデータキーを安全にファイルに保存

SOPS の秘密情報の暗号化手順

①ファイルから暗号化されたデータキーを抽出
②KMS を使ってデータキーを復号
③暗号化する key-value ごとに Initialization Vector(IV) を生成
④IVとデータキーを用いて value を暗号化
⑤IVと暗号化された value をファイルに保存
⑥ファイルの整合性の確認のため、すべての key-value をベースに計算された MAC を暗号化してファイルに保存

SOPSの秘密情報の複号手順

①ファイルから暗号化されたデータキーを抽出
②KMS を使ってデータキーを復号
③ファイルに入っている全ての key-value をベースに MAC を計算し暗号化して、ファイルに保存されている MAC と比較することで整合性を検証
④ファイルから IV と暗号化された value を抽出
⑤IV とデータキーを用いて value を復号

SOPSでは、複数のマスターキーを用いることが可能で、一つのマスターキーにアクセスできなくなってしまっても、別のマスターキーにアクセスできれば、復号が可能になります。
この機能は次のようなケースで利便性が向上します。
Ⅰ. チームごとにマスターキーを生成し、複数チームにファイルを共有できます。
Ⅱ. 複数のマスターキーを用意することで、バックアップとして使えます。

4-2. SOPS を Google Cloud で検証するまでの手順

①SOPS のインストール

セルフビルドを行う際は、Goのインストールが必要です。

$ mkdir -p $GOPATH/src/github.com/getsops/sops/
$ git clone <https://github.com/getsops/sops.git> $GOPATH/src/github.com/getsops/sops/
$ cd $GOPATH/src/github.com/getsops/sops/
$ make install

②Google Cloud KMS を利用するために、アプリケーションの認証情報を使用して認証

$ gcloud auth login
$ gcloud auth application-default login

③KMS リソースID を gcloud を使用して作成

$ gcloud kms keyrings create sops --location global
$ gcloud kms keys create sops-key --location global --keyring sops --purpose encryption
$ gcloud kms keys list --location global --keyring sops

④ファイルの暗号化

$ sops --encrypt --gcp-kms projects/sreake-intern/locations/global/keyRings/sops/cryptoKeys/sops-key test.yaml > test.enc.yaml

test.yaml は暗号化されるファイルなので、事前に作成しておきます。
また、key: value で要素を追加します。

今回は、user として masamu、pass として three-shake を追加しました。

sample

暗号化した結果

⑤ファイルの複号化

$ sops --decrypt test.enc.yaml

4-3. SOPS のメリット・デメリット

メリット

  • Kubernetes クラスタ上で特に設定をする必要がなくなる
  • KMS が鍵を管理するため、鍵を手に入れるために認証が必要になり、不正なユーザに盗まれるリスクが低くなる
  • Integrity Check が可能で改ざんされていないことが保証される。
  • 秘密情報そのものを Git で管理できるため、機能追加など他の部分の変更に合わせた秘密情報の変更が追いやすくなる
  • SOPS の構築が不要であるため、マニフェストファイルを書く必要がある
  • key-value の暗号化では、value の部分だけを暗号化する手法なので、暗号化されたファイルを見たとき、秘密情報が洩れることなく、レビューがしやすくなる

デメリット

  • 暗号化された yaml ファイルをそのまま Git 管理することへの抵抗心を持つ可能性がある
  • マスターキーが漏れると全ての秘密情報が復号されてしまう
  • ファイルが漏れたときに yaml ファイルの value はわからないが、構造はわかるため内容推測の材料になる
  • 秘密情報を利用する際、復号した状態をローカルに保存するので、平文状態が残るリスクがある

4-4. SOPSと他ツールの連携

SOPS は CLI ツールであるため、ネイティブな Kubernetes 環境で積極的に使うことはあまり有用ではなく、追加のツールを必要とする場合がほとんどです。

そのため、Helm で SOPS を利用する Helm-Secrets について紹介します。

①Helm-Secrets

Helm-Secret は暗号化された Helm のファイルをその場で復号するための Helm のプラグインです。

  • 暗号化には SOPS を利用します
  • シークレットを AWS SecretManager、Azure KeyVault、HashiCorp Vault などのクラウドネイティブシークレットマネージャーに格納します。
  • デプロイツールや Argo CD のような GitOps オペレーターで使用できます。
  • マニフェストファイルの管理には Helm を利用することが多いため、SOPS を Helm から使えるようにすることは利便性の向上に繋がります。

4-5. Helm-Secrets の利用手順

Helm-Secrets を利用するには、アプリケーションを Kubernetes クラスタにデプロイするのに必要なリソースを含んだパッケージである Helm Chart の他に平分で秘密情報を記述している plainSecrets.yaml と暗号化・復号に関する鍵の情報を記載する .sops.yaml ファイルを用意する必要があります。

plainSecrets.yaml の sample

# パスワードを "testpass" として
# echo "testpass" | base64 | xargs -I{} echo -e "test:\\n  pass: {}\\n" > plainSecrets.yaml # base64 エンコードしたファイルを作成する場合はこっち
echo "testpass" | xargs -I{} echo -e "test:\\n  pass: {}\\n" > plainSecrets.yaml
# 中身を確認
cat plainSecrets.yaml
=>
test:
  pass: testpass

.sops.yaml では暗号化・復号に関する鍵の情報を記載するが、SOPS 利用時に作成した KMS の鍵をそのまま利用することができます。

Helm-Secrets の利便性が向上する機能

ディレクトリ内のすべての復号されたファイルを再帰的に削除します。

$ helm secrets clean examples/sops/
removed examples/sops/secrets.yaml.dec

この機能を使うと、復号された平文状態のファイルが全て削除されるので、平文状態が残るリスクが低減されます。

secrets.yaml ファイルを復号し、エディターで開きます。そして、ファイルが変更された場合、エディターの終了後に再び暗号化されます。

$ helm secrets edit examples/sops/secrets.yaml

こちらについても、復号された状態がローカルに残らないので、平文状態が残るリスクが低減されます。

プロジェクトごと、グローバル、または任意のツリーレベルごとに異なる PGP または KMS キーを実行できる。これにより、同じ git リポジトリを使用して、異なる CI / CD インスタンス上のツリーを分離できます。

charts/
├── .sops.yaml
└── projectX
    ├── .sops.yaml
    ├── stages
    │   ├── dev
    │   │   ├── secrets.yaml
    │   │   └── env.yaml
    │   └── test
    │       ├── secrets.yaml
    │       └── env.yaml
    ├── secrets.yaml
    └── values.yaml

4-6. Helm-Secrets のメリット・デメリット

メリット

  • ファイルの復号をデプロイ時に自動で行うため、SOPS 単体で使うよりも秘密情報の扱いに関するヒューマンエラーが起きにくいです。
  • 利便性が SOPS 単体で利用するよりも高いです。
  • Helm のテンプレート機能を使えるため、秘密ファイルを別のファイルに保存するなど、秘密情報とそれ以外を切り分けて管理することができます。

デメリット

  • デメリットは基本的にはSOPSと同じですが、前述のとおり、ディレクトリ内のすべての復号されたファイルを再帰的に削除する機能や、エディターの終了時にファイルが暗号化される機能を用いれば、復号された状態がローカルに残らないので、平文状態が残るリスクが低減され、ヒューマンエラーのリスクも低減できると考えられます。

5-1. SSCD の仕組み

Secrets Store CSI Driver (SSCD) とは

Secrets Store CSI Driver は,、各ノード上で作動するkubeletとの通信を円滑に勧めてくれるデーモンセット(DaemonSets)のことです。
外部のシークレットプロバイダー(GCP Provider)と通信し、Secrets Store から秘密情報を Pod にマウントする機能も果たします。

SSCD は3つのコンテナで構成されており、以下で各コンテナについて簡単に説明します。

SSCD を構成する3つのコンテナ

node-driver-register

CSI Driver を kubelet に登録し、CSI 呼び出しを発行する UNIX ドメインソケットを認識します。

secrets-store

Pod の作成または削除時に、ボリュームのマウントまたはアンマウントを担当します。

liveness-probe

CSI Driver が正常に機能しているかを監視し、問題があれば Kubernetes に報告します。この機能により、Pod を再起動して問題の解決を試みることができます。

5-2. SSCD 構築手順

全体の流れ

  1. Workload Identity が有効な GKE クラスターを用意
  2. Secrets Store CSI Driver をインストール
  3. 外部のシークレットプロバイダーをインストール
  4. 秘密情報を作成
  5. 確認

1. Workload Identity が有効な GKE クラスターを用意

公式ドキュメントを参照してください。

2. Secrets Store CSI Driver をインストール

まず、https://github.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp.git をクローンします。

git clone https://github.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp.git

※ SSCD をインストールするために、Helm というツールを使います。

Helm のインストールはこちら

Helm のリポジトリを追加し、SSCDをインストール

helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver --namespace kube-system

インストールできたか確認

kubectl --namespace=kube-system get pods -l "app=secrets-store-csi-driver"

実行後、以下のような表示が出ればOK

NAME                                               READY   STATUS    RESTARTS   AGE
csi-secrets-store-secrets-store-csi-driver-flq5t   3/3     Running   0          31s
csi-secrets-store-secrets-store-csi-driver-fwbhf   3/3     Running   0          31s

参考:https://secrets-store-csi-driver.sigs.k8s.io/getting-started/installation

3. 外部のシークレットプロバイダーをインストール

今回は、GCP Provider を利用します。

helm upgrade --install secrets-store-csi-driver-provider-gcp charts/secrets-store-csi-driver-provider-gcp

インストールできたか確認

kubectl --namespace=kube-system get pods -l "app=secrets-store-csi-driver-provider-gcp"

実行後、以下のような表示が出ればOK

NAME                                   READY   STATUS    RESTARTS   AGE
csi-secrets-store-provider-gcp-ntwwc   1/1     Running   0          5m43s
csi-secrets-store-provider-gcp-qp2tc   1/1     Running   0          5m43s

4. Workload Identity サービスアカウントの用意

$ export PROJECT_ID=test-project # test-project には任意のプロジェクト名を入れてください
$ gcloud config set project test-project

# Workload Identity 用のサービスアカウントを作成
$ gcloud iam service-accounts create test-workload # test-workload には任意のアカウント名を入れてください

# default/mypod が新しいサービスアカウントとして機能するように設定
$ gcloud iam service-accounts add-iam-policy-binding \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:test-project.svc.id.goog[default/mypodserviceaccount]" \
    test-workload@test-project.iam.gserviceaccount.com

5. 秘密情報の作成

$ echo "foo" > secret.data
$ gcloud secrets create test-secret --replication-policy=automatic --data-file=secret.data
# test-secret には任意のシークレット名を入れてください

# secret.data ファイルを削除
$ rm secret.data

# 新しく作ったサービスアカウントにシークレットへのアクセス権を付与
$ gcloud secrets add-iam-policy-binding test-secret \
    --member=serviceAccount:test-workload@test-project.iam.gserviceaccount.com \
    --role=roles/secretmanager.secretAccessor

6. Podにマウントできているか確認

$ ./scripts/example.sh 
# https://github.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp.git
# 上記リポジトリ上のスクリプトを使用

$ kubectl exec -it mypod /bin/bash
root@mypod:/ cat /var/secrets/good1.txt
foo
root@mypod:/ cat /var/secrets/good2.txt
foo

※ example.sh について補足

example/script.sh では 2個のテンプレートファイル(examples/app-secrets.yaml.tmpl, examples/mypod.yaml.tmpl)の PROJECT_ID を PROJECT_ID 環境変数で置換し、デプロイするまでを自動化したシェルスクリプトです。

examples/app-secrets.yaml.tmplSecretProviderClass リソースの構成を記述したマニュフェストのテンプレートです。SecretProviderClass リソースはドライバーの構成やプロバイダー毎のパラメータ、秘密情報ファイルの PATH などを指定するカスタムリソースです。app-secrets.yaml.tmp 内では Google Cloud の Secret Manager にある秘密情報を good1.txtgood2.txt というファイルとしてマウントするという設定を記述しています。

examples/mypod.yaml.tmpl は Workload Identity 用の KSA と CSI ボリュームをマウントするように指定した Pod を構成するマニュフェストファイルです。

つまり、example/script.sh を実行すると秘密情報が good1.txtgood2.txt/var/secrets 以下にマウントした Pod が立ち上がります。
この内容は cat コマンドで該当ファイルの内容を出力すると確認できます。

5-3. SSCD のメリット・デメリット

メリット

  • CSI Driver でマウントされるファイルシステムが tmpfs であり、Pod を削除するとノード内の秘密情報も削除されるため、ノードに秘密情報が残ることがない
  • 秘密情報が直接ファイルシステムにマウントされるため、Pod内でファイルとして扱うことが出来る

デメリット

  • 秘密情報がアップデートされたときに、マウントしているPodで最新の秘密情報を取得するには、デプロイし直さないといけない
  • 秘密情報の自動更新はアルファ版の機能となっている

6-1. ESO の仕組み

ESO とは

External Secrets Operator(ESO) とは公式ドキュメントには以下のように記載されています。

External Secrets Operator is a Kubernetes operator that integrates external secret management systems like AWS Secrets Manager, HashiCorp Vault, Google Secrets Manager, Azure Key Vault, IBM Cloud Secrets Manager, CyberArk Conjur and many more. The operator reads information from external APIs and automatically injects the values into a Kubernetes Secret.

つまり、ESO は HachiCorp Vault などの外部秘密情報管理システム(ESMS)に保存した秘密情報を Kubernetes 側から参照できるようにした秘密情報管理ツールの一つです。

ESO のアーキテクチャは以下の図のようになります。ESO は Kubernetes 内に配置され、ESMS と Kubernetes の Secret オブジェクトの同期を行うために仲介を行います。秘密情報自体は ESMS に保存しておき、ESO が ESMS から秘密情報を取得し、 Secret オブジェクトに自動的に値をストアします。そのため、秘密情報の管理自体は ESMS 側に一任することになります。

ESO はこの機能を 3つのコンポーネントと 主に 4つの CRD で実現しています。

図の作成には以下のアイコンセットを利用しました。

Kubernetes の Secret リソース

各コンポーネント、カスタムリソースの説明の前に、Kubernetes の Secret リソースについて簡単に説明します。

Kubernetes には秘密情報を含むデータを保存するための Secret というリソースがあります。このリソースを使用することで、マニュフェストファイルに直接秘密情報を記載する必要がなくなり、秘密情報の漏洩のリスクが低減することが期待できます。

Secret リソースには秘密情報の漏洩のリスクを低減するための工夫があります。Secret は etcd に格納されており、ノード上の Pod から参照される場合にノード上に送信されるようになっています。このとき、 Secret はストレージ内には保存されず、 tmpfs としてノードの RAM 上に保存されます。そのため、Secret を参照している Pod がノード上から削除されるとノード上の Secret の内容も自動的に削除され、ノード上に必要のない Secret が残り続けることを防ぐことができます。

一方で Secret の内容はデフォルトでは Base64 エンコードされているのみで特に暗号化の処理を行っていないため、etcd に不正アクセスされた場合には情報漏洩となる可能性があるというデメリットも存在します。

ESO のコンポーネント

ESO は Core Controller、 Webhook、 Cert Controller という 3つのコンポーネントから構成されています。

特に Core Controller は ESO で最も重要なコンポーネントです。Core Controller では複数のカスタムコントローラが動いており、対応するカスタムリソースの内容をもとに秘密情報の取得、 Kubernetes の Secret オブジェクトへの値の自動挿入、更新などを行います。

Webhook は ExternalSecret、SecretStore リソースの検証などを行う Webhook です。検証が具体的にどのような処理を指しているかまでは分かりませんでした。

3つ目の Cert Contoroller は Webhook が TLS 通信を行うために必要な証明書の管理を行うコントローラです。

以上 3つのコンポーネントが Kubernetes の Pod として動作することで ESO の機能を実現しています。

ESO のカスタムリソース

ESO には主に 4つの CRD (SecretStore、 ClusterSecretStore、 ExternalSecret、 ClusterExternalSecret)があり、 ESO のユーザは CRD に基づいたカスタムリソースを作成し、組み合わせることで、秘密情報の取得が可能になります。

SecretStore/ClusterSecretStore

SecretStore/ClusterSecretStore リソースは ESO が ESMS から秘密情報を取得するために必要なプロジェクト名、鍵名などを指定します。このリソースには対応する ESMS のプロバイダーに合わせた記述が必要になります。

ESMS として Google Cloud Secret Manager を用いる場合は認証部分に Workload Identity を使用できるため、アクセストークンを別途用意し、マニュフェストファイルに記述するなどの手順がなく、比較的容易に設定できます。

SecretStore と ClusterSecretStore は基本的な役割は同じですが、スコープに違いがあります。SecretStore リソースは namespace スコープであり、同じ namespace からのみ参照できます。一方でClusterSecretStore リソースはクラスタスコープであり、全ての namespace から参照できます。

ExternalSecret

ExternalSecret リソースにはどのデータを取得し、どのようにデータを Secret オブジェクトにストアするかを記述します(spec.target)。また、どの SecretStore オブジェクトを使うかも指定する必要があります(spec.secretStoreRef)。

ExternalSecret リソースをデプロイすると Core Controller の中の ExternalSecret のコントローラが ExternalSecret オブジェクトの記載内容から ESMS にアクセスし秘密情報を取得した後 Secret オブジェクトに値をストアします。

また、ESMS の秘密情報の変更に対応するために、ExternalSecret リソースでは ESMS に更新を見に行く間隔(spec.refreshInterval)を設定することができ、コントローラはその内容を ESMS から fetch し、Secret の値を最新の内容に同期することが出来ます。

ClusterExternalSecret

ClusterExternalSecret リソースはクラスタスコープのリソースであり、特定の namespace に ExternalSecret を配置したい場合などに使用します。

ESO の秘密情報の取得手順

ESO のアーキテクチャ図にカスタムリソースを追加し、秘密情報の取得手順を図解したものを以下に示します。

図の ExternalSecret リソースと Secret の間及び ExternalSecret リソースと ClusterSecretStore リソースの間は、ExternalSecret リソースで秘密情報をストアする Secret リソースの名前や ESMS の情報が記述された ClusterSecretStore を指定する必要がありますが、実際の処理を行うのは ESO の Core Controller であるため、間接的な繋がりを表すために点線で結んでいます。

ESO は Core Controller の Reconciliation Loop の中で次の手順で秘密情報を同期します。

  1. ExternalSecret の内容を読み取る
  2. ExternalSecret で参照している SecretStore の情報を読み取る
  3. SecretStore の内容を元に ESMS との通信を確立し、秘密情報を取得する
  4. ExternalSecret でターゲットとして指定した Secret オブジェクトが存在しない場合は新規作成し、 その Secret オブジェクトに取得した秘密情報をストアする

さらに詳しい手順はコントローラの Reconciliation Loop の実装から辿ることが出来ます。

6-2. ESO の構築

今回は ClusterSecretStore、 ExternalSecret リソースを用いて先程の図のような ESO の構築を行い、 Google Cloud Secret Manager に保存された秘密情報を取得できることを確認します。構築には以下の 4つの手順を行う必要があります。

  1. Workload Identity の有効化(Google Cloud Secret Manager との認証で使用)
  2. Secret Manager で秘密情報を作成
  3. ESO のインストール
  4. ESO カスタムリソースの作成
  • ClusterSecretStore リソースを作成
  • ExternalSecret リソースを作成

パラメータ一覧

ESO の構築に用いるパラメータ名とその役割を以下のように定義します。各パラメータの値は環境によって異なる値を指定する必要がありますが、値の例も記載するため構築の手順での参考にしてください。

変数名値の例説明
PROJECT_IDexample-projectGoogle Cloud のプロジェクト ID
CLUSTER_NAMEexample-clusterGKE クラスタの名前
COMPUTE_REGIONasia-northeast1-aGKE クラスタのリージョン or ゾーン
NODEPOOL_NAMEdefault-poolノードプールの名前 デフォルトは default-pool
NAMESPACEexternal-secretsKSA を作成する namespace ,ESO をインストールする namespace
GSA_ACCOUNT_NAMEexample-gsaGoogle Cloud Service Account の名前
KSA_ACCOUNT_NAMEexample-ksaKubernetes Service Account の名前
KEY_NAMEexample-esoGoogle Cloud Secret Manager の鍵名
SECRET_NAMEtest-secret-info秘密情報をストアする Secret リソースの名前

1. Workload Identity の有効化

ESMS に Google Cloud の Secret Manager、 Kubernetes クラスタとして Google Cloud の GKE クラスタを利用している場合、Pod に対して Google Cloud のサービスを利用する権限を付与することが出来る Workload Identity という機能を利用できます。Workload Identity は Kubernetes Service Account (KSA) と Google Cloud の Service Account (GSA) を紐付けることで、KSA から GSA の権限を借用し、 Google Cloud のサービスを利用できるという仕組みになっています。

Workload Identity を有効化するとアクセストークンなどの資格情報を明示的に指定する必要がなくなるため高い利便性があり、アクセストークンが漏れることもなくなるためセキュリティ面でもより強固になることも期待できます。今回構築する ESO では Google Cloud Secret Manager から秘密情報を取得することが目的のため、Workload Identity を有効化します。

有効化の手順はこの記事では詳しく解説しませんが、Google Cloud 公式ドキュメントの「 Workload Identity を使用する」の手順を参考にしました。注意点として、上記の手順で作成する KSA の namespace は ESO をインストールする namespace と同じである必要があります。

上記手順で Workload Identity が正しく設定されているかは、Google Cloud の metadata-server にリクエストを送ることで確認できます。まず、以下のマニュフェストファイルを作成し、デプロイすることで Kubernetes 上に Pod を作成します。(kubectl apply -f wi-test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: workload-identity-test
  namespace: NAMESPACE
spec:
  containers:
  - image: google/cloud-sdk:slim
    name: workload-identity-test
    command: ["sleep","infinity"]
  serviceAccountName: KSA_ACCOUNT_NAME
  nodeSelector:
    iam.gke.io/gke-metadata-server-enabled: "true"

次に、作成した Pod 上に入ります。

# 以下でGSAのアカウントが帰ってくれば成功
$ kubectl exec -it workload-identity-test \\
  --namespace NAMESPACE \\
  -- /bin/bash

Pod 上のシェルで以下の curl コマンドを実行します。

この時指定している 169.254.169.254 は Google Cloud の Compute Engine がアクセスできる metadata-server が配置されているリンクローカルアドレスです。Compute Engine の VM が metadata-server にリクエストを行った場合は Google Cloud のサービスを使用するためのアクセストークンや GSA のアドレスなどを取得する事ができます。

一方で、Workload Identity を有効にした場合は kube-system の namespace に gke-metadata-server という Pod が作成され、iptables のルールが書き換えられることによって metadata-server への リクエストが gke-metadata-server にフォワーディングされるようになります。gke-metadata-server は KSA の annotations の情報を元に KSA と GSA の紐づけを処理し、 GSA の権限で Google Cloud の機能を使うためのアクセストークンなどを取得することができます。

以下で実行しているコマンドのアクセス先として指定しているエンドポイントは GSA のメールアドレスを返すエンドポイントであり、Workload Identity の設定が正しくなされている場合には KSA と紐付けられている GSA のメールアドレスがレスポンスとして返ってくることを確認できます。

$ curl -H "Metadata-Flavor: Google" <http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email>
=>
KSA_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com

2. Secret Manager で秘密情報を作成

Secret Manager で秘密情報を作成します。秘密情報の形式は文字列で記載することも出来ますが、辞書形式で記述することで、アプリケーションから使いやすくなるため、今回の検証では以下のようなユーザ名やパスワードなどを辞書形式で記述したものを使用します。

// 今回使用した秘密情報の例
{
	"user":"default-user",
	"pass":"super-weak-passphrase"
}

以下のコマンドで Secret Manager に秘密情報を作成することが出来ます。グラフィカルに行いたい場合は、Web の Secret Manager の管理画面から作成することも出来ます。

$ cat << EOF > secret.data
{
  "user":"default-user",
  "pass":"super-weak-passphrase"
}
EOF

$ gcloud secrets create KEY_NAME --replication-policy=automatic \\
  --data-file=secret.data

いずれかの方法により秘密情報作成後、 Web の Secret Manager の管理画面から設定した値を閲覧することで適切に設定できていることを確認します。

先ほど作成した GSA から Secret Manager の秘密情報を参照できるように権限の設定を行います。作成した GSA に対して以下のコマンドで roles/secretmanager.secretAccessor ロールを付与します。これにより、GSA から作成した秘密情報の閲覧ができるようになります。

$ gcloud secrets add-iam-policy-binding KEY_NAME \\
  --member="serviceAccount:GSA_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com" \\
  --role="roles/secretmanager.secretAccessor"

3. ESO のインストール

ここからは Secret Manager から秘密情報を取得する ESO を構築します。

まず、ESO のインストールを行います。ESO のインストール方法は公式ドキュメントに様々な方法が提示されています.今回は Helm に Chart レポジトリを追加する方法でインストールします。

ESO をインストールする namespace は任意に指定できますが、公式ドキュメントに沿って external-secrets や es とすると後の設定が簡単になります。ここでは namespace を NAMESPACE=external-secret としています。

# Helm にチャートレポジトリの情報を追加する
$ helm repo add external-secrets <https://charts.external-secrets.io>

# installサブコマンドでKubernetes上にオブジェクトを作成する
$ helm install external-secrets \\
   external-secrets/external-secrets \\
    -n NAMESPACE \\
    --create-namespace \\

ESO がインストールされると kubectl get pods -Aなどのコマンドを実行し、NAMESPACE という名前の namespace (ここでは external-secrets )に Pod が作成されていることが確認できます。

それぞれ,external-secrets-[1-9a-z]{10}-[1-9a-z]{5} が Core Controller の Pod、 external-secrets-cert-controller-[1-9a-z]{10}-[1-9a-z]{5} が Cert Controller、 external-secrets-webhook-[1-9a-z]{10}-[1-9a-z]{5} が Webhook のコンポーネントです。

$ kubectl get pods -A
=>
NAMESPACE          NAME                                                       READY   STATUS    RESTARTS        AGE
demo               nginx-77b4fdf86c-l4k9j                                     1/1     Running   0               38m
external-secrets   external-secrets-57ddc5b469-fqwcx                          1/1     Running   0               23s
external-secrets   external-secrets-cert-controller-6dc74f5db7-rb4md          0/1     Running   0               23s
external-secrets   external-secrets-webhook-57ffc57cb8-lfb9c                  0/1     Running   0               23s
gmp-system         alertmanager-0                                             2/2     Running   0               4h19m
gmp-system         collector-4vvxm                                            2/2     Running   0               4h17m

4. ESO のカスタムリソース作成

ESO の構築には最低限 2つのカスタムリソースを作成する必要があります。

  • SecretStore or ClusterSecretStore
  • ExternalSecret

カスタムリソースの内容を適切に書き換えることでプラットフォームに合わせた ESO を構築することも出来ます。

ClusterSecretStore リソースの作成

ClusterSecretStore は ESMS へのアクセス方法を指定するカスタムリソースであり、この情報を元に ESO が ESMS から秘密情報を取得します。SecretStore リソースではなく、ClusterSecretStore リソースを使用するのは、 namespace を考慮する必要がなくなり、構築が容易なためです。

ClusterSecretStore のマニュフェストファイルの例を以下に示します。今回秘密情報を保管してある Google Cloud の Secret Manager にアクセスするためにはこれまで設定してきた内容をもとにして以下のパラメータを適切な値に変更する必要があります。

  • spec.provider.gcpsm.projectID: GSA が存在する Google Cloud のプロジェクト ID
  • spec.provider.gcpsm.auth.clusterLocation:GKE のリージョン or ゾーン
  • spec.provider.gcpsm.auth.clusterName:GKE クラスタの名前
  • spec.provider.gcpsm.auth.clusterProjectID:GKE クラスタが所属している Google Cloud のプロジェクト ID
  • spec.provider.gcpsm.auth.serviceAccountRef.name:GSA と紐づけた KSA の名前
  • spec.provider.gcpsm.auth.serviceAccountRef.namespace:KSA の namespace

Google Cloud Secret Manager 以外の ESMS を使用する場合は spec.provider 以下の設定を ESO 公式ドキュメントの Provider の項目を参考に ESMS に合わせて変更する必要があります。

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: gcp-store
spec:
  provider:
    gcpsm:
      projectID: PROJECT_ID
      auth:
        workloadIdentity:
          clusterLocation: COMPUTE_REGION
          clusterName: CLUSTER_NAME
          clusterProjectID: PROJECT_ID
          serviceAccountRef:
            name: KSA_ACCOUNT_NAME
            namespace: NAMESPACE

作成したマニュフェストファイルはkubectl apply -f ClusterSecretStore.yaml でデプロイします。

ExternalSecret リソースの作成

ExternalSecret は ClusterSecretStore で指定した ESMS からどのようなデータを取得し、どのように Secret オブジェクトとして保存するかを定義するカスタムリソースです。ExternalSecret のスコープをクラスタスコープにした ClusterExternalSecret カスタムリソースも存在します。

ここでは、ESMS から全ての秘密情報を取得し、Secret オブジェクトにストアするシンプルな ExternalSecret の設定例を示します。

  • spec.refreshInterval:ESMS への fetch 頻度、0にすると更新を行わない
  • spec.target.name:取得したデータをストアする Secret オブジェクトの名前、ExternalSecret と同じ namespace で作成されます
  • spec.target.creationPolicy:Secret オブジェクトの内容を操作するためのポリシー(OwnerOrphanMergeNone
  • spec.dataFrom.extract.key:Secret Manager の秘密情報の名前
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: example
  namespace: default
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: gcp-store
  target:
    name: SECRET_NAME
    creationPolicy: Owner
  dataFrom:
  - extract:
      key: KEY_NAME

作成したマニュフェストファイルはkubectl apply -f ExternalSecret.yaml でデプロイします。

Secret の値確認

最後にESO が取得した秘密情報が Secret オブジェクトに保存されていることを確認します。まず、コマンドラインで Secret オブジェクトの値を確認し、次に Pod から Secret オブジェクトの値を参照できることを確認します。

コマンドラインから秘密情報を参照する

カスタムリソースが正常にデプロイされている場合、 ExternalSecret リソースで宣言した target.name で Secret オブジェクトが作成されており、以下のコマンドで確認できます。(以下の出力はKEY_NAME=test-secret-info の場合)

$ kubectl get secret -n default
=>
NAME               TYPE     DATA   AGE
test-secret-info   Opaque   2      20h

Secret オブジェクトには Google Cloud Secret Manager から同期された秘密情報がストアされているため、これを取り出して確認します。

Secret オブジェクトの情報は Base64 エンコードされて保存されているため、秘密情報を平文として取り出すには Secret オブジェクトの秘密情報を取り出した後に Base64 デコードをする必要があります。Linux には Base64 エンコード/デコードを行う base64 コマンドがあるため、これを使用します。

同期が適切に行われている場合は以下のコマンドを実行すると Secret Manager にて設定した値が取得できていることが確認できます。

# Secret 取得の例
$ kubectl get secret -n default SECRET_NAME -o jsonpath='{.data.pass}' | base64 -d | xargs -I{} echo "pass:{}"
=>
pass:super-weak-passphrase

Podから秘密情報を参照する

ESO において ESMS の秘密情報は Secret オブジェクトに保存されるため、 Secret の内容を Pod に環境変数として読み込ませることなどが出来ます。これにより、秘密情報を必要とするサービスをデプロイする際に、 Pod が作成される瞬間に Secret オブジェクトから自動で秘密情報を挿入することが可能になります。

ここを参考に以下のような spec.containers.env に Secret オブジェクトから秘密情報を取得するように宣言した Pod を構成するマニュフェストファイルを作成し、デプロイします。

apiVersion: v1
kind: Pod
metadata:
  name: secret-keyref-pod
spec:
  containers:
  - name: password-container
    image: k8s.gcr.io/busybox
		command: [ "/bin/sh", "-c", "echo user:$USER password:$PASSWORD" ]
    env:
      - name: USER
        valueFrom:
          secretKeyRef:
            name: SECRET_NAME
            key: user
      - name: PASSWORD
        valueFrom:
          secretKeyRef:
            name: SECRET_NAME
            key: pass
  restartPolicy: Never

この Pod 自体は USER, PASSWORD 環境変数を標準出力に表示して終了するだけの Pod です。この Pod のログは以下のコマンドを実行することで確認することが出来ます。ログを見ると Secret オブジェクトから秘密情報を参照できていることが確認できます。

$ kubectl logs -n default secret-keyref-pod
=>
user:default-user password:super-weak-passphrase

6-3. ESO のメリット・デメリット

メリット

  • Secret Manager が管理を行うので自分で秘密情報を暗号化復号をする必要がない
  • 秘密情報とマニュフェストファイルの管理が別になるため、秘密情報漏洩のリスクが軽減する
  • 構成に柔軟性があり、同じクラスタ内での細かいアクセスコントロールがし易い
  • Secret はノードのディスクには保存せず tmpfs に保存するため、ノード内に Secret を使用する Pod がない場合にノードから Secret が消される

デメリット

  • 秘密情報とマニュフェストファイルの管理が別になるため、管理が複雑になる
  • SOPS などのファイル暗号化ツールに比べて導入の難易度が高い
  • 同期の間隔次第では最新の秘密情報にアクセスできない時間が存在する

7. 3つのツールの比較

3つのツールについて 6つの指標から比較を行った表を示します。評価は動作検証と仕組みに関する調査からの主観的なものとなっていますのでご了承ください。

8. まとめとインターンの感想

本インターンを通して、SOPS、SSCD、ESO の3パターンの方法で秘密情報の管理について学び、それぞれの方法の比較を行いました。

実際の業務に携わった経験がないため、メリット・デメリットを考えることは非常に困難でしたが、様々なシーンに応じて使い分けるイメージができてよかったです。

各方法にメリット・デメリットがみられたため、唯一解が出ないことがもどかしくもあり、面白さもあったと思います。

竹下

コンテナや、Kubernetes に関して初めてのことばかりで、メンターの方々に助けをいただきながら調査・検証を進めていきました。

今回は、自分の研究分野と関わりのあるセキュリティの部分での調査・検証を行ったので、非常に興味深い内容がたくさんあり楽しいインターンでした。

実際に Google Cloud 上でクラスターを作り、シェル上で公式のドキュメントを見ながら試行錯誤して検証するのがとても新鮮で、実際に検証が成功したときは嬉しい気持ちになりました。このような貴重な体験を、これからの自分の研究や仕事で活かしていけたらなと思います。

最後に、株式会社スリーシェイクの皆様やメンターの方々には、ツールを調査・検証する上で、講義やミーティングなどでの情報共有、指導などを行って頂きました。深く感謝申し上げます。また、一緒に調査・検証を行ってくれたチーム02の皆様、同じ期間でとても興味深い発表をして下さったチーム01の皆様と楽しくインターンの時間を共有することができてよかったです。ありがとうございました。

中林

Kubernetes という技術に触れることが初めてで手探りな状態でスタートしました。そのため、2週間という期間はとても短いと感じましたが、限られた期間の中で全力で技術研究に取り組むという濃密な体験をすることができ、その中で様々な学びを得ることが出来ました。

特に技術研究のテーマである秘密情報の管理は Kubernetes に関わらず難しい問題ですが、Kubernetes の場合は様々な選択肢があり、権限の柔軟性も素晴らしいと感じました。また、調査した 3つのツールは宣言的にリソースを記述し、コントローラが自動で管理する Kubernetes の強みを生かし、シームレスに Kubernetes エコシステムに組み込む事ができるため、調査していてよく出来ていると感心することが多々あり、興味の尽きない 2週間となりました。

また、 Kubernetes の知識が以外にもインターン生との繋がりやメンターの方々からのインプット、アウトプットに関するアドバイスなど様々なものを得ることが出来ました。貴重な経験を大切にし自分の糧としていきたい思います。

最後に株式会社スリーシェイクの皆様には、技術研究をするにあたり必要なツールの提供と適切なご指導を頂きました。感謝申し上げます。

引野

1週間遅れで本インターンに参加となり、ハイペースで知識をインプットせざるを得ない状況になりましたが、このような状況のおかげで Docker と Kubernetes の用語や概念に短時間で慣れることができました。

初日は他のメンバーが作ってくれた資料に沿ってコマンドを打ったものの、予備知識がほとんどないこともあって何が何だかよくわかりませんでした。しかしながら、2日目以降のインプットする段階の時に、「あのコマンドはこういうことをしていたのか!」という発見があり、スムーズにインプットすることができたと思います。

私のメインの研究分野は医療ビッグデータを用いたデータ解析であるため、一見今回のインターンと何の関係もないように見えますが、解析結果の再現性を担保したり、解析環境の構築を容易にしたりするといった観点からIaCの考え方、コンテナの利用など非常に参考になりました。

右も左もわからない状態の自分を手厚くサポートしてくれたメンターの方々および竹下さん、中林さんには感謝してもしつくせません。本当にありがとうございました。

9. 参考文献

ブログ一覧へ戻る

お気軽にお問い合わせください

SREの設計・技術支援から、
SRE運用内で使用する
ツールの導入など、
SRE全般についてご支援しています。

資料請求・お問い合わせ