Zenn
🍦

Crossplane で GKE の Workload Identity による認証方法

2025/03/21に公開

クラウドエース北野です。
GKE の Workload Identity によって、Crossplane の認証させる方法を紹介します。

概要

Crossplane で Google Kubernetes Engine (以降 GKE と呼びます)で Workload Identity を使うには、以下のようにします。

  • ControllerConfig に Workload Identity により関連づける Google Cloud のサービスアカウントと Kubernetes のサービスアカウントの定義
  • ProviderConfig の spec.credentials.source に InjectedIdentity の定義

上記の内容のマニフェストは以下の通りです。

controller_config.yaml
apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: sa-k8s-crossplane
  annotations:
    iam.gke.io/gcp-service-account: <GOOGLE_SERVICEACCOUNT_EMAIL>
spec:
  serviceAccountName: <KUBERNETES_SERVICEACCOUNT_ID>
pc.yaml
apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: pc-workload-identity
spec:
  credentials:
    source: InjectedIdentity
  projectID: <PROJECT_ID>

この ProviderConfig を参照するように ManagedResource を定義すると、Crossplane の gcp-provider Provider を Google Cloud のサービスアカウントで認証させることができます。

はじめに

Crossplane は Google Cloud や AWS といった Kubernetes ではないリソースを Kubernetes リソースとして扱う OSS の Kubernetes 拡張機能です。Crossplane は Upbound 社により開発された CNCF プロジェクトのソフトウェアで、あらゆるものを Kubernetes から管理することを目的としたソフトウェアです。

Crossplane は Google Cloud や AWS などの操作する Kubernetes 外のプラットフォームごとに Provider を作成します。この Provider が、Kubernetes のマニフェストや API コールを外部プラットフォームの API に変換して、外部プラットフォーム上のリソースを管理します。
また、Provider は作成したリソースを Kubernetes 上で ManagedResource というオブジェクトで管理します。この ManagedResource を Kubernetes 上で作成、削除、更新すると、外部のプラットフォームのオブジェクトにも同じ処理がなされます。

Provider に外部リソースを操作させるためには、Provider を認証させる必要があります。Google Cloud の Provider を使って、Cloud Storage バケットを作成するクイックスタートでは、サービスアカウント鍵を使って認証しています。この方法はサービスアカウント鍵を作成する必要があり、セキュリティ上の問題があります。

そこで、本記事では GKE の Workload Identity を使って、Google Cloud のサービスアカウントで Provider を認証させる方法を紹介します。

Crossplane の認証方法

Crossplane は Google Cloud の Provider の認証に以下の 5 つの方法を提供しています。

  • Workload Identity
  • サービスアカウントキー
  • サービスアカウント権限の借用
  • OAuth2 Access Key
  • Upbound クラウドによる認証 (OIDC)

Crossplane を GKE 上で動かす場合、Workload Identity を使って認証させる方法が最もセキュアな方法となっています。

Crossplane の実行

以下のシステムの構成で Crossplane を実行して、Google Cloud の VPC Network を作成してみます。

Crossplane を GKE 上で実行し、Crossplane のデフォルト Namespace crossplane-system 上に作成します。この Namespace のサービスアカウントと Google Cloud で作成したサービスアカウントを Workload Identity の機能で紐つけます。
Crossplane で Google Cloud の VPC Network を作成する gcp-provider をインストールし、Workload Identity で認証するための ProviderConfig と ControllerConfig を作成します。
そして、作成する VPC Network の ManagedResource を作成します。この ManagedResource に ControllerConfig を紐つけて、Workload Identity で Provider を Google Cloud に認証させます。

この構成でシステムを構築する Terraform コードと、Kubernetes マニフェストを紹介します。<> で囲まれた部分は環境に合わせて読み替えてください。

事前準備

GKE と Service Account を作成する Terraform コードは以下の通りです。

network.tf
resource "google_compute_network" "main" {
  name = "gke-single"

  project                 = var.project
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "main" {
  name = "gke-platform-tyo"

  project       = var.project
  ip_cidr_range = "192.168.0.0/24"
  network       = google_compute_network.main.self_link
  region        = "asia-northeast1"
}
service_account.tf
resource "google_service_account" "main" {
  depends_on = [
    google_container_cluster.primary
  ]
  account_id = "<GOOGLE_SERVICEACCOUNT>"

  project = var.project
}

resource "google_service_account_iam_member" "workload_identity_argocd-server" {
  service_account_id = google_service_account.main.id
  role               = "roles/iam.workloadIdentityUser"
  member             = format("serviceAccount:%s.svc.id.goog[%s/%s]", var.project, "crossplane-system", "<KUBERNETES_SERVICEACCOUNT>")
}

resource "google_project_iam_member" "main" {
  member  = google_service_account.main.member
  project = var.project
  role    = "roles/editor"
}

今回、Autopilot の GKE を使うため、Terraform コードは以下のようになります。

gke.tf
resource "google_container_cluster" "main" {
  name = "platform"

  project          = var.project
  location         = "asia-northeast1"
  enable_autopilot = true
  network          = google_compute_network.main.id
  subnetwork       = google_compute_subnetwork.main.id
}

Crossplane の操作

Crossplane を以下のように Helm でインストールします。

helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update

helm install crossplane \
	 crossplane-stable/crossplane \
	 --namespace crossplane-system \
	 --create-namespace

インストールすると、以下の2つの Deployment が作成されます。

kubectl get deployment -n crossplane-system

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
crossplane                1/1     1            1           4m1s
crossplane-rbac-manager   1/1     1            1           4m1s

VPC Network を作成する compute の gcp-provider をインストールするマニフェストは以下のようになります。

provider.yaml
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-family-gcp
spec:
  package: xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v1.12.1
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-gcp-compute
spec:
  package: xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v1.12.1
  controllerConfigRef:
    name: sa-k8s-crossplane

Workload Identity を Kubernetes のサービスアカウントと Google Cloud のサービスアカウントの紐つけを以下の ControllerConfig で定義します。

controller_config.yaml
apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: sa-k8s-crossplane
  annotations:
    iam.gke.io/gcp-service-account: <GOOGLE_SERVICEACCOUNT_EMAIL>
spec:
  serviceAccountName: <KUBERNETES_SERVICEACCOUNT>

ここで、ControllerConfig と Provider 作成します。

kubectl apply -f controller_config.yaml
kubectl apply -f provider.yaml

暫くすると、gcp-provider がインストールされ、HEALTHY が True になります。

kubectl get provider

NAME                    INSTALLED   HEALTHY   PACKAGE                                                               AGE
provider-family-gcp     True        True      xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v1.12.1     3m56s
provider-gcp-compute    True        True      xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v1.12.1    3m56s

続いて、ProviderConfig を定義し、Provider を Workload Identity で認証するようにします。

pc.yaml
apiVersion: gcp.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: pc-workload-identity
spec:
  credentials:
    source: InjectedIdentity
  projectID: <PROJECT_ID>

次に実際に Network の ManagedResource を作って、VPC Network を作成してみます。
事前に作成する VPC Network がないことを確認します。

gcloud compute networks describe crossplane-sample --project <PROJECT ID>

ERROR: (gcloud.compute.networks.describe) Could not fetch resource:
 - The resource 'projects/<PROJECT ID>/global/networks/crossplane-sample' was not found

Network の ManagedResource を次のように定義します。

network.yaml
apiVersion: compute.gcp.upbound.io/v1beta1
kind: Network
metadata:
  name: crossplane-sample

spec:
  forProvider:
    autoCreateSubnetworks: false
    project: <PROJECT ID>
    description: "Created By Crossplane"
	
  providerConfigRef:
    name: pc-workload-identity

マニフェストを実行して、暫くすると ManagedResource の SYNCED と READY が True になると、Google Cloud 側にリソースが作成されます。

kubectl apply -f network.yaml
kubectl get network

NAME                SYNCED   READY   EXTERNAL-NAME       AGE
crossplane-sample   True     True    crossplane-sample   39s

作成された VPC Network を確認すると、作成されていること ManagedResource の内容が作成されていることが分かります。

gcloud compute networks describe crossplane-sample --project <PROJECT ID>

autoCreateSubnetworks: false
creationTimestamp: '2025-03-20T01:37:59.560-07:00'
description: Created By Crossplane
id: '8027351178494242056'
kind: compute#network
name: crossplane-sample
networkFirewallPolicyEnforcementOrder: AFTER_CLASSIC_FIREWALL
routingConfig:
  bgpBestPathSelectionMode: LEGACY
  routingMode: REGIONAL
selfLink: https://www.googleapis.com/compute/v1/projects/<PROJECT ID>/global/networks/crossplane-sample
selfLinkWithId: https://www.googleapis.com/compute/v1/projects/<PROJECT ID>/global/networks/8027351178494242056
x_gcloud_bgp_routing_mode: REGIONAL
x_gcloud_subnet_mode: CUSTOM

ManagedResource を削除して、Google Cloud 側のリソースが Crossplane により削除されるか確認します。

kubectl delete network crossplane-sample
network.compute.gcp.upbound.io "crossplane-sample" deleted

kubectl get network
No resources found

gcloud compute networks describe crossplane-sample --project <PROJECT ID>
ERROR: (gcloud.compute.networks.describe) Could not fetch resource:
 - The resource 'projects/<PROJECT ID>/global/networks/crossplane-sample' was not found

ManagedResource を削除すると、実際の Google Cloud 上でも対応するリソースが削除されていることが分かります。

さいごに

Workload Identity を使って、gcp-provider を Google Cloud に認証させる方法を紹介しました。Workload Identity を使うと、安全に Google Cloud に認証させられるので、GKE で Crossplane を使う場合は導入を検討してみてください。

Discussion

ログインするとコメントできます