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

このページでは、Google Distributed Cloud(GDC)エアギャップの専用ノードプールでコンテナ ワークロードを分離して、Pod をより詳細に制御する方法について説明します。ワークロードの分離には、次のようなメリットがあります。

  • Kubernetes クラスタでの権限昇格攻撃のリスクが軽減されます。
  • 追加のリソースを必要とする Pod をより細かく制御できます。

このような場合は、コンテナ ワークロードを分離して、制御と最適化を強化することを検討してください。

ワークロードを分離する理由

ワークロードを専用ノードプールに分離する必要はありませんが、潜在的な問題を回避するための賢明な対策となります。ただし、専用ノードプールの管理にはより多くの監視が必要であり、多くの場合、不要です。

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

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

専用ノードプールは、他より多くのリソース(メモリやローカル ディスク容量など)を必要とする Pod をスケジュールする必要がある場合にも役立ちます。

次のメカニズムを使用して、専用ノードプールでワークロードをスケジュールできます。

ノード taint は、これらのノード上で対応する toleration のないワークロード(GDC マネージド ワークロードなど)のスケジューリングを回避するよう Kubernetes クラスタに通知します。独自のワークロードのノード アフィニティは、Pod を専用ノードでスケジュールするようにクラスタに指示します。

ノード分離の制限事項

  • 攻撃者は、侵害されたノードからサービス拒否攻撃(DoS)を開始できます。

  • 侵害されたノードは、クラスタ内のすべての Pod や Namespace など、引き続き多くのリソースを読み取ることができます。

  • 侵害されたノードは、そのノードで実行されているすべての Pod が使用する Secret と認証情報にアクセスできます。

  • ワークロードを分離するために別のノードプールを使用すると、コスト効率、自動スケーリング、リソース使用率に影響を与える可能性があります。

  • 侵害されたノードは、下りネットワーク ポリシーを回避できます。

  • 一部の GDC マネージド ワークロードは、クラスタ内のすべてのノードで実行される必要があり、すべての taint を許容するように構成されています。

  • 昇格した権限を持ち、あらゆる taint を許容できる DaemonSet リソースをデプロイすると、これらの Pod が、侵害されたノードからの権限昇格に利用される可能性があります。

ノード分離の仕組み

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

  1. ワークロードのノードプールに taint とラベルを追加します。

  2. 対応する toleration とノード アフィニティ ルールでワークロードを更新します。

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

始める前に

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

  • 専用ノードプールに使用するノード taint とノードラベルに特定の名前を選択します。例: workloadType=untrusted

  • 必要に応じて、組織 IAM 管理者に、名前空間にバインドされていないユーザー クラスタ デベロッパー ロール(user-cluster-developer)を付与するよう依頼します。

新しいノードプールに taint とラベルを適用する

新しいノードプールに taint またはラベルを適用すると、後で追加されたノードを含むすべてのノードが、指定された taint とラベルを自動的に取得します。

新しいノードプールに taint とラベルを追加する手順は次のとおりです。

  1. ノードプールを作成するときに、Cluster カスタム リソースの nodePools セクションを直接編集します。

    nodePools:
      ...
      - machineTypeName: n2-standard-2-gdc
        name: nodepool-1
        nodeCount: 3
        taints: TAINT_KEY=TAINT_VALUE:TAINT_EFFECT
        labels: LABEL_KEY=LABEL_VALUE
    

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

    • TAINT_KEY=TAINT_VALUE: スケジュールする TAINT_EFFECT に関連付けられた Key-Value ペア。例: workloadType=untrusted
    • TAINT_EFFECT: 次のいずれかの効果値。
      • NoSchedule: この taint を容認しない Pod はノード上にスケジュールされません。既存の Pod はノードから強制排除されません。
      • PreferNoSchedule: Kubernetes は、この taint を容認しない Pod をノード上にスケジュールすることを避けます。
      • NoExecute: ノードですでに実行されている Pod は、ノードから強制排除されます。ノードでまだ実行されていない Pod は、ノード上にスケジュールされません。
    • LABEL_KEY=LABEL_VALUE: ノードラベルの Key-Value ペア。ワークロード マニフェストで指定したセレクタに対応しています。
  2. Cluster リソースを適用して、新しいノードプールを作成します。

    kubectl apply -f cluster.yaml \
        --kubeconfig MANAGEMENT_API_SERVER
    

    MANAGEMENT_API_SERVER は、Kubernetes クラスタがホストされているゾーン API サーバーの kubeconfig パスに置き換えます。ターゲット ゾーンの API サーバーの kubeconfig ファイルをまだ生成していない場合は、ログインをご覧ください。

既存のノードプールに taint とラベルを適用する

既存のノードプールに taint またはラベルを適用するには、既存の各ノードに変更を適用する必要があります。ノードプール構成を動的に更新することはできません。

既存のノードプールに taint とラベルを追加する手順は次のとおりです。

  1. 専用ノードプール内のノードを一覧表示します。

    kubectl get node --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG \
        -l baremetal.cluster.gke.io/node-pool=NODE_POOL_NAME
    

    次の変数を置き換えます。

    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes クラスタの kubeconfig パス。
    • NODE_POOL_NAME: 専用ノードプールの名前。

    出力から、ノードプール内のすべてのノードの各ノード ID をメモします。

  2. ノードプール内の各ノードに、次の taint を適用します。

    kubectl taint nodes NODE_ID \
        TAINT_KEY=TAINT_VALUE:TAINT_EFFECT \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    次の変数を置き換えます。

    • NODE_ID: 専用ノードプール内のワーカーノードの ID。
    • TAINT_KEY=TAINT_VALUE: スケジュールする TAINT_EFFECT に関連付けられた Key-Value ペア。例: workloadType=untrusted
    • TAINT_EFFECT: 次のいずれかの効果値。
      • NoSchedule: この taint を容認しない Pod はノード上にスケジュールされません。既存の Pod はノードから強制排除されません。
      • PreferNoSchedule: Kubernetes は、この taint を容認しない Pod をノード上にスケジュールすることを避けます。
      • NoExecute: ノードですでに実行されている Pod は、ノードから強制排除されます。ノードでまだ実行されていない Pod は、ノード上にスケジュールされません。
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes クラスタの kubeconfig パス。
  3. ノードプール内の各ノードに、コンテナ ワークロードで定義するセレクタに対応するラベルを適用します。

    kubectl label NODE_ID \
        LABEL_KEY:LABEL_VALUE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    次の変数を置き換えます。

    • NODE_ID: 専用ノードプール内のワーカーノードの ID。
    • LABEL_KEY:LABEL_VALUE: ノードラベルの Key-Value ペア。ワークロード マニフェストで指定したセレクタに対応しています。
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes クラスタの kubeconfig パス。

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

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

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

専用ノードプールで実行するようにコンテナ ワークロードを構成する手順は次のとおりです。

  1. コンテナ ワークロードの .spec.template.spec セクションに次のセクションを追加します。

    kind: Deployment
    apiVersion: apps/v1
        ...
        spec:
        ...
          template:
            spec:
              tolerations:
              - key: TAINT_KEY
                operator: Equal
                value: TAINT_VALUE
                effect: TAINT_EFFECT
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: LABEL_KEY
                        operator: In
                        values:
                        - "LABEL_VALUE"
              ...
    

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

    • TAINT_KEY: 専用ノードプールに適用した taint キー。
    • TAINT_VALUE: 専用ノードプールに適用した taint 値。
    • TAINT_EFFECT: 次のいずれかの効果値。
      • NoSchedule: この taint を容認しない Pod はノード上にスケジュールされません。既存の Pod はノードから強制排除されません。
      • PreferNoSchedule: Kubernetes は、この taint を容認しない Pod をノード上にスケジュールすることを避けます。
      • NoExecute: ノードですでに実行されている Pod は、ノードから強制排除されます。ノードでまだ実行されていない Pod は、ノード上にスケジュールされません。
    • LABEL_KEY: 専用ノードプールに適用したノードラベルのキー。
    • LABEL_VALUE: 専用ノードプールに適用したノードラベル値。

    たとえば、次の Deployment リソースは、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: workloadType
            operator: Equal
            value: untrusted
            effect: NoExecute
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: workloadType
                    operator: In
                    values:
                    - "untrusted"
          containers:
          - name: my-app
            image: harbor-1.org-1.zone1.google.gdc.test/harborproject/my-app
            ports:
            - containerPort: 80
          imagePullSecrets:
          - name: SECRET
    
  2. デプロイを更新します。

    kubectl apply -f deployment.yaml -n NAMESPACE \
        --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG
    

    次の変数を置き換えます。

    • NAMESPACE: コンテナ ワークロードのプロジェクト Namespace。
    • KUBERNETES_CLUSTER_KUBECONFIG: Kubernetes クラスタの kubeconfig パス。

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

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

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

kubectl get pods -o=wide -n NAMESPACE \
    --kubeconfig KUBERNETES_CLUSTER_KUBECONFIG

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

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

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