End-to-End GitHub Actions on Kubernetes
This guide walks through a complete deployment: Helm install, trust policy creation, GitHub Action integration, token exchange, and verification.
Overview
You will:
- Install github-sts on Kubernetes via Helm
- Configure a trust policy for your workflow
- Run a workflow that uses the github-sts-action
- Exchange an OIDC token for a scoped GitHub installation token
- Verify audit logs and metrics
1. Install github-sts
helm repo add depthmark https://depthmark.github.io/charts
helm install github-sts depthmark/github-sts \
--namespace github-sts --create-namespace \
--version v0.1.0 \
--set apps.default.appId=123456 \
--set-file apps.default.privateKey=/path/to/private-key.pem \
--set oidc.requiredAudience=https://sts.example.com \
--set oidc.allowedIssuers[0]=https://token.actions.githubusercontent.comVerify:
kubectl get pods -n github-sts
kubectl logs -n github-sts deploy/github-sts2. Create a trust policy
In your target repository, create .github/sts/default/ci.sts.yaml:
issuer: https://token.actions.githubusercontent.com
subject: repo:myorg/myrepo:ref:refs/heads/main
audience: https://sts.example.com
permissions:
contents: read
pull_requests: writeCommit and push.
3. Configure the workflow
Create .github/workflows/deploy.yml:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: Depthmark/github-sts-action@v0.2.0
id: sts
with:
sts-url: https://sts.example.com
audience: https://sts.example.com
scope: ${{ github.repository }}
identity: ci
- name: Verify the exchange
run: |
# token is registered as a secret, so it prints as *** in the log.
# Testing that it is non-empty still works.
test -n "${{ steps.sts.outputs.token }}" && echo "Token received"
- name: Use the token
run: |
gh repo view ${{ github.repository }} --json name
env:
GH_TOKEN: ${{ steps.sts.outputs.token }}4. Run the workflow
Push to the main branch. The workflow will:
- Request an OIDC token with audience
https://sts.example.com - Send it to github-sts’s
/sts/exchangeendpoint - Receive a scoped installation token
- Use it to access the repository
5. Verify
Check the action output
The action outputs the token’s scope, app, identity, and permissions.
Check server audit logs
kubectl logs -n github-sts deploy/github-sts | grep "result.success"Expected fields:
result: successissuer: https://token.actions.githubusercontent.comsubject: repo:myorg/myrepo:ref:refs/heads/main
Check metrics
# Successful exchange
curl -s https://sts.example.com/metrics | grep githubsts_token_exchanges_total{result=\"success\"}
# Token issued
curl -s https://sts.example.com/metrics | grep githubsts_github_tokens_issued_total
# No errors
curl -s https://sts.example.com/metrics | grep githubsts_oidc_validation_errors_totalVerify token revocation
Tokens expire automatically after one hour. You can also manually revoke:
- name: Revoke token at job end
if: always()
run: |
curl -X DELETE https://api.github.com/installation/token \
-H "Authorization: Bearer ${{ steps.sts.outputs.token }}"Failure scenarios to test
1. Missing audience
Remove audience: from the trust policy. The policy fails to parse, and the exchange returns 502 with upstream_error.
2. Wrong audience
Keep audience: in the policy but request a different audience in the workflow. The exchange returns 403 with audience_mismatch.
3. Wrong branch
Change the workflow to trigger on a branch other than main. The exchange should fail with policy_denied because subject doesn’t match.
4. Wrong identity
Change identity: ci to identity: unknown. The exchange should fail with policy_not_found.
Troubleshooting
See Troubleshooting for the complete diagnostic guide.
Next
- Compatibility: verified component combinations
- Action inputs and outputs: every input and output, with defaults and validation rules
- Values Reference: every chart value, with defaults