Skip to content

Octo STS

GitHub App

Octo STS

GitHub App

A "Security Token Service" for GitHub

tl;dr Octo STS is a GitHub App that acts as a "Security Token Service" (aka
STS) for the GitHub API.

Octo STS enables workloads running anywhere to exchange short-lived OIDC tokens
for short-lived GitHub credentials thereby eliminating the need for those
workloads to manage long-lived GitHub credentials.

Leveraging Octo STS, Chainguard has been able to eliminate a small horde of
assorted long-lived GitHub credentials including:

  • Dozens of Classic and Fine-grained "Personal Access Tokens" (aka PATs),
  • A handful of dedicated GitHub App private keys,
  • A handful of "deploy keys".

Chainguard does not currently offer any level of support for Octo STS, but if you are interested then please reach out to us.

Setting up workload trust

For the App to produce credentials that work with resources in your-org
it must be installed into your-org and have access to any repositories (e.g. your-repo)
that you will want workloads to be able to interact with.

Due to the way Github Apps work, the App must ask for a superset of the
permissions that any user might ask for in a token, so the full set of
permissions the App requests is large, but the only permission the
App itself uses is contents: read exclusively for reading trust policies.
All of the remaining permissions are requested exclusively for producing
tokens as specified in trust policies.

The Trust Policy

Trust policies are checked into .github/chainguard/{name}.sts.yaml, and
consist of a few key parts:

  1. The claim matching criteria for federation,
  2. The permissions to grant the identity, and
  3. (for Org-level policies) The list of repositories to grant access.

Here is a simple example that allows the Github actions workflows in
chainguard-dev/foo running on the main branch to read the repo contents and
interact with issues:

issuer: https://token.actions.githubusercontent.com
subject: repo:chainguard-dev/foo:ref:refs/heads/main

permissions:
  contents: read
  issues: write

The Trust Policy can also match the issuer, subject, and even custom claims with
regular expressions. For example:

issuer: https://accounts.google.com
subject_pattern: '[0-9]+'
claim_pattern:
  email: '.*@chainguard\.dev'

permissions:
  contents: read

This policy will allow OIDC tokens from Google accounts of folks with a
Chainguard email address to federate and read the repo contents.

Federating a token (GitHub Actions)

To support federation from GitHub Actions, we provide
octo-sts/action here, which can be used like so:

permissions:
  id-token: write # Needed to federate tokens.

steps:
- uses: octo-sts/action@6177b4481c00308b3839969c3eca88c96a91775f # v1.0.0
  id: octo-sts
  with:
    scope: your-org/your-repo
    identity: foo

- env:
    GITHUB_TOKEN: ${{ steps.octo-sts.outputs.token }}
  run: |
    gh repo list

The App will attempt to load the trust policy from
.github/chainguard/foo.sts.yaml from your-org/your-repo and if the
workflow's identity token satisfies the trust policy, it will return a token
with the enclosed permissions.

Federating a token (Go)

The Github App implements a subset of the Chainguard SecurityTokenService GRPC
service definition here, and we have some higher-level libraries
around this.

Here is an example program that takes in an input token via -token, performs
the federation, and prints the resulting token to STDOUT:

package main

import (
	"context"
	"flag"
	"log"
	"os"

	"chainguard.dev/sdk/sts"
)

var token = flag.String("token", "", "Bot token")

func main() {
	flag.Parse()

	xchg := sts.New(
		"https://octo-sts.dev",
		"does-not-matter",
		sts.WithScope("your-org/your-repo"),
		sts.WithIdentity("foo"),
	)

	res, err := xchg.Exchange(context.Background(), *token)
	if err != nil {
		log.Fatalf("exchange failed: %v", err)
	}

	os.Stdout.WriteString(res)
}

The App will attempt to load the trust policy from
.github/chainguard/foo.sts.yaml from your-org/your-repo and if the
workflow's identity token satisfies the trust policy, it will return a token
with the enclosed permissions.

Federating a token (low-level)

If you would like to try this out on the command-line with curl, here is how.

If a ${TOKEN} suitable for federation is sent like so:

curl -H "Authorization: Bearer ${TOKEN}" \
  "https://octo-sts.dev/sts/exchange?scope=your-org/your-repo&identity=foo"

The App will attempt to load the trust policy from
.github/chainguard/foo.sts.yaml from your-org/your-repo and if the
workflow's identity token satisfies the trust policy, it will return a token
with the enclosed permissions.

Developer

Octo STS is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.

Report abuse