Skip to main content

octo-sts-rs (GitHub Token Service)

Overview

octo-sts-rs is a Rust implementation of a GitHub App Short-Lived Token Service (STS). It provides secure, scoped GitHub authentication for CI/CD workflows by exchanging GitHub OIDC tokens for short-lived GitHub App tokens.

This service eliminates the need for long-lived GitHub credentials (such as Personal Access Tokens or SSH keys) in CI/CD pipelines, replacing them with ephemeral, scoped tokens that follow the principle of least privilege.

Architecture

PropertyValue
RuntimeGoogle Cloud Run
Regionnorthamerica-northeast2
Non-production domainsts.dev.badal.io
Key managementGoogle Cloud KMS (RSA 2048-bit asymmetric signing)
Service accountocto-sts-rust@{project}.iam.gserviceaccount.com
IAM roleroles/cloudkms.signerVerifier only

How It Works

The token exchange flow follows these steps:

GitHub Actions                octo-sts-rs              Cloud KMS          GitHub API
| | | |
| 1. Request OIDC token | | |
| (GitHub built-in) | | |
| | | |
| 2. Send OIDC token | | |
| + scope + identity | | |
|--------------------------->| | |
| | 3. Validate OIDC | |
| | token signature | |
| | | |
| | 4. Check trust | |
| | policy | |
| | | |
| | 5. Sign JWT | |
| |---------------------->| |
| | <-- signed JWT ------| |
| | | |
| | 6. Exchange JWT for | |
| | installation token| |
| |-------------------------------------->|
| | <-- scoped token ----| |
| | | |
| 7. Return scoped token | | |
|<---------------------------| | |

Detailed Steps

  1. GitHub Actions workflow requests a GitHub OIDC token -- This is a built-in capability of GitHub Actions; no external service is needed for this step.

  2. Workflow sends the OIDC token to the exchange endpoint:

    https://sts.dev.badal.io/sts/exchange?scope={scope}&identity={identity}
  3. octo-sts-rs validates the OIDC token signature -- Confirms the token was issued by GitHub and has not been tampered with.

  4. Trust policy check -- Verifies that the requesting identity (repository, workflow, branch) is authorized to request a token with the given scope.

  5. JWT signing via Cloud KMS -- The service signs a JWT using the GitHub App private key stored in Cloud KMS. The private key never leaves KMS; the service only has permission to sign.

  6. Token exchange -- The signed JWT is exchanged with the GitHub API for a scoped, short-lived GitHub App installation token.

  7. Return token -- The scoped token is returned to the calling workflow.

Infrastructure Components

ComponentDetails
KMS Key Ringocto-sts-keyring
Crypto Keyocto-sts-github-app-key (RSA 2048-bit asymmetric signing)
Cloud Run1 CPU, 512MB memory, 0-10 instances
Artifact Registryocto-sts-rust-non-production (cleanup: 10 recent, untagged after 7 days)
Image Sourceghcr.io/epiphytic/octo-sts-rust mirrored to Google Artifact Registry

GitHub Actions Integration

A custom GitHub Action wraps the token exchange for easy use in workflows.

Action Location

devex-reusable-workflows/.github/actions/octo-sts/token/action.yml

Usage

- uses: badal-io/devex-reusable-workflows/.github/actions/octo-sts/token@main
with:
scope: "badal-io" # Target org or org/repo
identity: "my-identity" # Trust policy identity name
domain: "sts.dev.badal.io" # octo-sts domain

Inputs

InputDescriptionRequired
scopeTarget organization or org/repo for the tokenYes
identityTrust policy identity nameYes
domainocto-sts-rs domainYes

Outputs

OutputDescription
tokenScoped, short-lived GitHub App token

Example Workflow

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC token
steps:
- name: Get GitHub token
id: octo-sts
uses: badal-io/devex-reusable-workflows/.github/actions/octo-sts/token@main
with:
scope: "badal-io"
identity: "deploy-identity"
domain: "sts.dev.badal.io"

- name: Use the token
env:
GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }}
run: |
gh repo list badal-io --limit 5

Trust Policies

Trust policies define which workflow identities can request tokens and what permissions those tokens will have.

Policy Location

Trust policies are stored as YAML files in repositories:

.github/chainguard/*.sts.yaml

Policy Structure

A trust policy specifies:

  • Which workflow identities can request tokens (repository, branch, workflow path)
  • What scope the token covers (entire organization or specific repositories)
  • What permissions the resulting token will have (read, write, admin on specific resources)

Security Features

No Long-Lived Credentials

The entire authentication chain uses ephemeral credentials:

  • GitHub OIDC tokens are short-lived and scoped to the running workflow
  • Workload Identity Federation provides GCP access without service account keys
  • The resulting GitHub App tokens expire automatically

KMS-Protected Keys

The GitHub App private key is stored exclusively in Google Cloud KMS:

  • The key material never leaves KMS
  • The octo-sts-rust service account only has roles/cloudkms.signerVerifier -- it can sign payloads but cannot export or read the key
  • All signing operations are logged in Cloud Audit Logs

Scoped Tokens

Tokens are scoped based on trust policies:

  • Minimal permissions following the principle of least privilege
  • Tokens can be scoped to an organization or a specific repository
  • Permission sets match only what the consuming workflow needs

Short-Lived Tokens

  • GitHub App installation tokens typically expire in 1 hour
  • No token refresh or caching -- each workflow run gets a fresh token

Audit Trail

  • All token exchange requests are logged in Cloud Run logs
  • KMS signing operations are logged in Cloud Audit Logs
  • GitHub token usage is tracked in the GitHub audit log

Terraform Configuration

The infrastructure for octo-sts-rs is managed via Terraform in the devex-backstage-mly9n repository:

PathPurpose
terraform/shared-octo-sts-rust/Shared Terraform module for octo-sts-rs resources
terraform/non-production/main.tfNon-production environment configuration
.github/workflows/octo-sts-rust-deploy.ymlDeployment workflow

Deployment Flow

The deployment process follows these steps:

  1. Pull image from the upstream source: ghcr.io/epiphytic/octo-sts-rust
  2. Tag and push the image to Google Artifact Registry
  3. Deploy to Cloud Run with the following environment variables:
VariableDescription
GITHUB_APP_IDThe GitHub App ID for token generation
KMS_KEY_NAMEFull resource path to the KMS crypto key version
DOMAINThe service domain (e.g., sts.dev.badal.io)
GITHUB_WEBHOOK_SECRETSecret for validating GitHub webhook payloads

Repositories

RepositoryPurpose
github.com/epiphytic/octo-sts-rustUpstream source code for octo-sts-rs
badal-io/devex-backstage-mly9nDeployment configuration and Terraform infrastructure
badal-io/devex-reusable-workflowsGitHub Action for token exchange