GitHub Actions with OIDC
GitHub Actions can give a job an OIDC token. The token is a JWT that GitHub signs. It names the repository, the git ref, the event, and the environment of the job.
ZeroVault accepts that token in place of an API key. You record which repository you trust. The job sends its OIDC token to ZeroVault and gets back a token for the ZeroVault API. That token is valid for 15 minutes.
An API key does not expire, reaches every project in the organization, and is readable by every workflow in the repository.
Set it up
Section titled “Set it up”Add the trust record from a terminal where zero is signed in:
zero ci trust add --repo octo-org/octo-repoThen give the job the id-token: write permission and delete the
ZERO_API_KEY secret:
permissions: id-token: write # GitHub gives no OIDC token without this
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npx @zeroapps/cli@0.7.0 vault run -p demo -e production -- ./build.shThe CLI reads the two ACTIONS_ID_TOKEN_REQUEST_* variables, requests an OIDC
token with the audience https://api.zeroapps.dev, and exchanges it. In the
job, zero whoami prints Credential: github actions.
The trust record is stored in a key-value store that is eventually consistent.
A run in the first minute after zero ci trust add can fail.
Private repositories
Section titled “Private repositories”ZeroVault matches the numeric owner id and repository id from the token. It does not match the repository name, because a name can move to a different repository.
zero ci trust add reads those ids from the GitHub API without a token, so it
cannot read a private repository. Set GITHUB_TOKEN, or pass the ids:
zero ci trust add --repo octo-org/private-repo --owner-id 65 --repo-id 74A refused run prints both ids in the error.
Limits on a trust record
Section titled “Limits on a trust record”A trusted workflow reads every secret in the organization. To make the trust smaller, add a condition:
zero ci trust add --repo octo-org/octo-repo --ref refs/heads/mainzero ci trust add --repo octo-org/octo-repo --environment production--ref must equal the ref claim of the token. --environment must equal the
environment claim.
Refused events
Section titled “Refused events”ZeroVault refuses three events unless the trust record allows them:
| Event | Claim in the token |
|---|---|
pull_request |
The job runs code from a branch that nobody reviewed |
pull_request_target |
The job runs with the base repository’s identity while a fork’s pull request is open |
workflow_run |
The job runs with the repository’s identity after another workflow |
zero ci trust add --repo octo-org/octo-repo --allow-event pull_requestOne --allow-event flag allows one event. The other two stay refused.
A fork’s pull_request run gets no OIDC token, because GitHub makes the
permissions of such a run read-only. A fork’s pull_request_target run does get
a token. That token contains the base repository’s ids and ref: refs/heads/main, so a --ref refs/heads/main condition matches it. Only the
event rule refuses it.
Manage trust records
Section titled “Manage trust records”zero ci trust list # id, repository, and conditionszero ci trust rm <id> # delete the recordAfter you delete a record, the next exchange fails. A token from an earlier exchange stays valid until it expires, for at most 15 minutes.
Where an API key is still necessary
Section titled “Where an API key is still necessary”- GitLab, CircleCI, and Buildkite sign OIDC tokens too, but ZeroVault accepts only GitHub’s.
- A server or a container has no OIDC issuer. Use an API key.
- A trust record cannot name one project. It gives access to the whole organization, the same as an API key.