Configure GitHub Actions OIDC
Set up Angos to accept GitHub Actions OIDC tokens for passwordless authentication from CI/CD pipelines.
Prerequisites
- Angos running with network access from GitHub Actions
- GitHub repository with Actions enabled
Configure the Registry
Step 1: Add OIDC Provider
Add the GitHub provider to config.toml:
[auth.oidc.github-actions]
provider = "github"
That's it for basic configuration. The registry automatically uses GitHub's default issuer and JWKS endpoints.
Step 2: Add Access Policy
Configure which repositories can access your registry:
[global.access_policy]
default_allow = false
rules = [
# Allow GitHub Actions from your organization
"identity.oidc != null && identity.oidc.claims['repository'].startsWith('myorg/')"
]
Step 3: Restart the Registry
./angos -c config.toml server
Configure GitHub Actions
Step 1: Add Permissions
Your workflow needs id-token: write permission:
name: Push to Registry
on: push
jobs:
push:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC
contents: read
Step 2: Get and Use the Token
steps:
- uses: actions/checkout@v4
- name: Get OIDC Token
id: get-token
run: |
TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/${{ github.repository }}" \
| jq -r '.value')
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Login to Registry
run: |
echo "${{ steps.get-token.outputs.token }}" | \
docker login registry.example.com \
--username github-actions \
--password-stdin
- name: Build and Push
run: |
docker build -t registry.example.com/myapp:${{ github.sha }} .
docker push registry.example.com/myapp:${{ github.sha }}
The username must match the provider name (github-actions in this example).
Policy Examples
Allow Specific Repositories
[repository."myapp".access_policy]
default_allow = false
rules = [
'''identity.oidc != null &&
identity.oidc.claims["repository"].matches("^myorg/(app1|app2|app3)$")'''
]
Allow Main Branch Only
[repository."production".access_policy]
default_allow = false
rules = [
'''identity.oidc != null &&
identity.oidc.claims["ref"] == "refs/heads/main" &&
identity.oidc.claims["repository"].startsWith("myorg/")'''
]
Allow Release Tags Only
[repository."releases".access_policy]
default_allow = false
rules = [
'''identity.oidc != null &&
identity.oidc.claims["ref"].matches("^refs/tags/v[0-9]+\\.[0-9]+\\.[0-9]+$")'''
]
Allow Specific Workflows
rules = [
'''identity.oidc != null &&
identity.oidc.claims["workflow"].matches("^(deploy|release)\\.yml$")'''
]
Allow Specific Environments
rules = [
'''identity.oidc != null &&
identity.oidc.claims["environment"] == "production"'''
]
Allow Specific Actors
rules = [
'''identity.oidc != null &&
identity.oidc.claims["actor"] in ["myuser", "dependabot[bot]", "renovate[bot]"]'''
]
Complete Example
config.toml
[server]
bind_address = "0.0.0.0"
port = 5000
[server.tls]
server_certificate_bundle = "/tls/server.crt"
server_private_key = "/tls/server.key"
[blob_store.fs]
root_dir = "/data"
[auth.oidc.github-actions]
provider = "github"
# Production: only main branch from specific repos
[repository."production".access_policy]
default_allow = false
rules = [
'''identity.oidc != null &&
identity.oidc.provider_name == "github-actions" &&
identity.oidc.claims["ref"] == "refs/heads/main" &&
identity.oidc.claims["repository"].matches("^myorg/(api|web|worker)$")'''
]
# Dev: any branch from org
[repository."dev".access_policy]
default_allow = false
rules = [
'''identity.oidc != null &&
identity.oidc.claims["repository"].startsWith("myorg/")'''
]
[ui]
enabled = true
GitHub Workflow
name: Build and Push
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: registry.example.com
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Get OIDC Token
id: oidc
run: |
TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://github.com/${{ github.repository }}" \
| jq -r '.value')
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Login to Registry
run: |
echo "${{ steps.oidc.outputs.token }}" | \
docker login $REGISTRY --username github-actions --password-stdin
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Verification
Check the registry logs for authentication details:
RUST_LOG=angos::command::server::auth=debug ./angos server
You should see:
OIDC token validated for provider github-actions
Claims: repository=myorg/myrepo, ref=refs/heads/main, actor=username
Troubleshooting
Token rejected:
- Check
id-token: writepermission is set - Verify the token audience matches expectations
- Enable debug logging to see validation details
Policy not matching:
- Always check
identity.oidc != nullfirst - Use bracket notation for claims:
identity.oidc.claims["claim"] - Check claim values in debug logs
Network errors:
- Ensure the registry can reach
token.actions.githubusercontent.comfor JWKS
Next Steps
- Configure Generic OIDC for other identity providers
- Set Up Access Control for comprehensive policies