Configuring Workload Identity Federation for Kubernetes
A pod in a Thalassa Kubernetes cluster can call the Thalassa Cloud API. The pod does not keep a client secret.
The pod reads a projected token. Thalassa Cloud exchanges that token for a bearer token. The bearer token uses one Thalassa Cloud service account.
These three objects must agree:
| Object | Description |
|---|---|
| Projected token | A token for one Kubernetes service account. The audience is https://api.thalassa.cloud. The token has a short life. |
| Identity provider | Thalassa Cloud creates this provider for the cluster. The provider name includes the cluster identity. |
| Federated identity | A rule on a Thalassa Cloud service account. The subject is system:serviceaccount:<namespace>:<name>. |
A Kubernetes service account and a Thalassa Cloud service account are different objects. The projected token identifies the Kubernetes service account. The federated identity selects the Thalassa Cloud service account.
Before you start
Make sure that these conditions are true:
- The cluster has an identity provider. Refer to Cluster identity provider.
- You can create a Thalassa Cloud service account.
- You can create a federated identity.
- You know the Kubernetes namespace.
- You know the Kubernetes service account name.
Bind the IAM roles or the IAM policies to the Thalassa Cloud service account. These permissions must allow the API calls from the pod. Set the API scopes on the federated identity. The scopes must allow the same calls. Refer to OIDC concepts.
Cluster identity provider
Thalassa Cloud creates one identity provider for each Kubernetes cluster. Thalassa Cloud does this during cluster OIDC integration. The provider name includes the cluster identity. The provider has this label:
kubernetes_cluster_id=<cluster identity>Select this identity provider for the federated identity. Do not create a second identity provider for the same Thalassa cluster.
If the identity provider is not in the list, cluster OIDC integration is not complete. Wait until the identity provider is in the list. Then continue.
The command tcloud iam workload-identity-federation bootstrap kubernetes stops if this identity provider is missing.
Get the cluster identity in the console, or run tcloud kubernetes list.
Configure the federated identity
- Create a Thalassa Cloud service account.
- Bind the IAM roles or the IAM policies that the pod needs.
- Create a federated identity on that service account.
Open the service account
- In the console, go to IAM → Service Accounts.
- Open the service account.
Create the federated identity
- Open Federated Identities.
- Click Create Federated Identity.
Set the match rules
- Select the cluster identity provider. Refer to Cluster identity provider.
- Set the fields in this table.
| Field | Value |
|---|---|
| Subject | system:serviceaccount:<namespace>:<service-account-name> |
| Trusted audience | https://api.thalassa.cloud |
| API scopes | The scopes for this pod. Examples: api:read and api:write. |
Example: the Kubernetes service account name is my-app. The namespace is default. Set the subject to:
system:serviceaccount:default:my-appSet the trusted audience to the same value as the projected token audience. For the Thalassa Cloud API, use https://api.thalassa.cloud.
Save the federated identity
Click Create Federated Identity.
Copy the organisation ID. Copy the Thalassa Cloud service account ID. These values are identifiers. They are not secrets. The pod sends these values with the projected token.
Put a projected token in the pod
Set the projected token audience to https://api.thalassa.cloud.
Kubernetes mounts a different token at /var/run/secrets/kubernetes.io/serviceaccount/token. That token is for the Kubernetes API. Do not send that token to Thalassa Cloud.
Use this manifest. It requests the Thalassa Cloud audience on a projected volume.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: default
---
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: default
spec:
serviceAccountName: my-app
containers:
- name: app
image: your-image
env:
- name: THALASSA_ORGANISATION_ID
value: "<organisation-id>"
- name: THALASSA_SERVICE_ACCOUNT_ID
value: "<thalassa-service-account-id>"
volumeMounts:
- name: thalassa-oidc
mountPath: /var/run/secrets/thalassa.cloud
readOnly: true
volumes:
- name: thalassa-oidc
projected:
sources:
- serviceAccountToken:
path: token
audience: https://api.thalassa.cloud
expirationSeconds: 3600Kubernetes writes the projected token to /var/run/secrets/thalassa.cloud/token. Kubernetes replaces this file when the token expires. Read this file before each token exchange.
For this example, the token claims are:
| Claim | Value |
|---|---|
sub | system:serviceaccount:default:my-app |
aud | https://api.thalassa.cloud |
| Issuer | The cluster identity provider |
Exchange the token
In the pod, exchange the projected token for a bearer token:
OIDC_TOKEN="$(cat /var/run/secrets/thalassa.cloud/token)"
BEARER_TOKEN="$(curl -sS -X POST https://api.thalassa.cloud/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=${OIDC_TOKEN}" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
-d "organisation_id=${THALASSA_ORGANISATION_ID}" \
-d "service_account_id=${THALASSA_SERVICE_ACCOUNT_ID}" \
| jq -r '.access_token')"Use BEARER_TOKEN for Thalassa Cloud API calls. The bearer token has a short life. When the bearer token expires, read the projected token again. Then do the exchange again.
curl -sS https://api.thalassa.cloud/v1/me \
-H "Authorization: Bearer ${BEARER_TOKEN}"Bootstrap with tcloud
You can use the CLI to create the Thalassa Cloud objects. Refer to tcloud iam workload-identity-federation bootstrap kubernetes.
The command creates these objects:
- A Thalassa Cloud service account
- The role bindings or the IAM policy bindings that you set
- A federated identity for
system:serviceaccount:<namespace>:<name>
On a Thalassa cluster, the command uses the identity provider with this label: kubernetes_cluster_id=<cluster identity>.
tcloud iam workload-identity-federation bootstrap kubernetes \
--cluster my-cluster-slug \
--namespace default \
--service-account my-app \
--role deployerUse --policy, or use --role. You can use both flags. Repeat a flag to add another binding. --cluster accepts the cluster identity, the slug, or the name.
The federated identity name is <name>-fi. If you do not set --name, the base name is wif-kubernetes-<key>.
The command adds these labels to the new resources:
thalassa.cloud/managed-by=workload-identity-bootstrapthalassa.cloud/wif-vcs=kubernetes
The default trusted audience is the API URL of the current tcloud context. This URL is usually https://api.thalassa.cloud. Set the projected token audience to this URL.
To use a different audience, set --trusted-audience. Set the same audience on the projected token.
Create the Kubernetes service account in the cluster. Mount the projected token in the pod. The bootstrap command does not change the pod.
Clusters that Thalassa Cloud does not manage
For these clusters, do not use --cluster. Use --issuer.
Set --issuer to the same URL as kube-apiserver --service-account-issuer. If the identity provider does not exist, the CLI creates it.
tcloud iam workload-identity-federation bootstrap kubernetes \
--issuer https://k8s.example.com \
--namespace cicd \
--service-account terraform \
--role deployerUse the same subject as for a Thalassa cluster. Use the same projected token. Use the same token exchange. The projected token audience must match a trusted audience on the federated identity.
Related documentation
- OIDC concepts — Token exchange and the three access controls
- Create a federated identity — Subject, audience, and API scopes in the console
- Service accounts — Thalassa Cloud service accounts and IAM roles
- tcloud iam workload-identity-federation bootstrap kubernetes — Command reference
- Troubleshooting — Wrong claims and permission errors after the exchange