Terraform / GKE で実現する ExternalSecretOperator テンプレート
はじめに
GKEのSecretの管理に External Secret Operator を利用することがありました。
備忘も兼ねて、周辺知識も補足しながら、今後の使い回しのできるようにTerraform / GKEでのExternal Secret Operatorのテンプレートを作成しました。
【前提】
- Cluster : GKE Autopilot 1.27
- GKEのWorkload Identityは有効化されている
- マニフェスト管理
- helm : helmfile
- IaC : Terraform
今回は、Service Account キーの使用ではなく、Workload Identityを利用しています。
基本的な流れはこちらの公式docに詳しく書いてあるので、参考にしてください。
Terraform
variable "roles" {
default = ["roles/secretmanager.secretAccessor", "roles/iam.serviceAccountTokenCreator", "roles/cloudkms.cryptoKeyEncrypterDecrypter"]
}
Service Account に対して必要な権限を定義します。
GKEでのアプリケーションレイヤでのSecretの暗号化が有効になっている場合は、Cloud KMSへのアクセスが必要なため、roles/cloudkms.cryptoKeyEncrypterDecrypter
を追加します。
また、アクセストークンの生成に必要なロール roles/iam.serviceAccountTokenCreator
も付与します。
# Service Account(ExternalSecretsOperator用ユーザ)
resource "google_service_account" "external_secrets" {
account_id = "${var.prefix}-${var.service}-eso-${var.env}"
display_name = "External Secrets Operator Service Account"
}
# Role Binding(ESOへSecretsを読み込む権限を)
resource "google_project_iam_binding" "secret-pull" {
for_each = toset(var.roles)
project = var.project_id
role = each.key
members = [
"serviceAccount:${google_service_account.external_secrets.email}",
]
}
# GKEに対して、WorkloadIdentityUserを付与する
resource "google_service_account_iam_member" "external_secrets_workload_identity_user" {
service_account_id = google_service_account.external_secrets.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[default/external-secrets-serviceaccount]"
}
google_project_iam_binding
に対するRoleのバインドは1つしかできないため、for_eachを利用して複数のRoleをバインドします。
member
の値には、kubernetes serviceaccount(KSA)の存在するNamespsaceと、KSAの名前を指定します。
ここでは、default Namespaceにexternal-secrets-serviceaccount
という名前のKSAを作成しています。
Manifest
環境差分がありそうな部分に関しては、values.yaml
に定義していますので、参照する形で作成してください。
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-secrets-serviceaccount
labels:
app: test-application
# ESOに対してSSM/SecretsManagerからSecretsを取得することを許可するServiceAccount(IAM Role Binding)
{{ - with Values.serviceAccount.annotations }}
annotations:
{{to nindent 4 .}}
{{- end }}
kubernetes の ServiceAccount を作成します。
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: test-application-secretstore
labels:
app: test-application
spec:
provider:
gcpsm:
projectID: {{ .Values.projectID}}
# ref: https://external-secrets.io/v0.5.7/spec/#external-secrets.io/v1beta1.GCPSMAuth
auth:
workloadIdentity:
clusterLocation: {{ .Values.clusterLocation }}
clusterName: {{ .Values.clusterName }}
serviceAccountRef:
name: external-secrets-serviceaccount
今回は Namespace ごとの制御にしたいため、kind: ClusterSecretStore
ではなく、kind: SecretStore
を利用しています。
違いはこちらに詳しく書いてあります。
また、serviceAccountRef
に対して Namespace を指定することはできないので、気をつけてください
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: test-application-externalsecret
labels:
app: test-application
spec:
# 外部providerに対してデータ参照を行いSecretの値を更新する頻度
refreshInterval: 1h
# 該当データをどのように取得するかを定義したSecretStoreを指定する
secretStoreRef:
kind: SecretStore
name: test-application-secretstore
# ExternalSecretから作成される `kind: Secret` リソースの情報を定義する.ExternalSecretにつき1つだけ定義できる
target:
name: secret-store
# Secret Managerから取得するパラメータを定義する
data:
- remoteRef:
key: foo
secretKey: bar
projectID: ${PROJECT_ID}
clusterName: ${CLUSTER_NAME}
clusterLocation: ${CLUSTER_LOCATION}
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: ${PROJECT_ID}.svc.id.goog[NAMESPACE/KSA_NAME]
Discussion