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

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

ポリシーの説明
GKEクラスタが限定公開(プライベート)クラスタとして構成されていない場合、ノードがパブリックIPアドレスを持ち、インターネットから直接アクセス可能になります。限定公開クラスタを使用することで、ノードを内部IPアドレスのみに制限し、インターネットからの直接的な攻撃を防ぎ、より安全なKubernetes環境を構築できます。
以下のリスクがあります。
- ノードがパブリックIPを持つため、インターネットから直接攻撃を受ける可能性があります。
- ノードのメタデータサービス(169.254.169.254)が外部に露出し、認証情報やAPIキーが漏洩する可能性があります
- パブリックIPを持つノードはDDoS攻撃の標的になりやすくなります
GKEで限定公開クラスタを構成する3つの方法
⚠️ 重要な注意事項
既存のパブリッククラスタを限定公開クラスタに変更することはできません。新しいクラスタの作成とワークロードの移行が必要です。
コンソールでの修復手順
Google Cloud コンソールを使用して、新しい限定公開クラスタを作成する手順を説明します。
新規クラスタの作成
- Google Cloud Consoleにログインし、「Kubernetes Engine」→「クラスタ」を選択します

- 「作成」ボタンをクリックし、「GKE Standard」を選択します
- 基本設定でクラスタ名とゾーン/リージョンを設定します
- 左側のメニューから「ネットワーキング」を選択します
- 「限定公開クラスタ」セクションで以下を設定:
- 「限定公開クラスタを有効にする」にチェック
- 「外部IPアドレスを使用したアクセス」オプションを選択(推奨)
- 「コントロールプレーンのIPアドレス範囲」に適切なCIDR(例:172.16.0.0/28)を入力
- 「承認済みネットワーク」セクションで:
- 「承認済みネットワークを有効にする」にチェック
- 管理者アクセス用のIPアドレス範囲を追加(例:オフィスのIPアドレス)
- その他の必要な設定を行い、「作成」をクリックします
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アドレス範囲
}
]
}
主要な設定ポイント
- enable_private_nodes = true: ノードに内部IPアドレスのみを割り当て
- Cloud NAT設定: ノードからの外部通信(パッケージダウンロード等)を可能にする
- 承認済みネットワーク: マスターAPIへのアクセスを特定のIPアドレスに制限
- プライベートGoogleアクセス: Google APIへの内部アクセスを有効化
- 適切なファイアウォールルール: ヘルスチェックなど必要最小限の通信のみ許可
最後に
この記事では、GKEクラスタを限定公開クラスタとして構成し、インターネットから分離する方法について、リスクと対策を解説しました。
限定公開クラスタを使用することで、ノードをインターネットから完全に分離し、直接的な攻撃のリスクを大幅に削減できます。Cloud NATや承認済みネットワークの適切な設定により、セキュリティを維持しながら必要なアクセスを確保できます。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。