アプリケーション レイヤでのシークレットの暗号化

このページでは、Cloud Key Management Service(Cloud KMS)で管理する鍵を使用して、アプリケーション レイヤで Kubernetes の Secret を暗号化する方法について説明します。この機能は Cloud KMS の機能に依存しているため、暗号鍵のローテーションエンベロープ暗号化について理解しておく必要があります。

概要

GKE はデフォルトで、シークレットなどの保存されている顧客コンテンツを暗号化します。GKE では、こうした暗号化の処理と管理が自動的に行われるため、ユーザー側での操作は不要です。

アプリケーション レイヤで Secret を暗号化すると、機密データ(etcd に格納されている Secret など)に対するセキュリティをさらに強化できます。この機能により、Cloud KMS で管理している鍵を使用して、アプリケーション レイヤでデータを暗号化できます。そうすることで、攻撃者が etcd のオフライン コピーにアクセスした場合でもデータの内容を保護できます。

アプリケーション レイヤでのシークレットの暗号化を使用するには、まず Cloud KMS 鍵を作成し、GKE サービス アカウントにその鍵へのアクセス権を付与する必要があります。レイテンシを減らすため、またリソースが複数の障害発生ドメイン間のサービスに依存することを防ぐために、鍵はクラスタと同じロケーションに存在する必要があります。このようにしてから、使用する鍵を指定して新しいクラスタまたは既存のクラスタでこの機能を有効にできます。

エンベロープ暗号化

Kubernetes の KMS プロバイダは、Secret のエンベロープ暗号化機能を提供します。具体的には、一般的にデータ暗号鍵(DEK)と呼ばれるローカル鍵が Secret の暗号化に使用されます。DEK 自体は、鍵暗号鍵(KEK)と呼ばれる別の鍵で暗号化されています。KEK は Kubernetes に保存されません。

エンベロープ暗号化には、主に 2 つの利点があります。

  • KEK は、すべての Secret を再暗号化することなくローテーションできます。つまり、パフォーマンスに大きな影響を与えることなく、通常の鍵ローテーションのベスト プラクティスに簡単に従うことができます。

  • Kubernetes に保存されている Secret は、外部の信頼のルートを利用できます。つまり、ハードウェア セキュリティ モジュールなどの一元的な信頼のルートによって Secret を保護できるため、攻撃者がオフラインでコンテナにアクセスした場合でも Secret が取得されることはありません。

GKE のアプリケーション レイヤでの Secret の暗号化により、Secret は AES-CBC プロバイダを使用して DEK でローカルに暗号化され、DEK は Cloud KMS で管理する鍵暗号鍵で暗号化されます。

エンベロープ暗号化の詳細については、エンベロープ暗号化をご覧ください。

シークレットを作成した場合

新しいシークレットを作成すると、次のようになります。

  1. Kubernetes API サーバーは乱数生成ツールを使用して、Secret のための一意の DEK を生成します。

  2. Kubernetes API サーバーは、DEK をローカルで使用して、Secret を暗号化します。

  3. KMS プラグインは暗号化のために DEK を Cloud KMS に送信します。KMS プラグインはプロジェクトの GKE サービス アカウントを使用して、Cloud KMS への認証を行います。

  4. Cloud KMS は KEK を使用して暗号化した DEK を KMS プラグインに返送します。

  5. Kubernetes API サーバーは暗号化されたシークレットと暗号化された DEK を保存します。平文の DEK はディスクに保存されません。

  6. Kubernetes API サーバーは、暗号化された DEK を平文の DEK にマッピングするキャッシュ エントリを作成します。これにより、Cloud KMS を使用せずに Secret を復号できます。

クライアントが Kubernetes API サーバーから Secret をリクエストすると、次の処理が実行されます。

  1. Kubernetes API サーバーは暗号化された Secret と暗号化された DEK を取得します。

  2. Kubernetes API サーバーは、既存のマッピング エントリのキャッシュをチェックし、Cloud KMS を使用せずに Secret を復号します。

  3. キャッシュ エントリが見つからない場合、KMS プラグインは KEK を使用して復号するために DEK を Cloud KMS に送信します。復号された DEK を使用して Secret を復号します。

  4. Kubernetes API サーバーは、復号された Secret をクライアントに返します。

鍵を破棄した場合

GKE で Secret の暗号化に使用された Cloud KMS で KEK を破棄すると、新しい KEK を最初に使用するようにクラスタを更新しない限り、Secret は使用できなくなります。

鍵のローテーションの後に古い KEK バージョンを破棄する予定である場合は、新しい KEK バージョンを使用してまず Secret を再暗号化してください。

サービス アカウントのトークン ボリューム プロジェクションを使用しない場合は、GKE のワークロードで使用されるサービス アカウントも Secret を使用するため、鍵が破壊されるとこれらは使用できなくなります。これらのアカウントにアクセスできないため、ワークロードは失敗します。

次の点が異なります。

  • マウントされたボリュームまたは環境変数として Secret への既存のアクセス権を保有する Pod は、アクセス権を保持します。

  • Kubernetes API サーバーは、キャッシュに保存された DEK マッピング エントリを使用して KEK を破棄した後もシークレットを復号できます。これにより、次の場合を除き、Pod を再起動または再スケジュール設定すると Secret にアクセスできます。

    • クラスタ コントロール プレーンが再起動されます。
    • Kubernetes API サーバー Pod が再起動されます。
    • Secret の DEK マッピング エントリは、Kubernetes API サーバーのキャッシュ内には存在しません。

KEK を破棄する前に、クラスタで使用されているかどうかを確認してください。Cloud KMS での鍵の破棄に関するアラート ポリシーを作成することもできます。

始める前に

  • このトピックの演習を行うには、2 つの Google Cloud プロジェクトが必要です。

    • 鍵プロジェクト: KEK を作成するプロジェクトです。

    • クラスタ プロジェクト: アプリケーション レイヤでのシークレットの暗号化を有効にするクラスタの作成先。

  • 鍵プロジェクトで、Cloud KMS API が有効になっていることを確認します。

    Cloud KMS API の有効化

  • 鍵プロジェクトで、キーリングと鍵を作成するユーザーには次の IAM 権限が必要になります。

    • cloudkms.keyRings.getIamPolicy
    • cloudkms.keyRings.setIamPolicy

    これらの権限(および他の権限)は、事前定義された roles/cloudkms.admin Identity and Access Management ロールに付与されています。鍵を管理するための権限の付与に関する詳細については、Cloud KMS のドキュメントをご覧ください。

  • クラスタ プロジェクトで、Google Kubernetes Engine API が有効になっていることを確認します。

    Google Kubernetes Engine API の有効化

  • Cloud SDK がインストール済みであることを確認します。

  • gcloud を最新バージョンに更新します。

    gcloud components update

Cloud KMS 鍵を作成する

キーリングを作成するときは、GKE クラスタのロケーションと一致するロケーションを指定します。

  • ゾーンクラスタは、スーパーセット ロケーションからのキーリングを使用する必要があります。たとえば、ゾーン us-central1-a のクラスタは、リージョン us-central1 の鍵のみを使用できます。

  • リージョン クラスタは、同じロケーションからのキーリングを使用する必要があります。たとえば、リージョン asia-northeast1 のクラスタは、リージョン asia-northeast1 のキーリングで保護する必要があります。

  • GKE では、Cloud KMS の global リージョンの使用はサポートされていません。

gcloud ツールまたは Google Cloud Console を使用できます。

Console

鍵プロジェクトでキーリングを作成します。

  1. Cloud Console で [暗号鍵] ページに移動します。

    [暗号鍵] ページに移動

  2. [キーリングを作成] をクリックします。

  3. [キーリング名] フィールドに、キーリングの名前を入力します。

  4. [ロケーション] プルダウンで、Kubernetes クラスタのロケーションを選択します。

  5. [作成] をクリックします。

次に、鍵を作成します。

  1. Cloud Console で [暗号鍵] ページに移動します。

    [暗号鍵] ページに移動

  2. 鍵を作成するキーリングの名前をクリックします。

  3. [鍵を作成] をクリックします。

  4. [キー名] フィールドに、鍵の名前を入力します。

  5. [ローテーション期間] と [開始日] に鍵のローテーション期間と開始時間を設定します。デフォルト値をそのまま使用することもできます。

  6. (省略可)鍵にラベルを追加する場合は、[ラベル] フィールドで [ラベルを追加] をクリックします。

  7. [作成] をクリックします。

gcloud

鍵プロジェクトでキーリングを作成します。

gcloud kms keyrings create RING_NAME \
    --location LOCATION \
    --project KEY_PROJECT_ID

以下を置き換えます。

  • RING_NAME: キーリングに選択した名前。
  • LOCATION: キーリングを作成するロケーション。
  • KEY_PROJECT_ID: 鍵プロジェクト ID。

鍵を作成します。

gcloud kms keys create KEY_NAME \
    --location LOCATION \
    --keyring RING_NAME \
    --purpose encryption \
    --project KEY_PROJECT_ID

以下を置き換えます。

  • KEY_NAME: 鍵に対して選択した名前。
  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • KEY_PROJECT_ID: 鍵プロジェクト ID。

鍵を使用する権限を付与する

クラスタ プロジェクトの GKE サービス アカウントの名前は次のとおりです。

service-CLUSTER_PROJECT_ID@container-engine-robot.iam.gserviceaccount.com

CLUSTER_PROJECT_ID をクラスタのプロジェクト ID に置き換えます。

サービス アカウントへのアクセスを許可するには、Google Cloud Console または gcloud コマンドを使用します。

Console

GKE サービス アカウントに Cloud KMS 暗号鍵の暗号化/復号の役割を付与します。

  1. Google Cloud Console で Cloud Key Management Service 鍵ブラウザを開きます。
    Cloud KMS 鍵のブラウザを開く
  2. 目的の鍵を含むキーリングの名前をクリックします。

  3. 使用する鍵のチェックボックスをオンにします。

    右側のウィンドウの [権限] タブが有効になります。

  4. [メンバーの追加] ダイアログで、アクセス権を付与する GKE サービス アカウントのメールアドレスを指定します。

  5. [役割を選択] プルダウンで、[クラウド KMS 暗号鍵の暗号化 / 復号] を選択します。

  6. [保存] をクリックします。

gcloud

GKE サービス アカウントに Cloud KMS 暗号鍵の暗号化/復号の役割を付与します。

gcloud kms keys add-iam-policy-binding KEY_NAME \
  --location LOCATION \
  --keyring RING_NAME \
  --member serviceAccount:SERVICE_ACCOUNT_NAME \
  --role roles/cloudkms.cryptoKeyEncrypterDecrypter \
  --project KEY_PROJECT_ID

以下を置き換えます。

  • KEY_NAME: 鍵の名前。
  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • SERVICE_ACCOUNT_NAME: GKE サービス アカウントの名前。
  • KEY_PROJECT_ID: 鍵プロジェクト ID。

アプリケーション レイヤでのシークレットの暗号化を有効にする

新しいクラスタの場合

新しいクラスタを作成するには、Google Cloud Console または gcloud ツールを使用します。

Console

  1. Cloud Console で Google Kubernetes Engine のメニューに移動します。

    Google Kubernetes Engine のメニューに移動

  2. [作成] をクリックします。

  3. 必要に応じてクラスタを構成します。

  4. ナビゲーション パネルの [クラスタ] の下の [セキュリティ] をクリックします。

  5. [アプリケーション レイヤでのシークレットの暗号化を有効にする] を選択して、Cloud KMS 鍵を作成するで作成したデータベース暗号鍵を選択します。

  6. [作成] をクリックします。

gcloud

アプリケーション レイヤでの Secret の暗号化をサポートするクラスタを作成するには、作成コマンドの --database-encryption-key パラメータに値を指定します。

gcloud container clusters create CLUSTER_NAME \
  --cluster-version=latest \
  --zone ZONE \
  --database-encryption-key projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
  --project CLUSTER_PROJECT_ID

以下を置き換えます。

  • CLUSTER_NAME: 新しいクラスタに付ける名前。
  • ZONE: クラスタを作成するコンピューティング ゾーン
  • KEY_PROJECT_ID: 鍵プロジェクト ID。
  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • KEY_NAME: 鍵の名前。
  • CLUSTER_PROJECT_ID: クラスタのプロジェクト ID

既存クラスタの場合

gcloud ツールまたは Google Cloud Console を使用して、アプリケーション レイヤでのシークレットの暗号化が使用できるように既存のクラスタを更新できます。

Console

アプリケーション レイヤでのシークレットの暗号化をサポートするようクラスタを更新するには:

  1. Cloud Console で Google Kubernetes Engine のメニューに移動します。

    Google Kubernetes Engine のメニューに移動

  2. 変更するクラスタの名前をクリックします。

  3. [セキュリティ] の [アプリケーション レイヤでのシークレットの暗号化] フィールドで、 [アプリケーション レイヤでのシークレットの暗号化を編集] をクリックします。

  4. [アプリケーション レイヤでのシークレットの暗号化を有効にする] のチェックボックスを選択して、Cloud KMS 鍵を作成するで作成したデータベース暗号鍵を選択します。

  5. [変更を保存] をクリックします。

gcloud

アプリケーション レイヤでのシークレットの暗号化を既存のクラスタで有効にするには、次のコマンドを実行します。

gcloud container clusters update CLUSTER_NAME \
  --zone ZONE \
  --database-encryption-key projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
  --project CLUSTER_PROJECT_ID

以下を置き換えます。

  • CLUSTER_NAME: クラスタの名前。
  • ZONE: クラスタのコンピューティング ゾーン
  • KEY_PROJECT_ID: 鍵プロジェクト ID。
  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • KEY_NAME: 鍵の名前。
  • CLUSTER_PROJECT_ID: クラスタのプロジェクト ID

Cloud KMS 鍵を更新する

Console

新しい Cloud KMS 鍵を使用するようにクラスタを更新するには:

  1. Cloud Console で Google Kubernetes Engine のメニューに移動します。

    Google Kubernetes Engine のメニューに移動

  2. 変更するクラスタの名前をクリックします。

  3. [セキュリティ] の [アプリケーション レイヤでのシークレットの暗号化] フィールドで、 [アプリケーション レイヤでのシークレットの暗号化を編集] をクリックします。

  4. 使用する新しい暗号鍵を選択します。

  5. [変更を保存] をクリックします。

gcloud

新しい Cloud KMS 鍵を使用するように既存のクラスタを更新するには:

gcloud container clusters update CLUSTER_NAME \
  --zone ZONE \
  --database-encryption-key projects/KEY_PROJECT_ID/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME \
  --project CLUSTER_PROJECT_ID

以下を置き換えます。

  • CLUSTER_NAME: クラスタの名前。
  • ZONE: クラスタのコンピューティング ゾーン
  • KEY_PROJECT_ID: 鍵プロジェクト ID。
  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • KEY_NAME: 鍵の名前。
  • CLUSTER_PROJECT_ID: クラスタのプロジェクト ID

アプリケーション レイヤでのシークレットの暗号化を無効にする

gcloud ツールまたは Google Cloud Console を使用できます。

Console

  1. Cloud Console で Google Kubernetes Engine のメニューに移動します。

    Google Kubernetes Engine のメニューに移動

  2. 変更するクラスタの名前をクリックします。

  3. [セキュリティ] の [アプリケーション レイヤでのシークレットの暗号化] フィールドで、 [アプリケーション レイヤでのシークレットの暗号化を編集] をクリックします。

  4. [アプリケーション レイヤでのシークレットの暗号化を有効にする] チェックボックスをオフにします。

  5. [変更を保存] をクリックします。

gcloud

アプリケーション レイヤでのシークレットの暗号化を無効にするには、次のコマンドを実行します。

gcloud container clusters update CLUSTER_NAME \
  --zone ZONE \
  --disable-database-encryption \
  --project CLUSTER_PROJECT_ID

以下を置き換えます。

アプリケーション レイヤでのシークレットの暗号化が有効になっていることを確認する

クラスタがアプリケーション レイヤでの Secret の暗号化を使用しているか調べるには、Google Cloud Console または gcloud コマンドを使用します。

Console

  1. Cloud Console で Google Kubernetes Engine のメニューに移動します。

    Google Kubernetes Engine のメニューに移動

  2. 変更するクラスタの名前をクリックします。

  3. [セキュリティ] で、[アプリケーション レイヤでのシークレットの暗号化] フィールドに「Enabled」と正しい鍵が表示されていることを確認します。

gcloud

クラスタがアプリケーション レイヤでのシークレットの暗号化を使用しているかどうかを確認します。

gcloud container clusters describe CLUSTER_NAME \
  --zone ZONE \
  --format 'value(databaseEncryption)' \
  --project CLUSTER_PROJECT_ID

以下を置き換えます。

クラスタがアプリケーション レイヤでの Secret の暗号化を使用している場合、レスポンスには次の EncryptionConfig が含まれます。

keyName=projects/project/locations/LOCATION/keyRings/RING_NAME/cryptoKeys/KEY_NAME;state=ENCRYPTED

制限事項

シークレットの数

GKE では、アプリケーション レイヤでのシークレットの暗号化のために、クラスタごとに最大 10,000 件のシークレットがサポートされます。10,000 件を超えるシークレットを保存すると、アップグレード時にクラスタが不安定になり、ワークロードの中断が発生する可能性があります。

鍵の場所

使用されているクラスタと同じリージョン内の鍵を選択する必要があります。たとえば us-central1-a 内のゾーンクラスタは、us-central1 リージョン内の鍵のみを使用できます。リージョン クラスタでは、レイテンシを減らすため、またリソースが複数の障害発生ドメイン間のサービスに依存することを防ぐため、鍵がクラスタと同じロケーションに存在する必要があります。

鍵のローテーション

鍵のローテーションを行う場合、既存のシークレットは以前の鍵暗号鍵(KEK)バージョンで暗号化された状態を保持します。新しい KEK バージョンが Secret をラップできるようにするには、鍵のローテーション後にシークレットを再暗号化します。

たとえば、Secret1 Secret を作成して保存します。この Secret が DEK1 で暗号化されて、さらに KEKv1 でラップされます。

KEK がローテーションした後で、Secret1 を再暗号化すると、DEK2 によってラップされます。さらに、KEKv2 でローテーションされる KEK がラップされます。

シークレットを再暗号化する

Secret の自動的な再暗号化を強制する手段はありません。必要な場合は、新しい鍵バージョンを作成して KEK を手動でローテーションできます。

gcloud kms keys versions create --location LOCATION \
   --keyring RING_NAME \
   --key KEY_NAME \
   --primary \
   --project KEY_PROJECT_ID

以下を置き換えます。

  • LOCATION: キーリングを作成した Cloud KMS のロケーション。
  • RING_NAME: キーリングの名前。
  • KEY_NAME: 鍵の名前。
  • KEY_PROJECT_ID: 鍵プロジェクト ID。

すべてのシークレットをタップして GKE を強制的に再暗号化します。

kubectl get secrets --all-namespaces -o json | kubectl annotate --overwrite -f - encryption-key-rotation-time="TIME"

TIME は、回転のタイミングを示す文字列(20200909-090909 など)に置き換えます。

EncryptionConfig

現時点では、Cloud KMS の鍵だけが GKE でサポートされています。それ以外の Kubernetes KMS プロバイダ暗号化プロバイダを使用することはできません。

次のステップ