Configuring Workload Identity Federation for GitHub Actions

This guide explains how to configure workload identity federation for GitHub Actions, allowing your GitHub workflows to authenticate with Thalassa Cloud without storing long-lived credentials.

Prerequisites

Before configuring workload identity federation for GitHub Actions, ensure you have:

  • A GitHub repository with Actions 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

Step 1: Create Identity Provider for GitHub Actions

Step 1: Navigate to Identity Providers

Navigate to IAMIdentity Providers in your Thalassa Cloud Console.

Step 2: Create GitHub Actions Identity Provider

Click “Create Identity Provider” and configure:

Basic Information:

  • Name: github-actions (or a descriptive name)
  • Description: Optional description, e.g., “GitHub Actions OIDC provider”

OIDC Configuration:

  • OIDC Issuer URL: https://token.actions.githubusercontent.com
    • This is GitHub’s OIDC issuer endpoint

JWKS Configuration:

  • Use Automatic Discovery (recommended)
    • Leave the JWKS endpoint blank
    • Thalassa Cloud will automatically discover the JWKS endpoint from GitHub’s well-known configuration at https://token.actions.githubusercontent.com/.well-known/openid-configuration

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 IAMService Accounts in your Thalassa Cloud Console.

Step 2: Select Service Account

Select the service account you want to configure for GitHub Actions federation.

Step 3: Create Federated Identity

Click “Add Federated Identity” or “Workload Identity Federation”.

Step 4: Configure Token Matching

Configure how tokens from GitHub Actions are matched:

Identity Provider:

  • Select the GitHub Actions identity provider you created in Step 1

Token Matching:

GitHub Actions OIDC tokens include several claims that you can use for matching:

  • Subject Claim (sub): The most important claim for matching. Format: repo:OWNER/REPO:ref:refs/heads/BRANCH or repo:OWNER/REPO:environment:ENVIRONMENT_NAME

    • Example for main branch: repo:myorg/myrepo:ref:refs/heads/main
    • Example for environment: repo:myorg/myrepo:environment:production
    • Example for pull requests: repo:myorg/myrepo:pull_request
  • Additional Claims (optional):

    • repository: The repository name (e.g., myorg/myrepo)
    • repository_owner: The repository owner (e.g., myorg)
    • ref: The git reference (e.g., refs/heads/main)
    • environment: The environment name if using GitHub Environments

Recommended Matching Patterns:

For maximum security, use specific subject claims:

  • Match specific repository and branch: repo:myorg/myrepo:ref:refs/heads/main
  • Match production environment: repo:myorg/myrepo:environment:production
  • Match pull requests: repo:myorg/myrepo:pull_request

You can use wildcards for more flexibility:

  • Match all branches in a repository: repo:myorg/myrepo:ref:refs/heads/*
  • Match all repositories in an organisation: repo:myorg/*:ref:refs/heads/main

API Scopes:

  • Select the API scopes your workflow 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 GitHub Actions Workflow

Now configure your GitHub Actions workflow to use workload identity federation.

Basic Workflow Example

name: Deploy to Thalassa Cloud

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # Required for OIDC token
      contents: read    # Required for repository access
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Get OIDC Token
        id: oidc
        uses: actions/github-script@v7
        with:
          script: |
            const token = await core.getIDToken();
            core.setOutput('token', token);

      - name: Exchange OIDC Token
        id: auth
        run: |
          # Exchange GitHub OIDC token for Thalassa Cloud bearer token
          RESPONSE=$(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=${{ steps.oidc.outputs.token }}" \
            -d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
            -d "organisation_id=${{ vars.THALASSA_ORGANISATION_ID }}" \
            -d "service_account_id=${{ vars.THALASSA_SERVICE_ACCOUNT_ID }}")
          
          BEARER_TOKEN=$(echo "$RESPONSE" | jq -r '.access_token')
          echo "token=$BEARER_TOKEN" >> $GITHUB_OUTPUT

      - name: Deploy with Terraform
        env:
          THALASSA_TOKEN: ${{ steps.auth.outputs.token }}
        run: |
          terraform init
          terraform plan
          terraform apply -auto-approve

Using GitHub Variables

Store your organisation ID and service account ID as GitHub repository or organisation variables:

  1. Navigate to your repository settings
  2. Go to Secrets and variablesActions
  3. Add repository variables:
    • THALASSA_ORGANISATION_ID: Your Thalassa Cloud organisation ID
    • THALASSA_SERVICE_ACCOUNT_ID: Your service account ID

Using GitHub Environments

For better security and control, you can use GitHub Environments to restrict which workflows can use your federated identity:

name: Deploy to Production

on:
  workflow_dispatch:
    inputs:
      environment:
        type: choice
        options:
          - production
          - staging

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}
    permissions:
      id-token: write
      contents: read
    
    steps:
      # ... same steps as above

When using environments, your subject claim should match the environment pattern: repo:OWNER/REPO:environment:ENVIRONMENT_NAME

Testing the Configuration

  1. Push to your repository to trigger the workflow
  2. Monitor the workflow to verify the OIDC token exchange succeeds
  3. Check logs to ensure the bearer token is being used correctly
  4. 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 is correct
  • Ensure the service account ID and organisation ID are correct

Token Matching Fails

  • Decode your OIDC token to inspect the actual claims:
    # In GitHub Actions, add a debug step
    - name: Debug Token Claims
      run: |
        echo "${{ steps.oidc.outputs.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

Best Practices

  • Use Specific Subject Claims: Match specific repositories and branches rather than using broad wildcards
  • Use GitHub Environments: Leverage GitHub Environments for better security and workflow control
  • Least Privilege: Grant only the minimum required API scopes
  • Monitor Usage: Regularly review federated identity usage through audit logs
  • Rotate Regularly: Set expiry dates and rotate federated identities periodically

Related Documentation