GKEクラスタでの限定公開クラスタ設定手順

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

この記事では、GKEクラスタの限定公開クラスタ設定手順について、リスクと対策を解説します。

ポリシーの説明

GKEクラスタが限定公開(プライベート)クラスタとして構成されていない場合、ノードがパブリックIPアドレスを持ち、インターネットから直接アクセス可能になります。限定公開クラスタを使用することで、ノードを内部IPアドレスのみに制限し、インターネットからの直接的な攻撃を防ぎ、より安全なKubernetes環境を構築できます。

以下のリスクがあります。

  • ノードがパブリックIPを持つため、インターネットから直接攻撃を受ける可能性があります。
  • ノードのメタデータサービス(169.254.169.254)が外部に露出し、認証情報やAPIキーが漏洩する可能性があります
  • パブリックIPを持つノードはDDoS攻撃の標的になりやすくなります

GKEで限定公開クラスタを構成する3つの方法

⚠️ 重要な注意事項
既存のパブリッククラスタを限定公開クラスタに変更することはできません。新しいクラスタの作成とワークロードの移行が必要です。

コンソールでの修復手順

Google Cloud コンソールを使用して、新しい限定公開クラスタを作成する手順を説明します。

新規クラスタの作成

  1. Google Cloud Consoleにログインし、「Kubernetes Engine」→「クラスタ」を選択します
  1. 「作成」ボタンをクリックし、「GKE Standard」を選択します
  2. 基本設定でクラスタ名とゾーン/リージョンを設定します
  3. 左側のメニューから「ネットワーキング」を選択します
  4. 「限定公開クラスタ」セクションで以下を設定:
    • 「限定公開クラスタを有効にする」にチェック
    • 「外部IPアドレスを使用したアクセス」オプションを選択(推奨)
    • 「コントロールプレーンのIPアドレス範囲」に適切なCIDR(例:172.16.0.0/28)を入力
  5. 「承認済みネットワーク」セクションで:
    • 「承認済みネットワークを有効にする」にチェック
    • 管理者アクセス用のIPアドレス範囲を追加(例:オフィスのIPアドレス)
  6. その他の必要な設定を行い、「作成」をクリックします

Terraformでの修復手順

限定公開クラスタを作成するTerraformコードと、主要な設定ポイントを説明します。

# 限定公開GKEクラスタの設定

# VPCネットワーク
resource "google_compute_network" "vpc" {
  name                    = "gke-private-vpc"
  auto_create_subnetworks = false
  project                 = var.project_id
}

# サブネット(ノード用)
resource "google_compute_subnetwork" "subnet" {
  name          = "gke-private-subnet"
  ip_cidr_range = "10.0.0.0/24"
  region        = var.region
  network       = google_compute_network.vpc.id
  project       = var.project_id

  # セカンダリ範囲(Pod用)
  secondary_ip_range {
    range_name    = "gke-pods"
    ip_cidr_range = "10.1.0.0/16"
  }

  # セカンダリ範囲(Service用)
  secondary_ip_range {
    range_name    = "gke-services"
    ip_cidr_range = "10.2.0.0/20"
  }

  # プライベートGoogleアクセスを有効化
  private_ip_google_access = true
}

# Cloud NATルーター(外部通信用)
resource "google_compute_router" "router" {
  name    = "gke-nat-router"
  region  = var.region
  network = google_compute_network.vpc.id
  project = var.project_id
}

# Cloud NAT(ノードからの外部通信を可能にする)
resource "google_compute_router_nat" "nat" {
  name                               = "gke-nat"
  router                             = google_compute_router.router.name
  region                             = var.region
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"

  subnetwork {
    name                    = google_compute_subnetwork.subnet.id
    source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
  }

  log_config {
    enable = true
    filter = "ERRORS_ONLY"
  }

  project = var.project_id
}

# 限定公開GKEクラスタ
resource "google_container_cluster" "primary" {
  name     = "secure-private-cluster"
  location = var.region
  project  = var.project_id

  # 初期ノード数(後でnode_poolで管理)
  initial_node_count       = 1
  remove_default_node_pool = true

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

  # 限定公開クラスタ設定
  private_cluster_config {
    enable_private_nodes    = true  # ノードに内部IPのみを割り当て
    enable_private_endpoint = false # マスターAPIにパブリックIPも割り当て(管理用)
    master_ipv4_cidr_block = "172.16.0.0/28" # マスター用のCIDR

    # グローバルアクセス設定(他リージョンからのアクセスを許可)
    master_global_access_config {
      enabled = true
    }
  }

  # 承認済みネットワーク(マスターAPIへのアクセス制限)
  master_authorized_networks_config {
    dynamic "cidr_blocks" {
      for_each = var.authorized_networks
      content {
        cidr_block   = cidr_blocks.value.cidr
        display_name = cidr_blocks.value.name
      }
    }
  }

  # IP割り当てポリシー
  ip_allocation_policy {
    cluster_secondary_range_name  = "gke-pods"
    services_secondary_range_name = "gke-services"
  }

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

  # その他のセキュリティ設定
  database_encryption {
    state    = "ENCRYPTED"
    key_name = google_kms_crypto_key.gke.id
  }

  # ロギングとモニタリング
  logging_config {
    enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"]
  }

  monitoring_config {
    enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"]

    managed_prometheus {
      enabled = true
    }
  }

  # メンテナンスポリシー
  maintenance_policy {
    daily_maintenance_window {
      start_time = "03:00"
    }
  }

  # リリースチャンネル
  release_channel {
    channel = "REGULAR"
  }
}

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

  node_config {
    preemptible  = false
    machine_type = var.machine_type

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

    # ラベル
    labels = {
      env = var.environment
    }

    # タグ(ファイアウォールルール用)
    tags = ["gke-private-node"]

    # シールドインスタンス設定
    shielded_instance_config {
      enable_secure_boot          = true
      enable_integrity_monitoring = true
    }

    # メタデータ設定
    metadata = {
      disable-legacy-endpoints = "true"
    }

    # ワークロードメタデータ
    workload_metadata_config {
      mode = "GKE_METADATA"
    }
  }

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

  # アップグレード設定
  upgrade_settings {
    max_surge       = 1
    max_unavailable = 0
  }
}

# ノード用サービスアカウント
resource "google_service_account" "gke_node" {
  account_id   = "gke-private-node-sa"
  display_name = "GKE Private Node Service Account"
  project      = var.project_id
}

# 必要なIAMロール
resource "google_project_iam_member" "gke_node_log_writer" {
  project = var.project_id
  role    = "roles/logging.logWriter"
  member  = "serviceAccount:${google_service_account.gke_node.email}"
}

resource "google_project_iam_member" "gke_node_metric_writer" {
  project = var.project_id
  role    = "roles/monitoring.metricWriter"
  member  = "serviceAccount:${google_service_account.gke_node.email}"
}

resource "google_project_iam_member" "gke_node_monitoring_viewer" {
  project = var.project_id
  role    = "roles/monitoring.viewer"
  member  = "serviceAccount:${google_service_account.gke_node.email}"
}

# KMS暗号化キー
resource "google_kms_key_ring" "gke" {
  name     = "gke-encryption-keyring"
  location = var.region
  project  = var.project_id
}

resource "google_kms_crypto_key" "gke" {
  name            = "gke-encryption-key"
  key_ring        = google_kms_key_ring.gke.id
  rotation_period = "86400s"

  lifecycle {
    prevent_destroy = true
  }
}

# ファイアウォールルール(必要最小限)
resource "google_compute_firewall" "allow_health_checks" {
  name    = "allow-gke-health-checks"
  network = google_compute_network.vpc.name
  project = var.project_id

  allow {
    protocol = "tcp"
    ports    = ["80", "443"]
  }

  source_ranges = [
    "35.191.0.0/16",  # Google health checks
    "130.211.0.0/22"  # Google health checks
  ]

  target_tags = ["gke-private-node"]
}

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

variable "region" {
  description = "GCP Region"
  type        = string
  default     = "us-central1"
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "production"
}

variable "node_count" {
  description = "Number of nodes per zone"
  type        = number
  default     = 2
}

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

variable "authorized_networks" {
  description = "List of authorized networks"
  type = list(object({
    name = string
    cidr = string
  }))
  default = [
    {
      name = "office-network"
      cidr = "203.0.113.0/24"  # 例:オフィスのIPアドレス範囲
    }
  ]
}

 

主要な設定ポイント

  1. enable_private_nodes = true: ノードに内部IPアドレスのみを割り当て
  2. Cloud NAT設定: ノードからの外部通信(パッケージダウンロード等)を可能にする
  3. 承認済みネットワーク: マスターAPIへのアクセスを特定のIPアドレスに制限
  4. プライベートGoogleアクセス: Google APIへの内部アクセスを有効化
  5. 適切なファイアウォールルール: ヘルスチェックなど必要最小限の通信のみ許可

最後に

この記事では、GKEクラスタを限定公開クラスタとして構成し、インターネットから分離する方法について、リスクと対策を解説しました。

限定公開クラスタを使用することで、ノードをインターネットから完全に分離し、直接的な攻撃のリスクを大幅に削減できます。Cloud NATや承認済みネットワークの適切な設定により、セキュリティを維持しながら必要なアクセスを確保できます。

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

参考情報

この記事をシェアする

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

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

料金プランを詳しく見る