GKE Container-Optimized OSの利用について

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

この記事では、GKEクラスタのノードでContainer-Optimized OS以外のイメージが使用されている場合のリスクと対策を解説します。

ポリシーの説明

GKEクラスタのノードでは、Googleが提供するContainer-Optimized OS(COS)を使用することが推奨されています。COSは、コンテナワークロードの実行に特化し、セキュリティを最重要視して設計されたLinuxベースのオペレーティングシステムです。

Container-Optimized OSの主な特徴:

  • 読み取り専用ファイルシステム: ルートファイルシステムが読み取り専用のため、マルウェアによる改ざんが困難
  • 自動セキュリティ更新: OSとカーネルのセキュリティパッチが自動的に適用
  • 最小化された構成: コンテナ実行に必要なコンポーネントのみを含む
  • セキュリティ強化: SELinux、AppArmor、seccompなどのセキュリティ機能がデフォルトで有効

なぜ危険なのか?

具体的なリスクと影響

Container-Optimized OS以外のイメージを使用した場合、以下のリスクが生じる可能性があります:

  1. 脆弱性の増加
    • 汎用OSには平均2,300個以上の不要なパッケージが含まれる
    • COSと比較して攻撃対象領域が15倍以上
  2. パッチ管理の複雑化

GKEのノードはあくまでコンテナを動作させる基盤であるため、不要なパッケージを含まず、ルートファイルシステムに対しても読み取り専用のOSとすることでリスクを軽減させるというわけですね。

セキュリティ面だけでなく、使用するメモリも少ないためパフォーマンス面などから考えてもとくべつな理由がない限りにおいては推奨通りCOSを利用するので間違いないでしょう。

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、既存のノードプールをContainer-Optimized OSに変更します。

注意: 既存のノードプールのイメージタイプは直接変更できないため、新しいノードプールを作成して移行する必要があります。

  1. Google Cloud コンソールにアクセス
    • GKEクラスタ一覧にアクセスします
    • 対象のクラスタ名をクリックして詳細画面を開きます
  2. 新しいノードプールの作成
    • 左側のメニューから「ノードプール」を選択します
    • 「ノードプールを追加」をクリックします
  3. ノードプールの基本設定
    • 名前: 適切な名前を入力(例: cos-node-pool-1
    • ノード数: 既存のノードプールと同じ数を設定
  4. ノードの設定でCOSを選択
    • 「ノード」セクションを展開します
    • 「イメージタイプ」で「Container-Optimized OS with containerd (cos_containerd)」を選択
    • マシンタイプ: 既存のノードプールと同じ設定を選択
  5. セキュリティ設定の確認
    • 「セキュリティ」セクションを展開します
    • 「シールドGKEノードを有効にする」にチェックを入れることを推奨
    • 「セキュアブートを有効にする」にチェック
    • 「整合性モニタリングを有効にする」にチェック
  6. 作成と移行
    • 「作成」をクリックして新しいノードプールを作成します
    • ワークロードが新しいノードプールに移行されたことを確認後、古いノードプールを削除します

gcloud CLIでの修復手順

Container-Optimized OSを使用する新しいノードプールを作成し、既存のワークロードを移行します。

注意: 既存のノードプールのイメージタイプは直接変更できないため、新しいノードプールを作成して移行する必要があります。

  1. 現在のノードプールの確認
    # クラスタの一覧を表示
    gcloud container clusters list
    
    # 特定のクラスタのノードプール一覧を表示
    gcloud container node-pools list \
      --cluster=CLUSTER_NAME \
      --zone=COMPUTE_ZONE
    
    # ノードプールの詳細情報を確認(イメージタイプを含む)
    gcloud container node-pools describe NODE_POOL_NAME \
      --cluster=CLUSTER_NAME \
      --zone=COMPUTE_ZONE \
      --format="value(config.imageType)"
    

     

  2. 新しいCOSノードプールの作成
    # Container-Optimized OSを使用する新しいノードプールを作成
    gcloud container node-pools create cos-node-pool \
      --cluster=CLUSTER_NAME \
      --zone=COMPUTE_ZONE \
      --image-type=COS_CONTAINERD \
      --num-nodes=3 \
      --machine-type=e2-standard-4 \
      --disk-size=100 \
      --disk-type=pd-standard \
      --enable-autoscaling \
      --min-nodes=3 \
      --max-nodes=10 \
      --enable-autorepair \
      --enable-autoupgrade
    

     

  3. セキュリティ強化オプションの追加
    # シールドGKEノードを有効化したCOSノードプール
    gcloud container node-pools create secure-cos-pool \
      --cluster=CLUSTER_NAME \
      --zone=COMPUTE_ZONE \
      --image-type=COS_CONTAINERD \
      --num-nodes=3 \
      --shielded-secure-boot \
      --shielded-integrity-monitoring \
      --metadata=disable-legacy-endpoints=true \
      --workload-metadata=GKE_METADATA \
      --service-account=NODE_SERVICE_ACCOUNT@PROJECT_ID.iam.gserviceaccount.com
    

     

  4. ワークロードの移行(コードンとドレイン)
    # 古いノードプールのノードを取得
    kubectl get nodes -l cloud.google.com/gke-nodepool=OLD_NODE_POOL_NAME
    
    # 各ノードをコードン(新しいPodのスケジューリングを停止)
    kubectl cordon NODE_NAME
    
    # ノードからPodを退避(新しいノードプールに移行)
    kubectl drain NODE_NAME \
      --ignore-daemonsets \
      --delete-emptydir-data \
      --force \
      --grace-period=60
    

     

  5. 古いノードプールの削除
    # すべてのワークロードが移行されたことを確認
    kubectl get pods --all-namespaces -o wide | grep -c OLD_NODE_POOL_NAME
    # 結果が0であることを確認
    
    # 古いノードプールを削除
    gcloud container node-pools delete OLD_NODE_POOL_NAME \
      --cluster=CLUSTER_NAME \
      --zone=COMPUTE_ZONE \
      --quiet
    

     

Terraformでの修復手順

Container-Optimized OSを使用する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"
  }

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

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

# Container-Optimized OSを使用するノードプール
resource "google_container_node_pool" "primary_nodes" {
  name       = "cos-node-pool"
  location   = var.region
  cluster    = google_container_cluster.primary.name
  node_count = var.node_count

  node_config {
    # Container-Optimized OSを明示的に指定
    image_type = "COS_CONTAINERD"

    machine_type = var.machine_type

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

    # シールドGKEノードの有効化(強く推奨)
    shielded_instance_config {
      enable_secure_boot          = true
      enable_integrity_monitoring = true
    }

    # 最小権限のサービスアカウント
    service_account = google_service_account.node_sa.email
    oauth_scopes = [
      "<https://www.googleapis.com/auth/logging.write>",
      "<https://www.googleapis.com/auth/monitoring>",
    ]

    # メタデータの保護
    metadata = {
      disable-legacy-endpoints = "true"
    }

    # ノードの自動アップグレード(COSのセキュリティ更新を自動適用)
    management {
      auto_repair  = true
      auto_upgrade = true
    }

    # プリエンプティブルノードの使用(コスト最適化)
    preemptible  = var.preemptible
    spot         = var.spot

    labels = {
      environment = var.environment
      managed_by  = "terraform"
    }

    tags = ["gke-node", "${var.cluster_name}-node"]
  }

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

# ノード用のサービスアカウント
resource "google_service_account" "node_sa" {
  account_id   = "${var.cluster_name}-node-sa"
  display_name = "GKE Node Service Account"
  description  = "Service account for GKE nodes with minimal permissions"
}

# 必要最小限の権限のみ付与
resource "google_project_iam_member" "node_sa_log_writer" {
  project = var.project_id
  role    = "roles/logging.logWriter"
  member  = "serviceAccount:${google_service_account.node_sa.email}"
}

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

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

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

variable "cluster_name" {
  description = "GKE cluster name"
  type        = string
}

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

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

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

variable "preemptible" {
  description = "Use preemptible nodes"
  type        = bool
  default     = false
}

variable "spot" {
  description = "Use spot nodes"
  type        = bool
  default     = false
}

 

まとめ

この記事では、GKEノードでContainer-Optimized OSを使用したセキュリティ強化について、リスクと対策を解説しました。

COSを使用することで、最小限の攻撃対象領域、自動セキュリティ更新、読み取り専用ファイルシステムなど、多層防御によるセキュリティ強化が実現できます。既存のクラスタでも、新しいノードプールを作成することで段階的な移行が可能です。

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

参考情報

Google Cloud公式ドキュメント

この記事をシェアする

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

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

料金プランを詳しく見る