> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siderolabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# GCP Workload Identity Federation

> Guide on how to configure Google Cloud Workload Identity Federation on Talos Linux

This guide provides a step-by-step walkthrough for configuring Google Cloud Workload Identity Federation on a Talos Kubernetes cluster.
It covers setting up the necessary GCP infrastructure (buckets, pools, providers), patching the Talos API server with the OIDC issuer, audience, and JWKS settings Google Cloud needs, and binding Kubernetes Service Accounts to Google Service Accounts for secure authentication.

## Environment setup

We'll make use of the following environment variables throughout the setup.
Edit the variables below with your correct information.

```bash theme={null}
export PROJECT_ID="GoogleProjectId"
export BUCKET_NAME="StorageBucketName"
export POOL_NAME="WorkloadIdentityPool"
export PROVIDER_NAME="WorkloadIdentityProvider"
export REGION="us-east1"
```

## Set up GCP Workload Identity infrastructure

This section sets up the bucket, pool, and OIDC provider Google Cloud needs to trust your cluster.

### Create the OIDC Storage Bucket

GCP needs a way to fetch the public keys from your cluster to verify signatures.
We use a public GCS bucket to host these keys.

```bash theme={null}
gcloud storage buckets create gs://${BUCKET_NAME} --project=${PROJECT_ID} --location=${REGION}

# Make it public (Read-only)
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
  --member="allUsers" \
  --role="roles/storage.objectViewer"
```

### Create the Workload Identity Pool

Create a Workload Identity Pool to manage and trust external identities for authentication.

```bash theme={null}
gcloud iam workload-identity-pools create ${POOL_NAME} \
  --project=${PROJECT_ID} \
  --location="global" \
  --display-name="Talos Workload Identity Pool"
```

### Create the OIDC provider

Create an OIDC provider that trusts tokens from the specified issuer, enabling secure external authentication to Google Cloud.

```bash theme={null}
gcloud iam workload-identity-pools providers create-oidc ${PROVIDER_NAME} \
  --project=${PROJECT_ID} \
  --location="global" \
  --workload-identity-pool=${POOL_NAME} \
  --issuer-uri="https://storage.googleapis.com/${BUCKET_NAME}" \
  --attribute-mapping="google.subject=assertion.sub,attribute.sub=assertion.sub"
```

## Configure Talos for Workload Identity Federation

With the GCP side in place, the next step is configuring Talos to issue and expose the OIDC tokens Google Cloud expects.

### Retrieve OIDC provider URL

First, retrieve the full resource name of the OIDC provider. This value will be used when configuring the API server audiences.

```bash theme={null}
OIDC_PROVIDER_URL=$(gcloud iam workload-identity-pools providers list --location="global" --workload-identity-pool="${POOL_NAME}" --filter="name:${PROVIDER_NAME}" --format json | jq -r '.[0].name')
```

### Generate a Talos patch

Next, create a patch file to configure the Talos cluster with the required OIDC settings for Google Workload Identity Federation.

<Tabs>
  <Tab title="Talos v1.14+">
    ```bash theme={null}
    cat <<EOF > oidc-patch.yaml
    apiVersion: v1alpha1
    kind: KubeAPIServerConfig
    extraArgs:
      # This must match the GCS bucket URL exactly
      service-account-issuer: "https://storage.googleapis.com/${BUCKET_NAME}"
      # GCP WIF expects this audience; you can also add "sts.googleapis.com"
      api-audiences: "iam.googleapis.com/${OIDC_PROVIDER_URL},https://storage.googleapis.com/${BUCKET_NAME},sts.googleapis.com,https://kubernetes.default.svc.cluster.local"
      # Where the public keys will be found (logically)
      service-account-jwks-uri: "https://storage.googleapis.com/${BUCKET_NAME}/keys.json"
      # How long tokens are valid (optional, but good for security)
      service-account-max-token-expiration: 24h
    EOF
    ```
  </Tab>

  <Tab title="Talos < v1.14">
    ```bash theme={null}
    cat <<EOF > oidc-patch.yaml
    cluster:
      apiServer:
        extraArgs:
          # This must match the GCS bucket URL exactly
          service-account-issuer: "https://storage.googleapis.com/${BUCKET_NAME}"
          # GCP WIF expects this audience; you can also add "sts.googleapis.com"
          api-audiences: "iam.googleapis.com/${OIDC_PROVIDER_URL},https://storage.googleapis.com/${BUCKET_NAME},sts.googleapis.com,https://kubernetes.default.svc.cluster.local"
          # Where the public keys will be found (logically)
          service-account-jwks-uri: "https://storage.googleapis.com/${BUCKET_NAME}/keys.json"
          # How long tokens are valid (optional, but good for security)
          service-account-max-token-expiration: 24h
    EOF
    ```
  </Tab>
</Tabs>

### Apply OIDC patch to control plane node

Retrieve every control plane node's IP and apply the OIDC patch to configure the cluster for Workload Identity authentication.

<Tabs>
  <Tab title="Talos-native">
    ```bash theme={null}
    CONTROL_PLANE_NODE_ADDRESSES=$(kubectl --kubeconfig kubeconfig get nodes --output json | jq -r '[.items[] | select(.metadata.labels."node-role.kubernetes.io/control-plane" == "").status.addresses[] | select(.type == "InternalIP").address] | join(",")')

    talosctl patch machineconfig --talosconfig talosconfig --patch @oidc-patch.yaml --nodes ${CONTROL_PLANE_NODE_ADDRESSES}
    ```
  </Tab>

  <Tab title="Omni">
    Apply the patch as a cluster config patch targeted at all control plane nodes at once, rather than collecting individual addresses:

    1. Select the **Clusters** tab in the left-hand menu.
    2. Open the cluster menu (**⋯**) and select **Config Patches**. (Alternatively, open the specific cluster and select **Config Patches** from the right-hand panel.)
    3. Click **Create Patch**.
    4. Select **Control Planes** from the **Patch Target** dropdown. This targets every control plane node in the cluster, so there's no per-node address collection to do.
    5. Enter the patch from the previous step into the patch editor.
    6. Click **Save**. Omni applies the configuration to every control plane node.
  </Tab>
</Tabs>

### Retrieve Kubernetes OIDC configuration

Download the cluster's keys.json and discovery.json files, which contain the OIDC public keys and discovery metadata needed for external authentication.

```bash theme={null}
kubectl --kubeconfig kubeconfig get --raw /openid/v1/jwks > keys.json
kubectl --kubeconfig kubeconfig get --raw /.well-known/openid-configuration > discovery.json
```

### Upload to GCS

Upload the cluster's OIDC `keys.json` and `discovery.json` to the storage bucket, making them publicly accessible for authentication verification.

```bash theme={null}
gcloud storage cp keys.json gs://${BUCKET_NAME}/keys.json
gcloud storage cp discovery.json gs://${BUCKET_NAME}/.well-known/openid-configuration
```

### Verify the upload

Confirm the file is valid JSON containing an issuer field that matches your bucket URL:

```bash theme={null}
curl https://storage.googleapis.com/${BUCKET_NAME}/.well-known/openid-configuration
```

## Create and bind the Google Service Account

With the cluster issuing valid OIDC tokens, the next step is creating a Google Service Account that Kubernetes workloads can impersonate.

### Create the Google Service Account (GSA)

Create a Google Service Account that external identities can impersonate via Workload Identity for accessing Google Cloud resources.

```bash theme={null}
GSA_NAME="talos-workload-sa"
gcloud iam service-accounts create ${GSA_NAME} --project=${PROJECT_ID}
```

### Get the Workload Identity Pool name

Retrieve the full resource name of the Workload Identity Pool for configuring identity bindings.

```bash theme={null}
WORKLOAD_IDENTITY_POOL_URL=$(gcloud iam workload-identity-pools list --location="global" --filter="name:${POOL_NAME}" --format json | jq -r '.[].name')
```

### Grant permissions to the GSA

Assign the necessary roles to the Google Service Account, including access to project resources and the ability to be impersonated via Workload Identity.

```bash theme={null}
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/storage.admin"

gcloud iam service-accounts add-iam-policy-binding "${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_URL}/attribute.sub/system:serviceaccount:default:workload-identity"
```

<Note>
  Things to note:

  * This grants `roles/storage.admin`, full administrative access to every bucket in the project, though the verification step later only lists the contents of one bucket. If you want tighter scoping, consider granting `roles/storage.objectViewer` on just the `${BUCKET_NAME}` bucket instead, and adjust if your workload needs more.
  * Ensure the member string matches your specific Kubernetes configuration. The format is `system:serviceaccount:<NAMESPACE>:<KSA_NAME>`. In this example, we use the default namespace and the workload-identity service account.
</Note>

### Generate the Workload Identity configuration file

Create a local configuration file that maps the Kubernetes service account to the Google Service Account for authentication.

```bash theme={null}
gcloud iam workload-identity-pools create-cred-config \
    ${OIDC_PROVIDER_URL} \
    --service-account="${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --credential-source-file="/var/run/secrets/tokens/gcp-ksa/token" \
    --output-file=sts-creds.json
```

## Deployment and verification

With the identity binding in place, deploy a test workload to confirm the whole chain works end to end.

### Deploy credential configMap

Create a ConfigMap to store the credential configuration file, enabling the Pod's Google SDK to perform the token exchange.

```bash theme={null}
kubectl --kubeconfig kubeconfig create configmap workload-identity-config --from-file=google-application-credentials.json=sts-creds.json -n default
```

### Create a Kubernetes service account

Create the Kubernetes Service Account that will be bound to the Google Service Account to authorize the workload.

```bash theme={null}
kubectl --kubeconfig kubeconfig create serviceaccount workload-identity --namespace default
```

### Deploy test pod

Deploy a Pod that projects the Service Account token and credential configuration to verify the identity federation.

```bash theme={null}
kubectl --kubeconfig kubeconfig apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: workload-identity-test
  namespace: default
spec:
  serviceAccountName: workload-identity
  containers:
  - image: google/cloud-sdk:slim
    name: workload-identity-test
    command:
    - /bin/sh
    - -c
    - sleep infinity
    env:
    - name: CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE
      value: /var/run/secrets/tokens/gcp-ksa/google-application-credentials.json
    - name: CLOUDSDK_CORE_PROJECT
      value: "${PROJECT_ID}"
    volumeMounts:
      - name: gcp-ksa
        mountPath: /var/run/secrets/tokens/gcp-ksa
        readOnly: true
  volumes:
  - name: gcp-ksa
    projected:
      sources:
      # The Token itself
      - serviceAccountToken:
          path: token
          audience: "//iam.googleapis.com/${OIDC_PROVIDER_URL}"
          expirationSeconds: 3600
      # The Config that tells the Google SDK how to exchange the token
      - configMap:
          name: workload-identity-config
          optional: false
          items:
          - key: "google-application-credentials.json"
            path: "google-application-credentials.json"
EOF
```

### Verify access

Execute a command inside the running Pod to list the storage bucket contents, confirming that the Workload Identity authentication is functioning correctly.

```bash theme={null}
kubectl exec -it workload-identity-test -- gcloud storage ls gs://${BUCKET_NAME}
```
