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以外のイメージを使用した場合、以下のリスクが生じる可能性があります:
- 脆弱性の増加
- 汎用OSには平均2,300個以上の不要なパッケージが含まれる
- COSと比較して攻撃対象領域が15倍以上
- パッチ管理の複雑化
GKEのノードはあくまでコンテナを動作させる基盤であるため、不要なパッケージを含まず、ルートファイルシステムに対しても読み取り専用のOSとすることでリスクを軽減させるというわけですね。
セキュリティ面だけでなく、使用するメモリも少ないためパフォーマンス面などから考えてもとくべつな理由がない限りにおいては推奨通りCOSを利用するので間違いないでしょう。
修復方法
コンソールでの修復手順
Google Cloud コンソールを使用して、既存のノードプールをContainer-Optimized OSに変更します。
注意: 既存のノードプールのイメージタイプは直接変更できないため、新しいノードプールを作成して移行する必要があります。
- Google Cloud コンソールにアクセス
- GKEクラスタ一覧にアクセスします
- 対象のクラスタ名をクリックして詳細画面を開きます
- 新しいノードプールの作成
- 左側のメニューから「ノードプール」を選択します
- 「ノードプールを追加」をクリックします
- ノードプールの基本設定
- 名前: 適切な名前を入力(例:
cos-node-pool-1
) - ノード数: 既存のノードプールと同じ数を設定
- 名前: 適切な名前を入力(例:
- ノードの設定でCOSを選択
- 「ノード」セクションを展開します
- 「イメージタイプ」で「Container-Optimized OS with containerd (cos_containerd)」を選択
- マシンタイプ: 既存のノードプールと同じ設定を選択
- セキュリティ設定の確認
- 「セキュリティ」セクションを展開します
- 「シールドGKEノードを有効にする」にチェックを入れることを推奨
- 「セキュアブートを有効にする」にチェック
- 「整合性モニタリングを有効にする」にチェック
- 作成と移行
- 「作成」をクリックして新しいノードプールを作成します
- ワークロードが新しいノードプールに移行されたことを確認後、古いノードプールを削除します
gcloud CLIでの修復手順
Container-Optimized OSを使用する新しいノードプールを作成し、既存のワークロードを移行します。
注意: 既存のノードプールのイメージタイプは直接変更できないため、新しいノードプールを作成して移行する必要があります。
- 現在のノードプールの確認
# クラスタの一覧を表示 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)"
- 新しい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
- セキュリティ強化オプションの追加
# シールド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
- ワークロードの移行(コードンとドレイン)
# 古いノードプールのノードを取得 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
- 古いノードプールの削除
# すべてのワークロードが移行されたことを確認 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機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。