データ暗号化にKMSプロバイダーを使用する

このページでは、機密データの暗号化を有効にするために、Key Management Service(KMS)プロバイダーとプラグインを設定する方法について説明します。 Kubernetes 1.33では、KMSによる保存時暗号化はv1とv2の2つのバージョンが利用できます。 KMS v1は(Kubernetes v1.28以降で)非推奨であり、(Kubernetes v1.29以降では)デフォルトで無効化されているため、特段の理由がない限りKMS v2を使用すべきです。 KMS v2は、KMS v1よりも大幅に優れたパフォーマンス特性を提供します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

必要なKubernetesのバージョンは、選択したKMS APIバージョンによって異なります。 KubernetesではKMS v2の使用を推奨しています。

  • バージョンv1.27より前のクラスターをサポートするためにKMS API v1を選択した場合、またはKMS v1のみをサポートするレガシーKMSプラグインを使用している場合、KMS v1をサポートする全てのKubernetesバージョンが動作します。 このAPIはKubernetes v1.28時点で非推奨です。 KubernetesではこのAPIの使用を推奨していません。

バージョンを確認するには次のコマンドを実行してください: kubectl version.

KMS v1

FEATURE STATE: Kubernetes v1.28 [deprecated]
  • Kubernetesバージョン1.10.0以降が必要です

  • バージョン1.29以降では、KMSのv1実装はデフォルトで無効になっています。 この機能を有効化するには、--feature-gates=KMSv1=trueを設定してKMS v1プロバイダーを構成してください。

  • クラスターはetcd v3以降を使用する必要があります

KMS v2

FEATURE STATE: Kubernetes v1.29 [stable]
  • クラスターはetcd v3以降を使用する必要があります

KMS暗号化とオブジェクトごとの暗号化キー

KMSプロバイダーは、etcd内のデータを暗号化するためにエンベロープ暗号化方式を使用します。 データはデータ暗号化鍵(DEK)を使用して暗号化されます。 DEKは、リモートKMSで保存・管理される鍵暗号化鍵(KEK)で暗号化されます。

(非推奨の)KMSのv1実装を使用する場合、各暗号化に対して新しいDEKが生成されます。

KMS v2では、暗号化ごとに新しいDEKが生成されます: APIサーバーは 鍵導出関数 を使用して、シークレットシード(暗号鍵生成の種)とランダムデータを組み合わせて単一用途のデータ暗号化鍵を生成します。 シードは、KEKがローテーションされるたびにローテーションされます(詳細については、以下の「key_idと鍵ローテーションの理解」セクションを参照してください)。

KMSプロバイダーは、UNIXドメインソケット経由で特定のKMSプラグインと通信するためにgRPCを使用します。 KMSプラグインは、gRPCサーバーとして実装され、Kubernetesコントロールプレーンと同じホストにデプロイされ、リモートKMSとのすべての通信を担当します。

KMSプロバイダーの設定

APIサーバーでKMSプロバイダーを設定するには、暗号化設定ファイルのproviders配列にkmsタイプのプロバイダーを含め、以下のプロパティを設定してください:

KMS v1

  • apiVersion: KMSプロバイダーのAPIバージョン。 この値を空のままにするか、v1に設定してください。
  • name: KMSプラグインの表示名。 一度設定すると変更できません。
  • endpoint: gRPCサーバー(KMSプラグイン)のリッスンアドレス。 このエンドポイントはUNIXドメインソケットです。
  • cachesize: 平文でキャッシュするデータ暗号化鍵(DEK)の数。 キャッシュする場合、DEKはKMSへの追加の呼び出しなしで使用できますが、キャッシュしない場合はKMSを呼び出して鍵を取り出す必要があります。
  • timeout: kube-apiserverがエラーを返す前にkms-pluginの応答を待つ時間(デフォルトは3秒)。

KMS v2

  • apiVersion: KMSプロバイダーのAPIバージョン。 これをv2に設定してください。
  • name: KMSプラグインの表示名。 一度設定すると変更できません。
  • endpoint: gRPCサーバー(KMSプラグイン)のリッスンアドレス。 このエンドポイントはUNIXドメインソケットです。
  • timeout: kube-apiserverがエラーを返す前にkms-pluginの応答を待つ時間(デフォルトは3秒)。

KMS v2はcachesizeプロパティをサポートしていません。 KMSを呼び出してサーバーが取り出した全てのデータ暗号化鍵(DEK)は、平文でキャッシュされます。 いったんキャッシュされたDEKは、KMSを呼び出すことなく無期限で復号に使用できます。

保存時暗号化設定の理解を参照してください。

KMSプラグインの実装

KMSプラグインを実装するには、新しいプラグインgRPCサーバーを開発するか、クラウドプロバイダーによって既に提供されているKMSプラグインを有効にすることができます。 その後、プラグインをリモートKMSと統合し、Kubernetesコントロールプレーンにデプロイします。

クラウドプロバイダーがサポートするKMSの有効化

クラウドプロバイダー固有のKMSプラグインを有効にする手順については、お使いのクラウドプロバイダーを参照してください。

KMSプラグインgRPCサーバーの開発

Go用に利用可能なスタブファイルを使用してKMSプラグインgRPCサーバーを開発できます。 他の言語については、protoファイルを使用してgRPCサーバーコードの開発に使用できるスタブファイルを作成します。

KMS v1

  • Goを使用する場合: gRPCサーバーコードを開発するには、スタブファイル内の関数とデータ構造を使用してください: api.pb.go

  • Go以外の言語を使用する場合: 個別言語向けのスタブファイルを生成するには、protocコンパイラーとprotoファイルを使用してください: api.proto

KMS v2

  • Goを使用する場合: gRPCサーバーコードの開発を簡単にするために高レベルライブラリが提供されています。 低レベルの実装では、スタブファイル内の関数とデータ構造を使用できます: api.pb.go

  • Go以外の言語を使用する場合: 個別言語向けのスタブファイルを生成するには、protocコンパイラーとprotoファイルを使用してください: api.proto

その後、スタブファイル内の関数とデータ構造を使用してサーバーコードを開発してください。

注意事項

KMS v1
  • KMSプラグインバージョン: v1beta1

    Versionプロシージャコールへの応答で、互換性のあるKMSプラグインはVersionResponse.versionとしてv1beta1を返す必要があります。

  • メッセージバージョン: v1beta1

    KMSプロバイダーからのすべてのメッセージには、バージョンフィールドがv1beta1に設定されています。

  • プロトコル: UNIXドメインソケット(unix)

    プラグインは、UNIXドメインソケットでリッスンするgRPCサーバーとして実装されます。 稼働中のプラグインは、UNIXドメインソケットでgRPC接続を待受するために、ファイルシステム上にファイルを作成する必要があります。 APIサーバー(gRPCクライアント)は、KMSプロバイダー(gRPCサーバー)とUNIXドメインソケットを通じて通信するためにエンドポイントを設定します。 unix:///@fooのように、/@から始まるエンドポイントを指定すると、抽象化したLinuxソケットを利用できます。 従来のファイルベースのソケットとは異なり、このタイプのソケットにはACLの概念がないため、使用する際は注意が必要です。 ただし、これらのソケットはLinuxネットワーク名前空間で制御されるため、ホストネットワーキングが使用されていない限りは、同じPod内のコンテナからのみアクセスできます。

KMS v2
  • KMSプラグインバージョン: v2

    Statusリモートプロシージャコールへの応答の際、KMS v2に互換なKMSプラグインはStatusResponse.versionとしてKMS互換性バージョンを返す必要があります。 また、ステータス応答にはStatusResponse.healthzとして「ok」、StatusResponse.key_idとしてkey_id(リモートKMSのKEK ID)を含める必要があります。 Kubernetesプロジェクトでは、プラグインを安定したv2 KMS APIと互換性があるようにすることを推奨します。 Kubernetes 1.33はKMS用のv2beta1 APIもサポートしており、将来のKubernetesリリースでもそのベータバージョンのサポートが継続される可能性があります。

    APIサーバーは、すべてが正常な場合は約1分ごとにStatusプロシージャコールをポーリングし、プラグインが正常でない場合は10秒ごとにポーリングします。 プラグインは、常にこの呼び出し負荷がかかることを考慮して最適化する必要があります。

  • 暗号化

    EncryptRequestプロシージャコールは、平文に加えて、ログ目的のUIDフィールドを提供します。 応答には暗号文と使用するKEKのkey_idを含める必要があり、KMSプラグインが将来のDecryptRequest呼び出しを(annotationsフィールド経由で)支援するために必要とする、任意のメタデータを含めることもできます。 プラグインは、異なる任意の平文が異なる応答(ciphertext, key_id, annotations)を与えることを保証しなければなりません(MUST)。

    プラグインが空でないannotationsマップを返す場合、すべてのマップキーはexample.comのような完全修飾ドメイン名である必要があります。 annotationの使用例は{"kms.example.io/remote-kms-auditid":"<audit ID used by the remote KMS>"}です。

    APIサーバーは高頻度でEncryptRequestプロシージャコールを実行しませんが、プラグインの実装では各リクエストのレイテンシーを100ミリ秒未満に保つことを目指す必要があります。

  • 復号

    DecryptRequestプロシージャコールでは、EncryptRequestから得た(ciphertext, key_id, annotations)とログ目的のUIDを提供します。 期待される通り、これはEncryptRequest呼び出しの逆です。 プラグインはkey_idが既知のものかどうかを検証しなければなりません(MUST)。 また、以前にプラグイン自身が暗号化したデータであることを確証できない限り、データの復号を試みてはいけません(MUST NOT)。

    APIサーバーは、Watchキャッシュを満たすために起動時に何千ものDecryptRequestプロシージャコールを実行する可能性があります。 したがって、プラグインの実装はこれらの呼び出しを可能な限り迅速に実行する必要があり、各リクエストのレイテンシーを10ミリ秒未満に保つことを目指す必要があります。

  • key_idと鍵ローテーションの理解

    key_idは、現在使用中のリモートKMS KEKの公開された非機密の名前です。 APIサーバーの通常の動作中にログに記録される可能性があるため、プライベートデータを含んではいけません。 プラグインの実装では、データの漏洩を避けるためにハッシュの使用が推奨されます。 KMS v2メトリクスは、/metricsエンドポイント経由で公開する前にこの値をハッシュ化するよう注意しています。

    APIサーバーは、Statusプロシージャコールから返されるkey_idを信頼できるものと見なします。 したがって、この値の変更は、リモートKEKが変更されたことをAPIサーバーに通知し、古いKEKで暗号化されたデータはno-op書き込みが実行されたときに古いものとしてマークされる必要があります(以下で説明)。 EncryptRequestプロシージャコールがStatusとは異なるkey_idを返す場合、応答は破棄され、プラグインは正常でないと見なされます。 したがって、実装はStatusから返されるkey_idEncryptRequestによって返されるものと同じであることを保証する必要があります。 さらに、プラグインはkey_idが安定しており、値間で変動しないことを確保する必要があります(つまり、リモートKEKローテーション中)。

    プラグインは、以前に使用されたリモートKEKが復元された状況でも、key_idを再利用してはいけません。 プラグインがkey_id=Aを使用していて、key_id=Bに切り替え、その後key_id=Aに戻った場合、key_id=Aを報告する代わりに、プラグインはkey_id=A_001のような派生値を報告するか、key_id=Cのような新しい値を使用する必要があります。

    APIサーバーは約1分ごとにStatusをポーリングするため、key_idローテーションは即座には行われません。 さらに、APIサーバーは約3分間最後の有効な状態で継続します。 したがって、ユーザーがストレージ移行に対して受動的な(つまり、待機による)アプローチを取りたい場合、リモートKEKがローテーションされた後の3 + N + M分でマイグレーションを予定する必要があります(Nはプラグインがkey_idの変更を検知するのにかかる時間、Mは設定変更が処理されることを許可するための望ましいバッファです。Mの最小値は5分が推奨されます)。 KEKローテーションを実行するためにAPIサーバーの再起動は必要ないことに注意してください。

  • プロトコル: UNIXドメインソケット(unix)

    プラグインは、UNIXドメインソケットでリッスンするgRPCサーバーとして実装されます。 稼働中のプラグインは、UNIXドメインソケットでgRPC接続を待受するために、ファイルシステム上にファイルを作成する必要があります。 APIサーバー(gRPCクライアント)は、KMSプロバイダー(gRPCサーバー)とUNIXドメインソケットを通じて通信するためにエンドポイントを設定します。 unix:///@fooのように、/@から始まるエンドポイントを指定すると、抽象化したLinuxソケットを利用できます。 従来のファイルベースのソケットとは異なり、このタイプのソケットにはACLの概念がないため、使用する際は注意が必要です。 ただし、これらのソケットはLinuxネットワーク名前空間で制御されるため、ホストネットワーキングが使用されていない限りは、同じPod内のコンテナからのみアクセスできます。

KMSプラグインとリモートKMSの統合

KMSプラグインは、KMSがサポートする任意のプロトコルを使用してリモートKMSと通信できます。 KMSプラグインがリモートKMSとの通信に使用する認証資格情報を含むすべての設定データは、KMSプラグインによって独立して保存・管理されます。 KMSプラグインは、復号のためにKMSに送信する前に必要な追加のメタデータで暗号文をエンコードできます(KMS v2は専用のannotationsフィールドを提供することでこのプロセスを簡単にします)。

KMSプラグインのデプロイ

KMSプラグインがKubernetes APIサーバーと同じホストで実行されることを確認してください。

KMSプロバイダーでデータを暗号化する

データを暗号化するには:

  1. SecretやConfigMapなどのリソースを暗号化するために、kmsプロバイダーの適切なプロパティを使用して新しいEncryptionConfigurationファイルを作成します。 CustomResourceDefinitionで定義された拡張APIを暗号化したい場合、クラスターはKubernetes v1.26以降を実行している必要があります。

  2. kube-apiserverの--encryption-provider-configフラグを、設定ファイルの場所を指すように設定します。

  3. --encryption-provider-config-automatic-reloadブール引数は、--encryption-provider-configで設定されたファイルがディスクの内容が変更された場合に自動的にリロードされるかどうかを決定します。

  4. APIサーバーを再起動します。

KMS v1

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile-foo.sock
          cachesize: 100
          timeout: 3s
      - kms:
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile-bar.sock
          cachesize: 100
          timeout: 3s

KMS v2

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          apiVersion: v2
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile-foo.sock
          timeout: 3s
      - kms:
          apiVersion: v2
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile-bar.sock
          timeout: 3s

--encryption-provider-config-automatic-reloadtrueに設定すると、すべてのヘルスチェックが単一のヘルスチェックエンドポイントに統合されます。 個別のヘルスチェックは、KMS v1プロバイダーが使用されており、暗号化設定が自動リロードされない場合にのみ利用できます。

以下の表は、各KMSバージョンのヘルスチェックエンドポイントをまとめています:

KMS設定自動リロードなし自動リロードあり
KMS v1のみ個別ヘルスチェック単一ヘルスチェック
KMS v2のみ単一ヘルスチェック単一ヘルスチェック
KMS v1とv2の両方個別ヘルスチェック単一ヘルスチェック
KMSなしなし単一ヘルスチェック

単一ヘルスチェックは、唯一のヘルスチェックエンドポイントが/healthz/kms-providersであることを意味します。

個別ヘルスチェックは、各KMSプラグインが暗号化設定での位置に基づいて関連するヘルスチェックエンドポイントを持つことを意味します: /healthz/kms-provider-0/healthz/kms-provider-1など。

これらのヘルスチェックエンドポイントパスはハードコードされており、サーバーによって生成/制御されます。 個別ヘルスチェックのインデックスは、KMS暗号化設定が処理される順序に対応します。

すべてのシークレットが暗号化されていることを確認するで定義された手順が実行されるまで、providersリストは暗号化されていないデータの読み取りを許可するためにidentity: {}プロバイダーで終わる必要があります。 すべてのリソースが暗号化されたら、APIサーバーが暗号化されていないデータを受け入れることを防ぐためにidentityプロバイダーを削除する必要があります。

EncryptionConfiguration形式の詳細については、APIサーバー暗号化APIリファレンスを確認してください。

データが暗号化されていることを確認する

保存時暗号化が正しく設定されている場合、リソースは書き込み時に暗号化されます。 kube-apiserverを再起動した後、新しく作成または更新されたSecretやEncryptionConfigurationで設定されたその他のリソースタイプは、保存時に暗号化される必要があります。 確認するには、etcdctlコマンドラインプログラムを使用して機密データの内容を取得できます。

  1. default名前空間にsecret1という新しいシークレットを作成します:

    kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
    
  2. etcdctlコマンドラインを使用して、etcdからそのシークレットを読み取ります:

    ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C
    

    ここで[...]にはetcdサーバーに接続するための追加の引数が含まれます。

  3. 保存されたシークレットがKMS v1の場合はk8s:enc:kms:v1:で始まり、KMS v2の場合はk8s:enc:kms:v2:で始まることを確認します。 これはkmsプロバイダーが結果データを暗号化したことを示します。

  4. API経由で取得されたときに、シークレットが正しく復号されることを確認します:

    kubectl describe secret secret1 -n default
    

    Secretにはmykey: mydataが含まれている必要があります

すべてのシークレットが暗号化されていることを確認する

保存時暗号化が正しく設定されている場合、リソースは書き込み時に暗号化されます。 したがって、データが暗号化されることを確保するために、インプレースでno-op更新を実行できます。

以下のコマンドは、すべてのシークレットを読み取り、その後サーバーサイド暗号化を適用するために更新します。 競合する書き込みによるエラーが発生した場合は、コマンドを再試行してください。 大規模なクラスターの場合、名前空間によってシークレットを細分化するか、更新をスクリプト化することをお勧めします。

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

ローカル暗号化プロバイダーからKMSプロバイダーへの切り替え

ローカル暗号化プロバイダーからkmsプロバイダーに切り替えて、すべてのシークレットを再暗号化するには:

  1. 以下の例に示すように、kmsプロバイダーを設定ファイルの最初のエントリとして追加します。

    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
      - resources:
          - secrets
        providers:
          - kms:
              apiVersion: v2
              name : myKmsPlugin
              endpoint: unix:///tmp/socketfile.sock
          - aescbc:
              keys:
                - name: key1
                  secret: <BASE64エンコード済みシークレット>
    
  2. すべてのkube-apiserverプロセスを再起動します。

  3. 以下のコマンドを実行して、kmsプロバイダーを使用してすべてのシークレットを強制的に再暗号化します。

    kubectl get secrets --all-namespaces -o json | kubectl replace -f -
    

次の項目

Kubernetes APIに永続化されたデータの暗号化をもう使用したくない場合は、既に保存時に保存されているデータの復号を読んでください。

最終更新 August 18, 2025 at 8:43 PM PST: [ja] Translate kms-provider.md into Japanese (#51690) (7d5bebdcdc)