ワークロードを専用ノードプールに分離する


このページでは、特権付き GKE マネージド ワークロードとは別の専用ノードプールでワークロードをスケジュールするように Google Kubernetes Engine(GKE)に指示することで、クラスタ内の権限昇格攻撃のリスクを軽減する方法について説明します。この方法は、GKE Sandbox を使用できない場合にのみ使用してください。ノード分離には GKE Sandbox を使用することをおすすめします。GKE Sandbox は、ワークロードの強化にも役立ちます。

このページは、ワークロードの分離レイヤが必要で、GKE Sandbox を使用できないセキュリティ スペシャリストを対象としています。 Google Cloud のコンテンツで使用されている一般的なロールとタスクの例の詳細については、一般的な GKE Enterprise ユーザーロールとタスクをご覧ください。

このページは、ノード自動プロビジョニングのない Standard クラスタに適用されます。ノード自動プロビジョニングが有効になっている Standard クラスタと Autopilot クラスタでワークロードを分離するには、GKE でワークロードの分離を構成するをご覧ください。

概要

GKE クラスタは、特権付き GKE マネージド ワークロードを使用して、指標の収集などの特定のクラスタの機能を有効にします。これらのワークロードには、クラスタ内で正しく実行するための特別な権限が付与されます。

ノードにデプロイするワークロードは、悪意のあるエンティティによって侵害されるおそれがあります。特権付き GKE マネージド ワークロードとともにこれらのワークロードを実行すると、侵害されたコンテナをブレイクアウトした攻撃者が、ノード上の特権付きワークロードの認証情報を使用して、クラスタ内の権限を昇格できるようになります。

コンテナ ブレイクアウトを防ぐ

主要な防御の対象はアプリケーションです。GKE には、クラスタと Pod を強化するために使用できる機能が複数あります。ほとんどの場合、ワークロードの分離に GKE Sandbox を使用することを強くおすすめします。GKE Sandbox は gVisor オープンソース プロジェクトに基づいており、ユーザー空間に Linux カーネル API を実装します。各 Pod は、ホストカーネル内の特権付きシステムコールへのアクセスを防止するため、アプリケーションをサンドボックス化する専用カーネルで実行します。GKE Sandbox で実行するワークロードは、他のワークロードから分離され、別々のノードで自動的にスケジューリングされます。

クラスタのセキュリティの強化の推奨事項にも従う必要があります。

権限昇格攻撃を回避する

GKE Sandbox を使用できず、他の強化策に加えて分離を強化したい場合は、ノード taintノード アフィニティを使用して、専用ノードプールでワークロードをスケジュールします。ノード taint は、これらのノード上で対応する toleration のないワークロード(GKE マネージド ワークロードなど)のスケジューリングを回避するよう GKE に指示します。独自のワークロードのノード アフィニティは、GKE に対し Pod を専用ノードでスケジュールするよう指示します。

ノード分離の制限事項

  • 攻撃者は、侵害されたノードからサービス拒否攻撃(DoS)を開始できます。
  • 侵害されたノードは、クラスタ内のすべての Pod や Namespace など、引き続き多くのリソースを読み取ることができます。
  • 侵害されたノードは、そのノードで実行されているすべての Pod が使用する Secret と認証情報にアクセスできます。
  • ワークロードを分離するために別のノードプールを使用すると、コスト効率、自動スケーリング、リソース使用率に影響を与える可能性があります。
  • 侵害されたノードは、下りネットワーク ポリシーを回避できます。
  • 一部の GKE マネージド ワークロードは、クラスタ内のすべてのノードで実行される必要があり、すべての taint を許容するように構成されています。
  • 昇格した権限を持ち、あらゆる taint を許容できる DaemonSet をデプロイすると、これらの Pod が、侵害されたノードからの権限昇格に利用される可能性があります。

ノード分離の仕組み

ワークロードにノード分離を実装するには、次のようにする必要があります。

  1. ワークロードのノードプールに taint とラベルを追加します。
  2. 対応する toleration とノード アフィニティ ルールでワークロードを更新します。

このガイドでは、クラスタ内の 1 つのノードプールから始めることを前提としています。ノード taint に加えてノード アフィニティを使用することは必須ではありませんが、スケジューリング全体をより詳細に管理できるようになるので、ノード アフィニティを使用することをおすすめします。

始める前に

作業を始める前に、次のことを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。
  • 専用ノードプールに使用するノードラベルとノード taint に特定の名前を選択します。この例では workloadType=untrusted を使用します。

ワークロードのノードプールに taint とラベルを適用する

ワークロードの新しいノードプールを作成し、ノード taint とノードラベルを適用します。ノードプール レベルで taint またはラベルを適用すると、自動スケーリングによって作成された新しいノードなど、新しいノードはすべて、指定された taint とラベルを自動的に取得します。

ノード taint とノードラベルを既存のノードプールに追加することもできます。NoExecute 効果を使用すると、GKE は、新しい taint に対応する toleration がないノード上で実行されている Pod を強制排除します。

新しいノードプールに taint とラベルを追加するには、次のコマンドを実行します。

gcloud container node-pools create POOL_NAME \
    --cluster CLUSTER_NAME \
    --node-taints TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
    --node-labels LABEL_KEY=LABEL_VALUE

次のように置き換えます。

  • POOL_NAME: ワークロードの新しいノードプールの名前。
  • CLUSTER_NAME: GKE クラスタの名前。
  • TAINT_KEY=TAINT_VALUE: スケジュールする TAINT_EFFECT に関連付けられた Key-Value ペア。例: workloadType=untrusted
  • TAINT_EFFECT: NoSchedulePreferNoSchedule、または NoExecute のいずれかの効果値NoExecuteNoSchedule よりも強制排除が保証されています。
  • LABEL_KEY=LABEL_VALUE: ノードラベルの Key-Value ペア。ワークロード マニフェストで指定したセレクタに対応しています。

ワークロードに toleration とノード アフィニティ ルールを追加する

専用ノードプールに taint を追加しても、追加した taint に対応する toleration がなければ、ワークロードはそのノードプールでスケジュールできません。toleration をワークロードの仕様に追加して、taint が追加されたノードプールで Pod がスケジュールされるようにします。

専用ノードプールにラベルを付けた場合は、ノード アフィニティ ルールを追加して、GKE にそのノードプールにのみワークロードをスケジュールする指示を行うこともできます。

次の例では、workloadType=untrusted:NoExecute taint に対する toleration と workloadType=untrusted ノードラベルのノード アフィニティ ルールを追加します。

kind: Deployment
apiVersion: apps/v1
metadata:
  name: my-app
  namespace: default
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      tolerations:
      - key: TAINT_KEY
        operator: Equal
        value: TAINT_VALUE
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: LABEL_KEY
                operator: In
                values:
                - "LABEL_VALUE"
      containers:
      - name: sleep
        image: ubuntu
        command: ["/bin/sleep", "inf"]

次のように置き換えます。

  • TAINT_KEY: 専用ノードプールに適用した taint キー。
  • TAINT_VALUE: 専用ノードプールに適用した taint 値。
  • LABEL_KEY: 専用のノードプールに適用したノードラベルのキー。
  • LABEL_VALUE: 専用ノードプールに適用したノードラベル値。

kubectl apply を使用して Deployment を更新すると、GKE では影響を受ける Pod が再作成されます。ノード アフィニティ ルールにより、作成した専用ノードプールに Pod が強制移動されます。この toleration によって、それらの Pod のみをノードに配置できます。

分離が機能していることを確認する

スケジューリングが正しく機能することを確認するには、次のコマンドを実行してワークロードが専用ノードプールにあるかどうかを確認します。

kubectl get pods -o=wide

推奨事項とベスト プラクティス

ノード分離を設定したら、次の作業を行うことをおすすめします。

  • components.gke.io/gke-managed-components taint を追加して、特定のノードプールを GKE マネージド ワークロードのみに制限します。この taint を追加すると、独自の Pod がこれらのノードでスケジュールされなくなり、分離が改善します。
  • 新しいノードプールを作成する場合、独自の taint をこれらの新しいノードプールに追加して、ほとんどの GKE マネージド ワークロードがこれらのノードで実行されないようにします。
  • サードパーティ ツールをインストールする場合など、クラスタに新しいワークロードをデプロイする際は、常に Pod に必要な権限を監査してください。可能であれば、昇格権限を使用するワークロードを共有ノードにデプロイすることは避けてください。

次のステップ