Configuring Workload Identity Federation for GitLab CI
This guide explains how to configure workload identity federation for GitLab CI, allowing your GitLab pipelines to authenticate with Thalassa Cloud without storing long-lived credentials.
Prerequisites
Before configuring workload identity federation for GitLab CI, ensure you have:
- A GitLab project with CI/CD enabled
- Access to your Thalassa Cloud organisation
- A service account created in Thalassa Cloud with appropriate IAM roles
- Permissions to create identity providers and federated identities
- GitLab 15.7 or later (OIDC support)
Step 1: Create Identity Provider for GitLab
Step 1: Navigate to Identity Providers
Navigate to IAM → Identity Providers in your Thalassa Cloud Console.
Step 2: Create GitLab Identity Provider
Click “Create Identity Provider” and configure:
Basic Information:
- Name:
gitlab(or a descriptive name) - Description: Optional description, e.g., “GitLab CI OIDC provider”
OIDC Configuration:
- OIDC Issuer URL: Your GitLab instance’s issuer URL
- For GitLab.com:
https://gitlab.com - For self-hosted GitLab:
https://your-gitlab-instance.com - The issuer URL is the base URL of your GitLab instance
- For GitLab.com:
JWKS Configuration:
- Use Automatic Discovery (recommended)
- Leave the JWKS endpoint blank
- Thalassa Cloud will automatically discover the JWKS endpoint from GitLab’s well-known configuration at
https://your-gitlab-instance.com/.well-known/openid-configuration - For GitLab.com:
https://gitlab.com/.well-known/openid-configuration
Alternative: Custom JWKS Endpoint
- If automatic discovery doesn’t work, you can specify:
- For GitLab.com:
https://gitlab.com/-/jwks - For self-hosted GitLab:
https://your-gitlab-instance.com/-/jwks
- For GitLab.com:
Step 3: Save Identity Provider
Click “Create Identity Provider” to save the configuration.
Step 2: Create Federated Identity
Step 1: Navigate to Service Accounts
Navigate to IAM → Service Accounts in your Thalassa Cloud Console.
Step 2: Select Service Account
Select the service account you want to configure for GitLab CI federation.
Step 3: Create Federated Identity
Click “Add Federated Identity” or “Workload Identity Federation”.
Step 4: Configure Token Matching
Configure how tokens from GitLab CI are matched:
Identity Provider:
- Select the GitLab identity provider you created in Step 1
Token Matching:
GitLab CI OIDC tokens include several claims that you can use for matching:
Subject Claim (
sub): The most important claim for matching. Format varies:- Project path:
project_path:NAMESPACE/PROJECT:ref_type:BRANCH_TYPE:ref:BRANCH_NAME - Example for main branch:
project_path:mygroup/myproject:ref_type:branch:ref:main - Example for tags:
project_path:mygroup/myproject:ref_type:tag:ref:v1.0.0 - Example for merge requests:
project_path:mygroup/myproject:ref_type:merge_request:ref:123
- Project path:
Additional Claims (optional):
project_path: The full project path (e.g.,mygroup/myproject)namespace_path: The namespace/group path (e.g.,mygroup)project_id: The numeric project IDref: The git reference (e.g.,main,refs/heads/main)ref_type: The reference type (branch,tag,merge_request)pipeline_id: The pipeline IDpipeline_source: The pipeline source (e.g.,push,merge_request_event)environment: The environment name if using GitLab Environments
Recommended Matching Patterns:
For maximum security, use specific subject claims:
- Match specific project and branch:
project_path:mygroup/myproject:ref_type:branch:ref:main - Match production environment: Use environment-specific claims
- Match merge requests:
project_path:mygroup/myproject:ref_type:merge_request:ref:*
You can use wildcards for more flexibility:
- Match all branches in a project:
project_path:mygroup/myproject:ref_type:branch:ref:* - Match all projects in a group:
project_path:mygroup/*:ref_type:branch:ref:main
API Scopes:
- Select the API scopes your pipeline needs (e.g.,
read,write) - Follow the principle of least privilege—only grant necessary scopes
Expiry (Optional):
- Set an expiry date if this is a temporary federation
Step 5: Save Federated Identity
Click “Create Federated Identity” to complete the setup.
Step 3: Configure GitLab CI Pipeline
Now configure your GitLab CI pipeline to use workload identity federation.
Basic Pipeline Example
stages:
- deploy
deploy:
stage: deploy
image: alpine:latest
id_tokens:
THALASSA_ID_TOKEN:
aud: https://api.thalassa.cloud
before_script:
- apk add --no-cache curl jq
script:
# Exchange GitLab OIDC token for Thalassa Cloud bearer token
- |
BEARER_TOKEN=$(curl -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=${THALASSA_ID_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')
# Export bearer token for use in subsequent commands
- export THALASSA_TOKEN="${BEARER_TOKEN}"
# Use the token with Terraform or other tools
- terraform init
- terraform plan
- terraform apply -auto-approveUsing GitLab CI/CD Variables
Store your organisation ID and service account ID as GitLab CI/CD variables:
- Navigate to your project or group settings
- Go to Settings → CI/CD
- Expand Variables
- Add variables:
THALASSA_ORGANISATION_ID: Your Thalassa Cloud organisation ID (masked)THALASSA_SERVICE_ACCOUNT_ID: Your service account ID (masked)
Using GitLab Environments
For better security and control, you can use GitLab Environments:
deploy:production:
stage: deploy
environment:
name: production
image: alpine:latest
id_tokens:
THALASSA_ID_TOKEN:
aud: https://api.thalassa.cloud
script:
# ... same as aboveWhen using environments, you can match tokens based on environment-specific claims.
Advanced Example with Multiple Stages
stages:
- plan
- apply
variables:
THALASSA_API: "https://api.thalassa.cloud"
plan:
stage: plan
image: hashicorp/terraform:latest
id_tokens:
THALASSA_ID_TOKEN:
aud: ${THALASSA_API}
before_script:
# Exchange token and export
- |
export THALASSA_TOKEN=$(curl -X POST ${THALASSA_API}/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=${THALASSA_ID_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')
script:
- terraform init
- terraform plan -out=tfplan
apply:
stage: apply
image: hashicorp/terraform:latest
id_tokens:
THALASSA_ID_TOKEN:
aud: ${THALASSA_API}
when: manual
only:
- main
before_script:
- |
export THALASSA_TOKEN=$(curl -X POST ${THALASSA_API}/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=${THALASSA_ID_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')
script:
- terraform apply tfplanTesting the Configuration
- Trigger a pipeline by pushing to your repository or manually running a pipeline
- Monitor the pipeline to verify the OIDC token exchange succeeds
- Check logs to ensure the bearer token is being used correctly
- Verify permissions by testing API calls or Terraform operations
Troubleshooting
OIDC Token Exchange Fails
- Verify the subject claim in your federated identity matches the actual token claims
- Check that the identity provider issuer URL matches your GitLab instance
- Ensure the service account ID and organisation ID are correct
- Verify the
aud(audience) claim matches your API endpoint
Token Matching Fails
- Decode your OIDC token to inspect the actual claims:
# In GitLab CI, add a debug job debug: image: alpine:latest script: - apk add --no-cache jq - echo "${THALASSA_ID_TOKEN}" | cut -d. -f2 | base64 -d | jq - Compare the actual claims with your federated identity matching rules
- Adjust your subject claim or additional claims to match the token
Permission Denied
- Verify the service account has the required IAM roles
- Check that the API scopes configured in the federated identity match your needs
- Review the service account’s role bindings
Self-Hosted GitLab Issues
If using self-hosted GitLab, ensure:
- The issuer URL matches your GitLab instance URL exactly
- JWKS endpoint is accessible (Thalassa Cloud must be able to reach it)
- TLS/SSL certificates are valid
Best Practices
- Use Specific Subject Claims: Match specific projects and branches rather than using broad wildcards
- Use GitLab Environments: Leverage GitLab Environments for better security and pipeline control
- Least Privilege: Grant only the minimum required API scopes
- Separate Jobs: Use different service accounts for different stages (plan vs apply)
- Monitor Usage: Regularly review federated identity usage through audit logs
- Rotate Regularly: Set expiry dates and rotate federated identities periodically
- Use Protected Branches: Combine with GitLab protected branches for additional security
Related Documentation
- GitLab ID Token Authentication: Official GitLab documentation on OIDC authentication using ID tokens
- Workload Identity Federation Overview: General overview of workload identity federation
- GitHub Actions Configuration: Configure federation for GitHub Actions
- Generic OIDC Configuration: Configure federation for generic OIDC providers
- Service Accounts: Learn about service accounts