Skip to content

Use Workload Identity

Last updated on

Managing credentials for workloads is a critical security challenge. STACKIT Workload Identity eliminates the need for long-lived, static secrets (like API keys or OAuth client secrets) by federating Kubernetes workload identities via OIDC federation, securing access with short-lived, auto-rotated tokens.

For SKE clusters, this feature is natively integrated. A managed stackit-pod-identity-webhook intercepts pod creation to automatically inject Kubernetes projected service account tokens into your workloads. The workload then utilizes this OIDC token to perform a token exchange with an external Identity Provider (IdP) for short-lived access tokens. For example, if the workload application uses the functions of a STACKIT SDK, it accesses the STACKIT IdP via the STACKIT API.

  • A STACKIT project.
  • A STACKIT Service Account with the appropriate permissions for your application.
  • An SKE cluster.

This flow shows how Kubernetes workload identity is exchanged for a short-lived STACKIT access token:

  1. The stackit-pod-identity-webhook intercepts the Pod’s creation request and, based on ServiceAccount annotations, injects a projected volume containing a signed JWT (the ServiceAccount token) and corresponding environment variables into the Pod spec.

  2. The application’s SDK reads this injected token volume and sends the K8s ServiceAccount token to the STACKIT IdP.

  3. The STACKIT IdP validates the token signature using the cluster’s Public OIDC Discovery endpoint (JWKS) and checks the configured assertions (for example, if the cluster identity is allowed to federate the requested IdP identity).

  4. If the validation is successful the IdP returns a temporary scoped STACKIT access token.

  5. The application uses the returned temporary access token to call the STACKIT API.

In practice, this keeps credentials short-lived and avoids static secrets in your workloads.

To use Workload Identity, you must establish a trust relationship between your SKE cluster and the STACKIT Identity Provider (IdP).

  1. Navigate to the STACKIT Portal to configure Service Account Federation. Refer to the STACKIT IdP documentation on how to create, manage and delete federated identity providers.

  2. Provide your clusters serviceAccountIssuer URL. You can obtain the URL from your cluster status via the SKE API, the STACKIT CLI or by inspecting your clusters status in the STACKIT Portal.

    For example, using the STACKIT CLI:

    Terminal window
    stackit ske cluster describe -p <PROJECT_ID> <CLUSTER_NAME> -o json | jq '.status.serviceAccountIssuer'
  3. Create an assertion mapping using the sub (subject) claim to limit the usage of the STACKIT Service Account to a specific Kubernetes ServiceAccount.

  4. Format the sub claim exactly as system:serviceaccount:<namespace>:<k8s-serviceaccount>.

For more details about the claims of the cluster’s JWT see the Kubernetes docs about Schema for service account private claims.

You can configure the Service Account Federation via Terraform using the stackit_service_account_federated_identity_provider resource. The serviceAccountIssuer is available as the read-only service_account_issuer attribute on the stackit_ske_cluster resource:

resource "stackit_service_account_federated_identity_provider" "example" {
service_account_email = var.service_account_email
name = "ske-federation"
issuer = stackit_ske_cluster.example.service_account_issuer
assertions = [
{
item = "sub"
operator = "equals"
value = "system:serviceaccount:default:app"
},
{
item = "aud"
operator = "equals"
value = "sts.accounts.stackit.cloud"
}
]
}

Visit our Terraform documentation on this resource for more details.

Once the trust relationship is established, you can enable Workload Identity for specific Pods by annotating their associated Kubernetes ServiceAccount.

Here is a complete minimal manifest showing the annotated ServiceAccount and a Pod that uses it:

apiVersion: v1
kind: ServiceAccount
metadata:
name: app
namespace: default
annotations:
workload-identity.stackit.cloud/service-account-email: "<sa-email>"
---
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: default
spec:
serviceAccountName: app
automountServiceAccountToken: false
  • Set the workload-identity.stackit.cloud/service-account-email annotation on your Kubernetes ServiceAccount.
  • Ensure the annotation matches the STACKIT Service Account email configured in the Portal.
  • The webhook automatically injects the necessary environment variables (like STACKIT_SERVICE_ACCOUNT_EMAIL) and projected volume mounts for the STACKIT SDK to use.

For details, see the example workload provided in the stackitcloud/stackit-pod-identity-webhook GitHub repo.

The following annotations can be applied to a Kubernetes ServiceAccount to configure the identity injection:

You can also skip token injection for specific resources using labels:

  • Add the label workload-identity.stackit.cloud/skip-pod-identity-webhook: "true" to a Pod to skip mutation.
  • Add the label workload-identity.stackit.cloud/skip-pod-identity-webhook: "true" to a namespace to ignore all Pods within it.

Step 3: Consider security aspects and follow best practices

Section titled “Step 3: Consider security aspects and follow best practices”

When operating workloads using Workload Identity in SKE, keep the following behaviors and security best practices in mind.

If a ServiceAccount is used only for Workload Identity, set automountServiceAccountToken: false in the Pod spec. This prevents the ServiceAccount token from being mounted for generic API server communication, reducing the impact radius of a potential compromise.

Secure the VM metadata service with a network policy

Section titled “Secure the VM metadata service with a network policy”

By default, IaaS virtual machines can obtain their own ServiceAccount tokens, and Pods running on these VMs can also retrieve these tokens by reaching the underlying VM’s metadata service. This behavior is intentional and aligns with how other major cloud providers operate.

However, if you want to strictly isolate your cluster workloads and prevent them from assuming the VM’s infrastructure identity, you can deploy a Kubernetes NetworkPolicy to block egress traffic to the metadata server.

The standard IP address for the cloud instance metadata service is 169.254.169.254. You can restrict access to this IP by applying a NetworkPolicy that allows all egress except the metadata IP.

Here is an example NetworkPolicy that blocks egress traffic to the metadata service for all Pods in a namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress-except-metadata
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32
  • podSelector: {}: This empty selector applies the policy to all Pods in the namespace where the policy is deployed.
  • cidr: 0.0.0.0/0: This allows egress traffic to all destinations globally.
  • except: [169.254.169.254/32]: This explicitly carves out the metadata IP address, dropping any outgoing requests from your Pods attempting to reach it.

The process of rotating the cluster’s signing key is automated by the SKE API.

  • The IdP caches the JWKS response per URI for 1 hour. As long as the kid (key ID) used to sign the token is present on the cached keys, the IdP accepts the token.
  • After a successful key rotation, old tokens signed by the previous key remain valid for up to 1 hour.

When using the STACKIT SDK for Go, the SDK automatically detects the injected environment variables and handles the token exchange with the STACKIT IdP behind the scenes.

For details, see the example workload provided in the stackitcloud/stackit-pod-identity-webhook GitHub repo.