概要

CSIドライバーがサービスアカウントトークンをより安全な secrets フィールド経由で受け取れるようにするオプトインメカニズムを追加するKEPです。CVE-2023-2878・CVE-2024-3744 として発見されたセキュリティ問題に対処します。v1.35 でBeta(デフォルト有効)、v1.36 でGAとなっています。


背景・前提知識

CSI(Container Storage Interface)とは? Kubernetes がストレージシステムと通信するための標準プロトコルです。AWS EBS・GCP PD・Azure Disk などのクラウドストレージや、Ceph・NFS などのストレージシステムがCSIドライバーとして実装されています。Kubernetes はCSIドライバーに対してgRPCでコマンドを送ります。

サービスアカウントトークンとは? Kubernetes の Pod に割り当てられるJWT(JSON Web Token)形式の認証情報です。PodがKubernetes APIやクラウドプロバイダーのAPIと通信する際に使用します。Secret Storeなどの外部システムにアクセスするためにCSIドライバーに渡される場合があります。

TokenRequests とは? CSIDriver のスペックに設定できるフィールドで、「このCSIドライバーはPodのサービスアカウントトークンが必要」と宣言するものです。設定されると kubelet がトークンを生成してCSIドライバーに渡します。

NodePublishVolumeRequest とは? kubelet がCSIドライバーにボリュームのマウントを要求する際に送るgRPCメッセージです。このメッセージに volume_context(非機密メタデータ)と secrets(機密データ)という2つのフィールドがあります。

protosanitizer とは? gRPCメッセージをログ出力する際に機密情報をマスクするライブラリです。secrets フィールドは自動的にマスクされますが、volume_context はマスクされません。ここに問題の根本があります。


詳細:問題の経緯と解決策

問題(CVE-2023-2878・CVE-2024-3744)

従来の実装では、kubelet はサービスアカウントトークンを volume_context マップに含めていました:

NodePublishVolumeRequest {
  volume_context: {
    "csi.storage.k8s.io/pod.name": "my-pod",
    "csi.storage.k8s.io/pod.namespace": "default",
    "csi.storage.k8s.io/serviceAccount.tokens": "eyJhbGci..."  // ← トークンがここに!
  }
}

volume_context は非機密メタデータのためのフィールドです。protosanitizer はこのフィールドをマスクしないため、CSIドライバーやサイドカーのデバッグログにトークンがそのまま記録されてしまいました。

解決策(KEP-5538)

CSIDriver スペックに新しいフィールド serviceAccountTokenInSecrets を追加します:

apiVersion: storage.k8s.io/v1
kind: CSIDriver
metadata:
  name: secrets-store.csi.k8s.io
spec:
  tokenRequests:
    - audience: "my-app"
      expirationSeconds: 3600
  serviceAccountTokenInSecrets: true  # ← 新フィールド(デフォルト: false)

true に設定すると、kubelet はトークンを volume_context ではなく secrets フィールドに配置します:

NodePublishVolumeRequest {
  volume_context: {
    "csi.storage.k8s.io/pod.name": "my-pod",
    "csi.storage.k8s.io/pod.namespace": "default"
    // トークンはここにはもう入らない
  }
  secrets: {
    "csi.storage.k8s.io/serviceAccount.tokens": "eyJhbGci..."  // ← protosanitizer がマスク
  }
}

secrets フィールドは gRPC の仕様でも機密データ向けに設計されており、protosanitizer が自動的にマスクします。

バリデーションルール

  1. serviceAccountTokenInSecrets: truetokenRequests が設定されている場合のみ有効
  2. CSIServiceAccountTokenSecrets feature gate が有効な場合のみ設定可能(v1.35 以降デフォルト有効)

CSIドライバー側の移行コード例

移行期間中(古いkubeletと新しいkubeletが混在する期間)は、両方のフィールドにフォールバックするコードが推奨されます:

const serviceAccountTokenKey = "csi.storage.k8s.io/serviceAccount.tokens"
 
func getServiceAccountTokens(req *csi.NodePublishVolumeRequest) (string, error) {
    // 新方式:secretsフィールドを最初に確認
    if tokens, ok := req.Secrets[serviceAccountTokenKey]; ok {
        return tokens, nil
    }
    
    // 旧方式へのフォールバック(古いkubelet対応)
    if tokens, ok := req.VolumeContext[serviceAccountTokenKey]; ok {
        return tokens, nil
    }
    
    return "", fmt.Errorf("service account tokens not found")
}

リリース履歴

バージョンステータス詳細
v1.34AlphaCSIServiceAccountTokenSecrets feature gate(デフォルト無効)
v1.35Betafeature gate デフォルト有効、CSIDriver への新フィールド追加
v1.36GA完全安定化、volume_context 使用時に警告を追加

既存ユーザへの影響

CSIドライバー開発者:

  • serviceAccountTokenInSecrets: true への移行を検討することを強く推奨
  • v1.36 以降、tokenRequests を使って serviceAccountTokenInSecrets を設定しない場合、APIサーバーから警告が表示される

クラスター管理者:

  • Secrets Store CSI Driver など TokenRequests を使うドライバーを使用している場合、ドライバーのアップデートに従って CSIDriver リソースを更新
  • デフォルト動作(false)は変わらないため、即時の対応は不要だが、セキュリティ向上のため移行を推奨

参考リンク