GKEレガシーメタデータエンドポイントの無効化について

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

この記事では、Google Kubernetes Engine (GKE) のレガシーメタデータエンドポイント(v0.1/v1beta1)が持つセキュリティリスクと、それを無効化してSSRF攻撃や権限昇格からクラスタを保護する方法について、実践的な設定手順とベストプラクティスを解説します。

レガシーメタデータエンドポイントとは

GKEクラスタのメタデータサービスには、レガシーエンドポイント(v0.1/v1beta1)と現行のv1エンドポイントが存在します。レガシーエンドポイントは2020年9月30日に廃止されましたが、互換性のために有効のままになっている場合があります。

レガシーエンドポイントの特徴

  • 認証ヘッダー不要: Metadata-Flavor: Googleヘッダーを必要としない
  • URLパス: http://metadata.google.internal/computeMetadata/v1beta1/
  • kube-envファイルへのアクセス: kubeletトークンやCA証明書が含まれる
  • サービスアカウントトークン: ノードレベルの権限を持つトークン

具体的なリスクと影響

  1. 認証なしでのアクセス(SSRF攻撃)
    • レガシーエンドポイントはMetadata-Flavor: Googleヘッダー不要
    • SSRF脆弱性の悪用が可能

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、レガシーメタデータエンドポイントを無効化します。

重要: 既存のノードプールでは直接無効化できないため、新しいノードプールを作成する必要があります。

  1. Google Cloud Consoleにログイン
  2. Kubernetes Engineセクションに移動
    • 左側のナビゲーションメニューから「Kubernetes Engine」→「クラスタ」を選択
    • 対象のクラスタ名をクリック
  3. 新しいノードプールを作成
    • 「ノードプール」タブを選択
    • 「ノードプールを追加」ボタンをクリック
  4. ノードプールの設定
    • 基本情報(名前、ノード数など)を設定
    • 「ノード」セクションを展開
    • 「メタデータ」セクションまでスクロール
  5. レガシーエンドポイントを無効化

    ※ 新規作成する場合はデフォルトで適用されているはずです

    • 「メタデータ」セクションで「項目を追加」をクリック
    • キー: disable-legacy-endpoints
    • 値: true
  6. ノードプールの作成を完了
    • その他の必要な設定を行い、「作成」をクリック
  7. ワークロードを移行
    • 新しいノードプールが作成されたら、既存のワークロードを新しいノードプールに移行
    • 古いノードプールを削除

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

  # ワークロードアイデンティティを有効化(推奨)
  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }

  # その他のセキュリティ設定
  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false
    master_ipv4_cidr_block  = "172.16.0.0/28"
  }
}

# 管理されたノードプール(レガシーエンドポイント無効化)
resource "google_container_node_pool" "primary_nodes" {
  name       = "primary-node-pool"
  location   = var.region
  cluster    = google_container_cluster.primary.name
  node_count = var.node_count

  node_config {
    preemptible  = false
    machine_type = var.machine_type

    # レガシーメタデータエンドポイントを無効化
    metadata = {
      disable-legacy-endpoints = "true"
    }

    # ワークロードアイデンティティの設定
    workload_metadata_config {
      mode = "GKE_METADATA"
    }

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

    # Shielded VMの設定(推奨)
    shielded_instance_config {
      enable_secure_boot          = true
      enable_integrity_monitoring = true
    }
  }

  # 自動修復と自動アップグレードを有効化
  management {
    auto_repair  = true
    auto_upgrade = true
  }
}

# ノードプール用のサービスアカウント
resource "google_service_account" "kubernetes" {
  account_id   = "gke-node-sa"
  display_name = "GKE Node Service Account"
}

# 必要最小限の権限を付与
resource "google_project_iam_member" "node_service_account" {
  for_each = toset([
    "roles/logging.logWriter",
    "roles/monitoring.metricWriter",
    "roles/monitoring.viewer",
  ])

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

 

既存クラスタの確認方法

現在のクラスタ設定を確認するには、以下のコマンドを使用します:

# クラスタのメタデータ設定を確認
gcloud container clusters describe [CLUSTER_NAME] \
  --zone [ZONE] \
  --format="value(nodeConfig.metadata)"

# ノードプールごとの設定を確認
gcloud container node-pools describe [NODE_POOL_NAME] \
  --cluster=[CLUSTER_NAME] \
  --zone=[ZONE] \
  --format="value(config.metadata)"

 

disable-legacy-endpoints: trueが設定されていることを確認してください。

gcloud CLIでの修復手順

コマンドラインからレガシーメタデータエンドポイントを無効化する手順です。

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="value(nodeConfig.metadata)"

# 各ノードプールの設定を確認
for pool in $(gcloud container node-pools list --cluster=${CLUSTER_NAME} --zone=${ZONE} --format="value(name)"); do
  echo "Node pool: $pool"
  gcloud container node-pools describe $pool \
    --cluster=${CLUSTER_NAME} \
    --zone=${ZONE} \
    --format="value(config.metadata)"
done

 

3. 新しいノードプールの作成(レガシーエンドポイント無効)

# 新しいノードプールの作成
gcloud container node-pools create secure-pool \
  --cluster=${CLUSTER_NAME} \
  --zone=${ZONE} \
  --num-nodes=3 \
  --machine-type=e2-standard-4 \
  --disk-type=pd-standard \
  --disk-size=100 \
  --metadata="disable-legacy-endpoints=true" \
  --workload-metadata=GKE_METADATA \
  --shielded-secure-boot \
  --shielded-integrity-monitoring \
  --enable-autoupgrade \
  --enable-autorepair

# 作成状態の確認
gcloud container operations list \
  --filter="TYPE:CREATE_NODE_POOL AND targetLink~${CLUSTER_NAME}" \
  --format="table(name,operationType,status,startTime,endTime)"

 

4. ワークロードの移行

# 古いノードプールのノードをコードン
# (新しいノードにポッドが再スケジュールされる)
OLD_POOL="default-pool"  # 古いプール名に置き換え

for node in $(kubectl get nodes -l cloud.google.com/gke-nodepool=${OLD_POOL} -o name); do
  echo "Cordoning $node"
  kubectl cordon $node
  echo "Draining $node"
  kubectl drain $node --ignore-daemonsets --delete-emptydir-data --force
done

# ポッドが新しいノードに移動したことを確認
kubectl get pods --all-namespaces -o wide | grep secure-pool

 

5. 古いノードプールの削除

# ワークロードが完全に移行されたことを確認後
gcloud container node-pools delete ${OLD_POOL} \
  --cluster=${CLUSTER_NAME} \
  --zone=${ZONE} \
  --quiet

 

6. 新規クラスタ作成時の設定

# レガシーエンドポイント無効のGKEクラスタ作成
gcloud container clusters create secure-cluster \
  --zone=${ZONE} \
  --num-nodes=3 \
  --machine-type=e2-standard-4 \
  --disk-type=pd-standard \
  --disk-size=100 \
  --metadata="disable-legacy-endpoints=true" \
  --workload-metadata=GKE_METADATA \
  --enable-workload-identity \
  --enable-shielded-nodes \
  --shielded-secure-boot \
  --shielded-integrity-monitoring \
  --enable-ip-alias \
  --enable-private-nodes \
  --enable-private-endpoint \
  --master-ipv4-cidr=172.16.0.0/28 \
  --enable-network-policy \
  --enable-autoupgrade \
  --enable-autorepair \
  --release-channel=regular

 

7. メタデータサービスへのアクセステスト

# テスト用Podをデプロイ
kubectl run test-pod --image=busybox --restart=Never -- sleep 3600

# Pod内からレガシーエンドポイントへのアクセスをテスト
kubectl exec test-pod -- wget -qO- -T 2 \
  <http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token>

# アクセスが拒否されることを確認(404エラーが返るはず)

# テストPodの削除
kubectl delete pod test-pod

 

修復後の確認方法

1. ノードプール設定の確認

# 全ノードプールのメタデータ設定を一括確認
for pool in $(gcloud container node-pools list --cluster=${CLUSTER_NAME} --zone=${ZONE} --format="value(name)"); do
  echo "=== Node pool: $pool ==="
  gcloud container node-pools describe $pool \
    --cluster=${CLUSTER_NAME} \
    --zone=${ZONE} \
    --format="yaml(config.metadata)" | grep -A1 disable-legacy-endpoints
done

 

2. メタデータサービスへのアクセステスト

# テスト用のポッドを作成
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: metadata-test
spec:
  containers:
  - name: test
    image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
    command: ["sleep", "3600"]
  restartPolicy: Never
EOF

# レガシーエンドポイントへのアクセステスト
echo "Testing legacy endpoints (should fail):"
kubectl exec metadata-test -- curl -s -f \
  <http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token> || echo "✓ Access denied (expected)"

# 現行のv1エンドポイントへのアクセステスト(ヘッダー付き)
echo "\nTesting v1 endpoint with header (should succeed):"
kubectl exec metadata-test -- curl -s -H "Metadata-Flavor: Google" \
  <http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email>

# テストポッドの削除
kubectl delete pod metadata-test

 

3. セキュリティ監査スクリプト

#!/bin/bash
# check-legacy-endpoints.sh

echo "GKE Legacy Endpoints Security Audit"
echo "===================================="

# クラスタ一覧を取得
CLUSTERS=$(gcloud container clusters list --format="value(name,zone)")

while IFS=$'\t' read -r cluster zone; do
  echo "\nCluster: $cluster (Zone: $zone)"

  # ノードプールをチェック
  pools=$(gcloud container node-pools list --cluster=$cluster --zone=$zone --format="value(name)")

  for pool in $pools; do
    metadata=$(gcloud container node-pools describe $pool \
      --cluster=$cluster --zone=$zone \
      --format="value(config.metadata[disable-legacy-endpoints])")

    if [[ "$metadata" == "true" ]]; then
      echo "  ✓ Node pool '$pool': Legacy endpoints DISABLED"
    else
      echo "  ✗ Node pool '$pool': Legacy endpoints ENABLED (SECURITY RISK!)"
    fi
  done
done <<< "$CLUSTERS"

 

まとめ

この記事では、GKEクラスタでレガシーメタデータエンドポイントが有効化されている問題について、リスクと対策を解説しました。

重要なポイントの振り返り

  1. セキュリティリスク
    • SSRF攻撃による認証なしアクセス(成功率85%)
    • kubeletトークンやCA証明書などの機密情報漏洩
    • 権限昇格による横展開攻撃(被害額平均1,200万円)
  2. 対策の要点
    • 新しいノードプールの作成が必須(既存プールでは変更不可)
    • disable-legacy-endpoints=trueのメタデータ設定
    • 段階的なワークロード移行計画の重要性
  3. ベストプラクティス
    • Workload Identityとの併用による多層防御
    • 定期的なセキュリティ監査の実施
    • アプリケーションの互換性テストの徹底

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

参考情報

Google Cloud公式ドキュメント

この記事をシェアする

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

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

料金プランを詳しく見る