先日 GKE クラスタを Terraform で作成する機会があり、不明点やハマったポイントがあったのでメモとして残しておこうと思います。

クラスタのセキュリティの強化: 権限 - 最小限の権限の Google サービス アカウントを使用する

この記事では、 Terraform を使用して、 GKE ノード用のサービス アカウントと、それを使用する GKE ノード プールをあわせて作成するためのコードを紹介します。 GKE クラスター自体のコードは Terraform のドキュメント等を参照してください。

IAM ロールとアクセス スコープ

この 2 つの違いで少々混乱したので簡単にまとめてみる。

  • IAM ロール
    • GCP リソースへのアクセス権限
    • サービス アカウントに割り当てる
  • アクセス スコープ
    • GCE インスタンスに設定する
    • インスタンス上で GCP API のクライアントがアクセス可能な API の種類を制限する

例えば、 GCR のプライベート レジストリからコンテナー イメージを pull したい場合は、サービス アカウントに "roles/storage.objectViewer" を割り当て、 GCE インスタンスのアクセス スコープには "https://www.googleapis.com/auth/devstorage.read_only" を設定する。

サービス アカウントの作成とロールの割り当て

GCE のデフォルトのサービス アカウントには、プロジェクトの編集者ロールが割り当てられており、 GKE クラスターの動作には過剰な権限が含まれる。 そのため、 GKE クラスター用のサービス アカウントを作成し、必要な権限のロールのみを割り当てる構成をつくる。

まずは google_service_account リソースを使用し、 GKE ノード用のサービス アカウントを作成する。

そして google_project_iam_member リソースで、 GKE で最低限となるロール (monitoring.viewer, monitoring.metricWriter, logging.logWriter) をサービス アカウントに割り当てる。

# Service Account
resource "google_service_account" "gke_node_pool" {
  account_id   = "gke-node-pool"
  display_name = "gke-node-pool"
  description  = "A service account for GKE node"
}

# IAM Role binding
resource "google_project_iam_member" "gke_node_pool_roles" {
  for_each = toset([
    "roles/logging.logWriter",
    "roles/monitoring.metricWriter",
    "roles/monitoring.viewer"
  ])
  role   = each.value
  member = "serviceAccount:${google_service_account.gke_node_pool.email}"
}

注意点としては、ロールの割り当てに google_project_iam_binding リソースを使用しないようにする。 こちらは「あるロールに対して指定したメンバーを割り当てる」挙動となり、 2つ目以降の iam_binding を作成したところメンバーの割り当てが上書きされ、 当該ロールを使用する既存のサービスアカウントからロールが外れてしまった。

GKE ノードプールの作成

続いて google_container_node_pool リソースで GKE ノード プールを作成する。

node_config.service_account には作成したサービス アカウントの email を指定する。 oauth_scopes には cloud-platform (Web コンソールから SA を指定したクラスターを作った場合の既定値) を指定する。

# GKE Node Pool
resource "google_container_node_pool" "node_pool" {
  name               = "node-pool"
  location           = var.location
  cluster            = google_container_cluster.cluster.name
  initial_node_count = 1

  ...

  node_config {
    service_account = google_service_account.gke_node_pool.email
    oauth_scopes = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]

    ...
  }
}

まとめ