GKE Intranode VisibilityによるPod間通信の可視化について

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

この記事では、Google Kubernetes Engine (GKE) のIntranode Visibility機能を活用して、同一ノード内のPod間通信を可視化し、セキュリティ監視を強化する方法について、実践的な設定手順とベストプラクティスを解説します。

Intranode Visibilityとは

Intranode Visibility(ノード内可視化)は、GKEクラスタ内の同一ノード上で動作するPod間の通信を可視化する機能です。この機能を有効にすると、通常は見えないノード内部のトラフィックがVPCフローログに記録され、Cloud Loggingで確認できるようになります。これにより、マイクロサービス間の通信パターンの把握、異常なトラフィックの検知、トラブルシューティングの効率化が可能になります。特に、セキュリティ監視やコンプライアンス要件において重要な役割を果たします。

なぜIntranode Visibilityが必要なのか?

具体的なリスクと影響

  1. 内部脅威の検知困難
    • 同一ノード内のPod間通信が完全に不可視
    • 悪意のあるコンテナの横展開を検知できない
  2. セキュリティインシデントの調査不能
    • 攻撃経路の特定に必要な時間が10倍増加
    • 証跡不足による法的リスクの増大

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、GKEクラスタでIntranode Visibilityを有効化します。

事前確認事項

  • クラスタのバージョンが1.15以降であることを確認
  • VPCフローログが有効になっていることを確認
  • Cloud Loggingへの書き込み権限があることを確認

既存クラスタでのIntranode Visibility有効化

  1. Google Cloud コンソールで「Kubernetes Engine」→「クラスタ」に移動
  2. 対象のクラスタ名をクリックして詳細ページを開く
  3. 「クラスタ ネットワーキング」タブを選択
  1. 「ノード内の可視化」セクションを探す
  1. 「編集」ボタンをクリック
  2. 「ノード内の可視化を有効にする」のチェックボックスをオン
  3. 「保存」をクリックして変更を適用

新規クラスタ作成時のIntranode Visibility設定

  1. 「Kubernetes Engine」→「クラスタを作成」をクリック
  2. クラスタの基本設定を入力
  3. 「ネットワーキング」セクションを展開
  4. 「詳細なネットワーキングオプション」をクリック
  5. 「ノード内の可視化を有効にする」のチェックボックスをオン
  1. その他の設定を完了後、「作成」をクリック

VPCフローログの確認と設定

  1. 「VPCネットワーク」→「VPCネットワーク」に移動
  2. GKEクラスタが使用しているVPCネットワークを選択
  3. 「サブネット」タブでGKEが使用しているサブネットを選択
  4. 「フローログ」が「オン」になっていることを確認
  5. 無効の場合は「編集」→「フローログ」を「オン」に設定

ログの確認方法

  1. 「ロギング」→「ログエクスプローラー」に移動
  2. リソースタイプで「GKE Cluster」を選択
  3. ログ名で「vpc_flows」を選択
  4. 以下のようなクエリでPod間通信を確認:
    resource.type="gke_cluster"
    logName="projects/PROJECT_ID/logs/vpc_flows"
    jsonPayload.src_gke_details.pod.name!=""
    jsonPayload.dest_gke_details.pod.name!=""
    

     

gcloud CLIでの修復手順

コマンドラインからIntranode Visibilityを設定する手順です。

1. 環境変数の設定

# プロジェクトとクラスタ情報の設定
export PROJECT_ID="your-project-id"
export CLUSTER_NAME="your-cluster-name"
export ZONE="asia-northeast1-a"  # またはリージョン
export REGION="asia-northeast1"

# 認証とプロジェクト設定
gcloud auth login
gcloud config set project ${PROJECT_ID}

 

2. 現在のクラスタ設定の確認

# クラスタの詳細情報を取得
gcloud container clusters describe ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --format="yaml(name,network,subnetwork,privateClusterConfig,enableIntraNodeVisibility)"

# Intranode Visibilityの状態を確認
gcloud container clusters describe ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --format="value(enableIntraNodeVisibility)"

 

3. 既存クラスタでIntranode Visibilityを有効化

# Intranode Visibilityを有効化
gcloud container clusters update ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --enable-intra-node-visibility

# 更新の進行状況を確認
gcloud container operations list \
  --filter="TYPE:UPDATE_CLUSTER AND targetLink~${CLUSTER_NAME}" \
  --format="table(name,operationType,status,startTime,endTime)"

 

4. VPCフローログの有効化

# クラスタが使用しているサブネットを取得
SUBNET_NAME=$(gcloud container clusters describe ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --format="value(subnetwork)")

# サブネットでフローログを有効化
gcloud compute networks subnets update ${SUBNET_NAME} \
  --region=${REGION} \
  --enable-flow-logs \
  --logging-aggregation-interval="INTERVAL_5_SEC" \
  --logging-flow-sampling=1.0 \
  --logging-metadata="INCLUDE_ALL_METADATA"

 

5. 新規クラスタ作成時のIntranode Visibility設定

# VPCネットワークの作成
gcloud compute networks create gke-vpc \
  --subnet-mode=custom \
  --bgp-routing-mode=regional

# サブネットの作成(フローログ有効)
gcloud compute networks subnets create gke-subnet \
  --network=gke-vpc \
  --region=${REGION} \
  --range=10.0.0.0/20 \
  --secondary-range=pod-range=10.4.0.0/14,service-range=10.8.0.0/20 \
  --enable-flow-logs \
  --logging-aggregation-interval="INTERVAL_5_SEC" \
  --logging-flow-sampling=0.5 \
  --logging-metadata="INCLUDE_ALL_METADATA"

# Intranode Visibility有効のGKEクラスタ作成
gcloud container clusters create ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --network=gke-vpc \
  --subnetwork=gke-subnet \
  --enable-intra-node-visibility \
  --enable-ip-alias \
  --cluster-secondary-range-name=pod-range \
  --services-secondary-range-name=service-range \
  --enable-network-policy \
  --enable-cloud-logging \
  --logging=SYSTEM,WORKLOADS,API_SERVER \
  --enable-cloud-monitoring \
  --monitoring=SYSTEM,API_SERVER,CONTROLLER_MANAGER,SCHEDULER \
  --num-nodes=3

 

6. ログの確認とクエリ

# Pod間通信ログの確認
gcloud logging read \
  'resource.type="gke_cluster"
  logName="projects/'${PROJECT_ID}'/logs/vpc_flows"
  jsonPayload.src_gke_details.pod.name!=""
  jsonPayload.dest_gke_details.pod.name!=""' \
  --limit=50 \
  --format=json | jq '.[].jsonPayload | {
    timestamp: .timestamp,
    src_pod: .src_gke_details.pod.name,
    src_namespace: .src_gke_details.pod.namespace.name,
    dest_pod: .dest_gke_details.pod.name,
    dest_namespace: .dest_gke_details.pod.namespace.name,
    bytes_sent: .bytes_sent,
    packets_sent: .packets_sent
  }'

# 特定のPod名でフィルタ
gcloud logging read \
  'resource.type="gke_cluster"
  logName="projects/'${PROJECT_ID}'/logs/vpc_flows"
  (jsonPayload.src_gke_details.pod.name="frontend" OR
   jsonPayload.dest_gke_details.pod.name="frontend")' \
  --limit=20

 

7. ログベースメトリクスの作成

# Pod間通信量のメトリクス作成
gcloud logging metrics create pod_traffic_volume \
  --description="Total bytes transferred between pods" \
  --log-filter='resource.type="gke_cluster"
    logName="projects/'${PROJECT_ID}'/logs/vpc_flows"
    jsonPayload.src_gke_details.pod.name!=""
    jsonPayload.dest_gke_details.pod.name!=""' \
  --value-extractor='EXTRACT(jsonPayload.bytes_sent)' \
  --metric-kind=DELTA \
  --value-type=INT64

 

Terraformでの修復手順

GKEクラスタでIntranode Visibilityを有効にするTerraformコードと、主要な修正ポイントを説明します。

# -------------------- ① VPCネットワーク設定 --------------------
resource "google_compute_network" "gke_vpc" {
  name                    = "gke-vpc-${var.environment}"
  auto_create_subnetworks = false
  project                 = var.project_id
}

# -------------------- ② サブネット設定(フローログ有効) --------------------
resource "google_compute_subnetwork" "gke_subnet" {
  name          = "gke-subnet-${var.environment}"
  ip_cidr_range = "10.0.0.0/20"
  region        = var.region
  network       = google_compute_network.gke_vpc.id
  project       = var.project_id

  # Pod用のセカンダリレンジ
  secondary_ip_range {
    range_name    = "pod-range"
    ip_cidr_range = "10.4.0.0/14"
  }

  # Service用のセカンダリレンジ
  secondary_ip_range {
    range_name    = "service-range"
    ip_cidr_range = "10.8.0.0/20"
  }

  # VPCフローログの有効化(重要)
  log_config {
    aggregation_interval = "INTERVAL_5_SEC"
    flow_sampling        = 1.0  # 100%サンプリング(本番環境では調整推奨)
    metadata             = "INCLUDE_ALL_METADATA"
    metadata_fields      = []
  }

  private_ip_google_access = true
}

# -------------------- ③ Intranode Visibility対応GKEクラスタ --------------------
resource "google_container_cluster" "primary" {
  name     = "gke-cluster-${var.environment}"
  location = var.region
  project  = var.project_id

  # 初期設定
  initial_node_count       = 1
  remove_default_node_pool = true

  # ネットワーク設定
  network    = google_compute_network.gke_vpc.name
  subnetwork = google_compute_subnetwork.gke_subnet.name

  # Intranode Visibilityの有効化(重要)
  enable_intranode_visibility = true

  # IPアドレス割り当て
  ip_allocation_policy {
    cluster_secondary_range_name  = "pod-range"
    services_secondary_range_name = "service-range"
  }

  # プライベートクラスタ設定
  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false
    master_ipv4_cidr_block  = "172.16.0.0/28"

    master_global_access_config {
      enabled = true
    }
  }

  # ネットワークポリシー有効化(推奨)
  network_policy {
    enabled  = true
    provider = "CALICO"
  }

  # ロギング設定(フローログ含む)
  logging_config {
    enable_components = [
      "SYSTEM_COMPONENTS",
      "WORKLOADS",
      "APISERVER",
      "CONTROLLER_MANAGER",
      "SCHEDULER"
    ]
  }

  # モニタリング設定
  monitoring_config {
    enable_components = [
      "SYSTEM_COMPONENTS",
      "APISERVER",
      "CONTROLLER_MANAGER",
      "SCHEDULER"
    ]

    managed_prometheus {
      enabled = true
    }
  }

  # セキュリティ設定
  binary_authorization {
    evaluation_mode = "PROJECT_SINGLETON_POLICY_ENFORCE"
  }

  # Workload Identity
  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }
}

# -------------------- ④ ノードプール設定 --------------------
resource "google_container_node_pool" "primary_pool" {
  name       = "primary-pool-${var.environment}"
  location   = var.region
  cluster    = google_container_cluster.primary.name
  project    = var.project_id

  # ノード数設定
  node_count = var.node_count

  # オートスケーリング
  autoscaling {
    min_node_count = var.min_nodes
    max_node_count = var.max_nodes
  }

  # ノード設定
  node_config {
    machine_type = var.machine_type
    disk_size_gb = 100
    disk_type    = "pd-standard"

    # メタデータ設定(セキュリティ強化)
    metadata = {
      disable-legacy-endpoints = "true"
      block-project-ssh-keys   = "true"
    }

    # サービスアカウント
    service_account = google_service_account.gke_node_sa.email
    oauth_scopes = [
      "<https://www.googleapis.com/auth/cloud-platform>"
    ]

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

    # ラベル
    labels = {
      environment = var.environment
      cluster     = google_container_cluster.primary.name
    }

    # タグ(ファイアウォールルール用)
    tags = ["gke-${var.environment}"]
  }

  # 管理設定
  management {
    auto_repair  = true
    auto_upgrade = true
  }
}

# -------------------- ⑤ サービスアカウント --------------------
resource "google_service_account" "gke_node_sa" {
  account_id   = "gke-node-sa-${var.environment}"
  display_name = "GKE Node Service Account"
  project      = var.project_id
}

# 必要な権限を付与
resource "google_project_iam_member" "gke_node_permissions" {
  for_each = toset([
    "roles/logging.logWriter",
    "roles/monitoring.metricWriter",
    "roles/monitoring.viewer",
    "roles/stackdriver.resourceMetadata.writer"
  ])

  project = var.project_id
  role    = each.value
  member  = "serviceAccount:${google_service_account.gke_node_sa.email}"
}

# -------------------- ⑥ ログ分析用のログシンク --------------------
resource "google_logging_project_sink" "gke_flows" {
  name        = "gke-intranode-flows-${var.environment}"
  destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.flow_logs.dataset_id}"
  project     = var.project_id

  # Pod間通信のフローログのみをフィルタ
  filter = <<-EOT
    resource.type="gke_cluster"
    resource.labels.cluster_name="${google_container_cluster.primary.name}"
    logName="projects/${var.project_id}/logs/vpc_flows"
    jsonPayload.src_gke_details.pod.name!=""
    jsonPayload.dest_gke_details.pod.name!=""
  EOT

  unique_writer_identity = true
}

# -------------------- ⑦ 分析用BigQueryデータセット --------------------
resource "google_bigquery_dataset" "flow_logs" {
  dataset_id                  = "gke_flow_logs_${var.environment}"
  friendly_name               = "GKE Flow Logs"
  description                 = "Storage for GKE intranode visibility flow logs"
  location                    = var.region
  default_table_expiration_ms = 2592000000  # 30日

  access {
    role          = "OWNER"
    user_by_email = var.admin_email
  }

  access {
    role          = "WRITER"
    user_by_email = google_logging_project_sink.gke_flows.writer_identity
  }
}

# -------------------- ⑧ 監視アラート設定 --------------------
resource "google_monitoring_alert_policy" "suspicious_pod_traffic" {
  display_name = "Suspicious Pod Traffic - ${var.environment}"
  project      = var.project_id

  documentation {
    content = "Detected unusual traffic patterns between pods in GKE cluster"
  }

  conditions {
    display_name = "High volume pod-to-pod traffic"
    condition_threshold {
      filter = <<-EOT
        resource.type = "gke_cluster"
        resource.labels.cluster_name = "${google_container_cluster.primary.name}"
        metric.type = "logging.googleapis.com/user/pod_traffic_bytes"
      EOT

      comparison      = "COMPARISON_GT"
      threshold_value = 1000000000  # 1GB
      duration        = "300s"

      aggregations {
        alignment_period     = "60s"
        per_series_aligner   = "ALIGN_RATE"
        cross_series_reducer = "REDUCE_SUM"
        group_by_fields      = ["metadata.user_labels.src_pod", "metadata.user_labels.dest_pod"]
      }
    }
  }

  notification_channels = var.notification_channels
  alert_strategy {
    auto_close = "1800s"
  }
}

# -------------------- ⑨ ファイアウォールルール --------------------
resource "google_compute_firewall" "allow_gke_internal" {
  name    = "allow-gke-internal-${var.environment}"
  network = google_compute_network.gke_vpc.name
  project = var.project_id

  allow {
    protocol = "tcp"
  }

  allow {
    protocol = "udp"
  }

  allow {
    protocol = "icmp"
  }

  source_ranges = [
    google_compute_subnetwork.gke_subnet.ip_cidr_range,
    google_compute_subnetwork.gke_subnet.secondary_ip_range[0].ip_cidr_range
  ]

  target_tags = ["gke-${var.environment}"]
}

# -------------------- ⑩ ログメトリクス作成 --------------------
resource "google_logging_metric" "pod_traffic_bytes" {
  name    = "pod_traffic_bytes"
  project = var.project_id

  filter = <<-EOT
    resource.type="gke_cluster"
    logName="projects/${var.project_id}/logs/vpc_flows"
    jsonPayload.src_gke_details.pod.name!=""
    jsonPayload.dest_gke_details.pod.name!=""
  EOT

  metric_descriptor {
    metric_kind = "DELTA"
    value_type  = "INT64"
    unit        = "By"
    labels {
      key         = "src_pod"
      value_type  = "STRING"
      description = "Source pod name"
    }
    labels {
      key         = "dest_pod"
      value_type  = "STRING"
      description = "Destination pod name"
    }
  }

  value_extractor = "EXTRACT(jsonPayload.bytes_sent)"

  label_extractors = {
    "src_pod"  = "EXTRACT(jsonPayload.src_gke_details.pod.name)"
    "dest_pod" = "EXTRACT(jsonPayload.dest_gke_details.pod.name)"
  }
}

 

Terraform実装時の重要なポイント:

  1. VPCフローログの有効化: サブネットレベルでフローログを有効化し、メタデータを含める
  2. クラスタでの有効化: enable_intranode_visibility = trueを明示的に設定
  3. ログの保存と分析: BigQueryへのエクスポートを設定して長期保存と分析を可能に
  4. 監視とアラート: 異常なPod間通信を検知するアラートを設定
  5. サンプリング率の調整: 本番環境では適切なサンプリング率(0.1〜0.5)に調整してコストを最適化
  6. ネットワークポリシー: Calicoを有効化してPod間通信を制御

修復後の確認方法

1. Intranode Visibility有効化の確認

# クラスタ設定の確認
gcloud container clusters describe ${CLUSTER_NAME} \
  --zone=${ZONE} \
  --format="table(name,enableIntraNodeVisibility)"

# 期待される出力
# NAME           ENABLE_INTRA_NODE_VISIBILITY
# your-cluster   True

 

2. フローログの動作確認

# テスト用のPodをデプロイ
kubectl create deployment nginx --image=nginx --replicas=2
kubectl expose deployment nginx --port=80

# 別のPodから通信テスト
kubectl run -it --rm debug --image=busybox --restart=Never -- \
  wget -O- <http://nginx:80>

# 5分待機後、ログを確認
sleep 300
gcloud logging read \
  'resource.type="gke_cluster"
  jsonPayload.dest_gke_details.pod.name="nginx"' \
  --limit=10

 

3. メトリクスの確認

# Cloud Monitoringでメトリクスを確認
gcloud monitoring time-series list \
  --filter='metric.type="logging.googleapis.com/user/pod_traffic_volume"' \
  --interval-end-time=now \
  --interval-start-time=-10m

 

ログの分析と活用方法

1. 異常な通信パターンの検出

-- BigQueryでの分析例
WITH pod_communications AS (
  SELECT
    TIMESTAMP(jsonPayload.timestamp) as flow_time,
    jsonPayload.src_gke_details.pod.name as src_pod,
    jsonPayload.src_gke_details.pod.namespace.name as src_namespace,
    jsonPayload.dest_gke_details.pod.name as dest_pod,
    jsonPayload.dest_gke_details.pod.namespace.name as dest_namespace,
    CAST(jsonPayload.bytes_sent AS INT64) as bytes_sent,
    CAST(jsonPayload.packets_sent AS INT64) as packets_sent
  FROM
    `{project_id}.{dataset_id}.vpc_flows`
  WHERE
    DATE(timestamp) = CURRENT_DATE()
    AND jsonPayload.src_gke_details.pod.name IS NOT NULL
)
SELECT
  src_namespace,
  src_pod,
  dest_namespace,
  dest_pod,
  COUNT(*) as connection_count,
  SUM(bytes_sent) as total_bytes,
  AVG(bytes_sent) as avg_bytes_per_flow
FROM
  pod_communications
GROUP BY
  1, 2, 3, 4
HAVING
  total_bytes > 1000000000  -- 1GB以上
ORDER BY
  total_bytes DESC

 

2. セキュリティアラートの設定

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: pod-traffic-anomaly
spec:
  groups:
  - name: pod_security
    rules:
    - alert: UnauthorizedPodCommunication
      expr: |
        rate(pod_traffic_volume[5m]) > 1000000
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Unusual pod communication detected"
        description: "Pod {{ $labels.src_pod }} is sending high volume of traffic"

 

注意事項について

フローログにおよるコストが発生します。ストレージコストは1GBあたり約$0.01/月です。1,000 Pod規模のクラスタで、50%サンプリングの場合、月額$50-100程度が目安です。

環境に応じて利用有無を決めてコストの最適化を実現することが求められます。

まとめ

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

参考情報

Google Cloud公式ドキュメント

セキュリティフレームワーク準拠

関連記事

 

この記事をシェアする

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

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

料金プランを詳しく見る