Operator to automate workload identity setup on GCP clusters
Workload identity gives a workload app the ability to exchange an OAuth token, acquired from some external identity provider, for a GCP access token. The application can use this access token to use whatever resources it needs on GCP.
Workload Identity allows a Kubernetes service account in your GKE cluster to act as an IAM service account. Pods that use the configured Kubernetes service account automatically authenticate as the IAM service account when accessing Google Cloud APIs. Using Workload Identity allows you to assign distinct, fine-grained identities and authorization for each application in your cluster.
These are steps that are meant to be taken on the workload cluster before the configuration steps
giantswarm.io/workload-identity-enabled: "true"
💡 It expects to find the GCP project id in the spec of the workload cluster
export KUBE_SA_NAME="<inset-sa-name-here>"
export KUBE_NAMESPACE="<insert-namespace-here>"
kubectl create serviceaccount $KUBE_SA_NAME \
--namespace $KUBE_NAMESPACE
export GOOGLE_SA_NAME="<insert-gcp-service-account-name-here>"
gcloud iam service-accounts create "$GOOGLE_SA_NAME" --project="$GCP_PROJECT_NAME"
export GOOGLE_SA_ID="$GOOGLE_SA_NAME@$GCP_PROJECT_NAME.iam.gserviceaccount.com"
# this policy binding associates the GCP service account with a Kubernetes service account
gcloud iam service-accounts add-iam-policy-binding \
--project "$GCP_PROJECT_NAME" \
"$GOOGLE_SA_ID" \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:$WORKLOAD_ID_POOL[$KUBE_NAMESPACE/$KUBE_SA_NAME]"
Example: Add the compute.viewer
role:
# Add necessary permissions to the GCP Service Account
gcloud projects add-iam-policy-binding "$GCP_PROJECT_NAME" \
--role=roles/compute.viewer \
--member="serviceAccount:$GOOGLE_SA_ID"
kubectl annotate sa $KUBE_SA_NAME \
giantswarm.io/gcp-service-account=$GOOGLE_SA_ID \
The reconciler tracks ServiceAccounts
annotated with giantswarm.io/gcp-service-account
, giantswarm.io/gcp-workload-identity
& giantswarm.io/gcp-identity-provider
which should contain the GCP service account ID (in the format <gcp-service-account-name>@<gcp-project-name>.iam.gserviceaccount.com
).
When it notices such a ServiceAccount
, it will create a Secret
with the GOOGLE_APPLICATION_CREDENTIALS
json file, which has the following format:
{
"type": "external_account",
"audience": "identitynamespace:<workload-identity-pool-id>:<identity-provider-from-workload-identity-pool>",
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<service-account-id>:generateAccessToken",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": {
"file": "/var/run/secrets/tokens/gcp-ksa/token"
}
}
These credentials will be used by the pod's GCP SDK library to perform the token exchange, swapping the Kubernetes ServiceAccount token for a GCP one.
The webhook injects the necessary volumes and env variable to a pod labelled with: giantswarm.io/workload-identity: "true"
.
The label is there so it doesn't interfere with normal Pod creation.
If the pod is labelled and it also has a ServiceAccount
, that has the annotation giantswarm.io/gcp-service-account
, it will inject the env variable: