GKEネットワークポリシーでのPod間通信の制御について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、 「どうやって直すのか?」 という具体的な修復手順(コンソール、gcloud CLI、Terraformなど)まで、分かりやすく解説します。

この記事では、GKEクラスタでネットワークポリシーが無効化されている場合のリスクと対策を解説します。

ポリシーの説明

Kubernetesのネットワークポリシーは、Pod間の通信を制御するためのセキュリティ機能です。デフォルトでは、Kubernetes内の全てのPodは相互に通信可能ですが、ネットワークポリシーを使用することで、必要な通信のみを許可し、不要な通信を遮断できます。GKEではCalicoを使用し、Dataplane V2ではCiliumを使用してネットワークポリシーを実装できます。

 

修復手順

⚠️ 重要な注意事項 ネットワークポリシーを有効化する前に、既存の通信パターンを分析し、必要なポリシーを事前に準備することを推奨します。

コンソールでの修復手順(Dataplane v2以前の手順)

Google Cloud コンソールを使用して、GKEクラスタでネットワークポリシーを有効化します。

  1. Google Cloud コンソールにアクセス
    • GKEクラスタ一覧にアクセスします
    • 対象のクラスタ名をクリックして詳細画面を開きます
  2. 既存クラスタでネットワークポリシーを有効化
    • クラスタの詳細ページで「編集」ボタンをクリックします
    • 「ネットワーキング」セクションまでスクロールします
    • 「ネットワークポリシーを有効にする」のチェックボックスをオンにします
    • ネットワークポリシープロバイダーとして「Calico」を選択(推奨)
    • 「保存」をクリックして変更を適用します
  3. Cloud Shellを起動してクラスタに接続
    gcloud container clusters get-credentials [CLUSTER_NAME] --zone [ZONE] --project [PROJECT_ID]
    

     

  4. ネットワークポリシーが有効になっていることを確認
    # ネットワークポリシーのAPIが有効か確認
    kubectl api-resources | grep networkpolicies
    
    # Calicoのポッドが実行されているか確認
    kubectl get pods -n kube-system | grep calico
    

     

  5. デフォルト拒否ポリシーの作成

    まず、全ての通信をデフォルトで拒否するポリシーを作成します:

    # default-deny-all.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-all
      namespace: default
    spec:
      podSelector: {}
      policyTypes:
      - Ingress
      - Egress
    

     

    kubectl apply -f default-deny-all.yaml
    

     

  6. 必要な通信のみを許可するポリシーを作成

    例:フロントエンドからバックエンドへの通信のみを許可

    # allow-frontend-to-backend.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-frontend-to-backend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: backend
      policyTypes:
      - Ingress
      ingress:
      - from:
        - podSelector:
            matchLabels:
              app: frontend
        ports:
        - protocol: TCP
          port: 8080
    

     

    kubectl apply -f allow-frontend-to-backend.yaml
    

     

  7. DNSアクセスを許可(必須)
    # allow-dns-access.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-dns-access
      namespace: default
    spec:
      podSelector: {}
      policyTypes:
      - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              name: kube-system
        ports:
        - protocol: UDP
          port: 53
    

     

    kubectl apply -f allow-dns-access.yaml
    

     

gcloud CLIでの修復手順

gcloud CLIを使用してネットワークポリシーを有効化する完全な手順:

# 1. 現在のクラスタ設定を確認
gcloud container clusters describe CLUSTER_NAME \
    --zone=ZONE \
    --project=PROJECT_ID \
    --format="value(networkPolicy.enabled)"

# 2. 既存クラスタでネットワークポリシーを有効化
gcloud container clusters update CLUSTER_NAME \
    --zone=ZONE \
    --project=PROJECT_ID \
    --enable-network-policy

# 3. アップデートの進行状況を確認
gcloud container operations list \
    --zone=ZONE \
    --project=PROJECT_ID \
    --filter="TYPE:UPDATE_CLUSTER AND targetLink~CLUSTER_NAME" \
    --limit=1

# 4. 新規クラスタ作成時にネットワークポリシーを有効化
gcloud container clusters create secure-cluster \
    --zone=ZONE \
    --project=PROJECT_ID \
    --enable-network-policy \
    --enable-ip-alias \
    --cluster-secondary-range-name=pods \
    --services-secondary-range-name=services \
    --enable-private-nodes \
    --master-ipv4-cidr=10.0.0.0/28 \
    --enable-autorepair \
    --enable-autoupgrade

# 5. Dataplane V2 (Cilium) を使用する場合
gcloud container clusters create advanced-cluster \
    --zone=ZONE \
    --project=PROJECT_ID \
    --enable-dataplane-v2 \
    --enable-ip-alias \
    --cluster-secondary-range-name=pods \
    --services-secondary-range-name=services

# 6. ネットワークポリシーのテストツールをインストール
kubectl apply -f <https://raw.githubusercontent.com/ahmetb/kubernetes-network-policy-recipes/master/01-deny-all-traffic-to-an-application.yaml>

# 7. 接続性テスト用のPodを作成
kubectl run test-source --image=busybox --rm -it -- /bin/sh
# 別のターミナルで
kubectl run test-target --image=nginx --labels="app=target"

# 8. ポリシー適用前後の接続性を確認
# test-source Pod内から
wget -qO- test-target:80  # ポリシー適用前は成功
# ポリシー適用後は失敗することを確認

# 9. 監視用のログクエリを設定
gcloud logging read '
resource.type="k8s_cluster"
AND protoPayload.methodName="io.k8s.networking.v1.networkpolicies"' \
    --limit=50 \
    --format=json \
    --project=PROJECT_ID

 

Terraformでの修復手順

ネットワークポリシーを有効化したGKEクラスタと、セキュアなネットワークポリシーのTerraformコードを説明します。

# ネットワークポリシーを有効化したGKEクラスタ
resource "google_container_cluster" "primary" {
  name     = "secure-gke-cluster"
  location = var.region

  # 初期ノードプールを削除(後で適切なものを作成)
  remove_default_node_pool = true
  initial_node_count       = 1

  # ネットワークポリシーの有効化(重要)
  network_policy {
    enabled  = true
    provider = "CALICO"  # CALICOを使用(高度な機能をサポート)
  }

  # Dataplaneの設定(注意:ADVANCED_DATAPATHを使用する場合、network_policyは自動的に有効になります)
  # datapath_provider = "ADVANCED_DATAPATH"  # Ciliumを使用する場合はコメントアウトを解除

  # IPエイリアスの有効化(ネットワークポリシーに必要)
  ip_allocation_policy {
    cluster_secondary_range_name  = "pods"
    services_secondary_range_name = "services"
  }

  # プライベートクラスタ設定(推奨)
  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false
    master_ipv4_cidr_block  = "10.0.0.0/28"
  }

  # ワークロードアイデンティティ
  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }
}

# ノードプール
resource "google_container_node_pool" "primary" {
  name       = "primary-node-pool"
  location   = var.region
  cluster    = google_container_cluster.primary.name
  node_count = var.node_count

  node_config {
    machine_type = var.machine_type

    # セキュリティ設定
    shielded_instance_config {
      enable_secure_boot          = true
      enable_integrity_monitoring = true
    }

    metadata = {
      disable-legacy-endpoints = "true"
    }

    oauth_scopes = [
      "<https://www.googleapis.com/auth/logging.write>",
      "<https://www.googleapis.com/auth/monitoring>",
    ]
  }
}

# Kubernetesプロバイダーの設定
provider "kubernetes" {
  host                   = "<https://$>{google_container_cluster.primary.endpoint}"
  token                  = data.google_client_config.default.access_token
  cluster_ca_certificate = base64decode(google_container_cluster.primary.master_auth[0].cluster_ca_certificate)
}

# ネームスペースの作成
resource "kubernetes_namespace" "app_namespaces" {
  for_each = toset(["frontend", "backend", "database"])

  metadata {
    name = each.key
    labels = {
      name = each.key
    }
  }
}

# デフォルト拒否ポリシー(各ネームスペースに適用)
resource "kubernetes_network_policy" "default_deny_all" {
  for_each = kubernetes_namespace.app_namespaces

  metadata {
    name      = "default-deny-all"
    namespace = each.value.metadata[0].name
  }

  spec {
    pod_selector {}
    policy_types = ["Ingress", "Egress"]
  }
}

# DNS解決を許可(全ネームスペース)
resource "kubernetes_network_policy" "allow_dns" {
  for_each = kubernetes_namespace.app_namespaces

  metadata {
    name      = "allow-dns-access"
    namespace = each.value.metadata[0].name
  }

  spec {
    pod_selector {}
    policy_types = ["Egress"]

    egress {
      to {
        namespace_selector {
          match_labels = {
            "name" = "kube-system"
          }
        }
      }
      ports {
        port     = "53"
        protocol = "UDP"
      }
      ports {
        port     = "53"
        protocol = "TCP"
      }
    }
  }
}

# フロントエンドからバックエンドへの通信を許可
resource "kubernetes_network_policy" "frontend_to_backend" {
  metadata {
    name      = "allow-frontend-to-backend"
    namespace = "backend"
  }

  spec {
    pod_selector {
      match_labels = {
        app = "backend"
      }
    }

    policy_types = ["Ingress"]

    ingress {
      from {
        namespace_selector {
          match_labels = {
            name = "frontend"
          }
        }
        pod_selector {
          match_labels = {
            app = "frontend"
          }
        }
      }
      ports {
        port     = "8080"
        protocol = "TCP"
      }
    }
  }
}

# バックエンドからデータベースへの通信を許可
resource "kubernetes_network_policy" "backend_to_database" {
  metadata {
    name      = "allow-backend-to-database"
    namespace = "database"
  }

  spec {
    pod_selector {
      match_labels = {
        app = "database"
      }
    }

    policy_types = ["Ingress"]

    ingress {
      from {
        namespace_selector {
          match_labels = {
            name = "backend"
          }
        }
        pod_selector {
          match_labels = {
            app = "backend"
          }
        }
      }
      ports {
        port     = "5432"  # PostgreSQL
        protocol = "TCP"
      }
    }
  }
}

# インターネットからフロントエンドへのHTTPS通信を許可
resource "kubernetes_network_policy" "allow_internet_to_frontend" {
  metadata {
    name      = "allow-internet-to-frontend"
    namespace = "frontend"
  }

  spec {
    pod_selector {
      match_labels = {
        app = "frontend"
      }
    }

    policy_types = ["Ingress"]

    ingress {
      ports {
        port     = "443"
        protocol = "TCP"
      }
      ports {
        port     = "80"
        protocol = "TCP"
      }
    }
  }
}

# バックエンドから外部APIへの通信を許可(必要に応じて)
resource "kubernetes_network_policy" "backend_egress_to_external" {
  metadata {
    name      = "allow-backend-external-api"
    namespace = "backend"
  }

  spec {
    pod_selector {
      match_labels = {
        app = "backend"
      }
    }

    policy_types = ["Egress"]

    egress {
      # DNSアクセス
      to {
        namespace_selector {
          match_labels = {
            name = "kube-system"
          }
        }
      }
      ports {
        port     = "53"
        protocol = "UDP"
      }
    }

    egress {
      # 外部API(HTTPS)
      ports {
        port     = "443"
        protocol = "TCP"
      }
    }
  }
}

# 変数定義
variable "project_id" {
  description = "GCP Project ID"
  type        = string
}

variable "region" {
  description = "GCP region"
  type        = string
  default     = "asia-northeast1"
}

variable "node_count" {
  description = "Number of nodes"
  type        = number
  default     = 3
}

variable "machine_type" {
  description = "Machine type for nodes"
  type        = string
  default     = "e2-standard-4"
}

# ネットワークポリシーの状態を出力
output "network_policy_status" {
  value = {
    enabled  = google_container_cluster.primary.network_policy[0].enabled
    provider = google_container_cluster.primary.network_policy[0].provider
  }
}

 

最後に

この記事では、GKEクラスタでネットワークポリシーを有効化し、Pod間通信を制御する方法について、リスクと対策を解説しました。

ネットワークポリシーを適用することで、最小権限の原則に基づいた通信制御を実現し、横展開攻撃やデータ漏洩のリスクを大幅に軽減できます。デフォルト拒否ポリシーから始めて、必要な通信のみを明示的に許可することが重要です。

この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

参考情報

関連ドキュメント

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る