前回の続編です。GKEクラスタの設定をterraformで行なうように準備ができたので、肝心のWorkload Idenfityの設定をしていきます。
なぜ
Workload Identityの実基盤への導入に丁寧に書いてありますが、素朴なやり方では以下のような問題があるからです。
- 権限が強すぎる
- 方法: GKEの裏側のGCEに丸ごと権限を付与する
- 楽チンだが、本来付与すべきでない権限も一律で付与してしまうことになる
- GKEの上で様々なアプリケーションが動いているときには危険
- 権限管理が面倒
- 方法: 専用のサービスアカウントをのキーをダウンロードして使う
- 鍵をどうやって安全に配置するか
- 使い回されるとカオスになっていく
- 10年経つとダウンロードしたキーが失効する
- 仕事だと突然サービスが動かないことになるので、まずい
今回の私のケースは趣味プロジェクトであり、私しか使わないのでぶっちゃげGKEに丸ごと権限付与で問題ないのですが、仕事でもGKEを使う機会が出てきたので、素振りをしようという感じです。
どうやって
上記の問題を解決する方法として、Workload Identityというものがあります。
Workload Identityの理解にはあれこれ知識が必要なので、整理していきます。Kubernetes初心者なので...。
前提知識: Kubernetesのサービスアカウント
Kubernetesの操作(例: kubectl apply
)を行なう代表的な登場人物としては人間(ユーザー)がいるが、プログラムからアクセスするときは共通ユーザーなどは作りたくない。なので、GCPと同じく、Kubernetesにもサービスアカウントという概念が登場する。
Kubernetesで「どういうことができる~」という権限はRoleで表現される。クラスタ単位の権限はClusterRole
として定義され、namespace単位の権限はRole
で定義される。「この{ユーザー, グループ, サービスアカウント}にこの権限を付与する」というのはRoleをbindするという形で表わされ、具体的にはRoleBinding
やClusterRoleBinding
がある。kind: ClusterRoleBinding
といった形でyamlファイルで定義していくようだ。
というわけで、Kubernetes上で動くpodは
- Kubernetesに対する操作をする時は、Kubernetesのサービスアカウントを経由してKubernetesのAPIを触る
- GCPのリソースに対して操作をするときは、GCPのサービスアカウントを利用してGCPのリソースを触る
という風にして動いていた(Workload IdentityでPodからのGCPリソースアクセスをセキュアにする - Carpe Diemより図を引用)。podがGCPのサービスアカウントを知る必要があり、ダウンロードしたキーをどう管理する問題が起きてしまう。
Workload IdentityによるGCPのサービスアカウントとKubernetesのサービスアカウントの紐付け
前提知識が揃ったので、Workload Identityの説明。Workload Identityでは、KubernetesのサービスアカウントとGCPのサービスアカウントを紐付けることによって、GCPのサービスアカウントのキーをダウンロードせずGCP側のリソースを使えるようにする。Workload IdentityでPodからのGCPリソースアクセスをセキュアにする - Carpe Diemより、再び図を引用。
具体的な設定方法
GCPのサービスアカウントを作成
今回はBigQueryからデータを取得して、推薦した結果をGCSに置くバッチジョブを対象にします。BigQueryとGCSの権限に絞ったサービスアカウントをまず作成しましょう。
resource "google_service_account" "recommend_related_examples_service_account" { account_id = "recommend-related-examples" display_name = "Service account for recommend-related-examples" } resource "google_project_iam_member" "recommend_related_examples_roles_binding" { for_each = toset(local.recommend_related_examples_roles) role = each.value member = "serviceAccount:${google_service_account.recommend_related_examples_service_account.email}" } locals { recommend_related_examples_roles = [ "roles/bigquery.dataViewer", "roles/bigquery.jobUser", "roles/storage.admin", "roles/storage.objectCreator", "roles/storage.objectViewer", ] }
Kubernetesのnamespaceの作成
GKEのクラスタ全体にbindをしたくないので、namespaceを事前に作っておきます。
kind: Namespace apiVersion: v1 metadata: name: my-project-batch
Kubernetesのサービスアカウントの作成
GCPのサービスアカウントを紐付ける用のKubernetesのサービスアカウントを作成します。annotations
を使って、どのGCP側のサービスアカウントと紐付けるかを記述します。
apiVersion: v1 kind: ServiceAccount metadata: name: my-job namespace: my-project-batch annotations: iam.gke.io/gcp-service-account: recommend-related-examples@my-project.iam.gserviceaccount.com
annotations
とは何ぞや...という気持ちになるけど、labels
と同じようにkey/value形式でオブジェクトに対してメタデータを付与する方法であるが、labels
と違ってキーの長さなどに制約があまりない、というのが特徴のようだ(labels
はselectorなどで検索する用途で使われる想定っぽい)。
Workload Identityを有効にする
GCPのサービスアカウントに対してWorkload Identityの権限を有効にします。
resource "google_service_account_iam_member" "recommend_related_examples_wi_iam_binding" { service_account_id = google_service_account.recommend_related_examples_service_account.name member = "serviceAccount:my-project.svc.id.goog[ml-news-batch/recommend-related-examples]" role = "roles/iam.workloadIdentityUser" }
これでクラスタには最小限の権限、個別のworkloadには適切な権限を付与しつつ、管理方法も面倒なことにならずにいけるようになりました。