Under a CI burst (a batch merge fires ~13 workflows x ~4 jobs, each doing an AppRole login = a raft write), the vault nodes return nginx 504s and the rate-limit quota returns 429s. Both are non-JSON, which crashed jq and hard-failed the job with a misleading "parse error". Add a bao_req helper that retries connect-failures/429/5xx with backoff+jitter (~60s over 6 tries) and surfaces real 4xx bodies unchanged. Transient throttling no longer fails CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bao-secrets — fetch OpenBao secrets in one step
Composite action for Gitea Actions (spikersoft-issues#545). AppRole-logs in to OpenBao, reads the KV v2 paths you list, and exports them as masked env vars for the rest of the job — so workflows stop carrying a pile of per-repo Actions secrets.
One-time setup per repo
- Provision the repo's AppRole (once, by an admin):
It prints a
export BAO_ADDR=https://bao.spikersoft.com BAO_TOKEN=<admin token> cd /mnt/infrastructure/openbao && bash provision-ci-approles.shrole_idandsecret_idper repo. - In the repo's Settings → Actions → Secrets, set exactly two:
BAO_ROLE_IDandBAO_SECRET_ID. These replaceDOCKER_PASSWORD,*_S3_SECRET_KEY,ACCESS_TOKEN_GITEA,DISCORD_*,HF_TOKEN, … — delete those once the workflow is migrated.
Use it in a workflow
steps:
- uses: spikerj/spikersoft-infrastructure/.gitea/actions/bao-secrets@master
with:
role-id: ${{ secrets.BAO_ROLE_ID }}
secret-id: ${{ secrets.BAO_SECRET_ID }}
secrets: |
DOCKER_PASSWORD secret/ci/shared/registry password
VIDEOS_S3_KEY secret/ci/backend/minio/videos secret_key
# DOCKER_PASSWORD and VIDEOS_S3_KEY are now normal (masked) env vars:
- run: echo "$DOCKER_PASSWORD" | docker login git.spikersoft.com -u ci --password-stdin
secrets format
One mapping per line: ENV_NAME <kv-v2-path> <field>, whitespace-separated.
The path is exactly what you'd pass to bao kv get. Blank lines and #
comments are ignored. Multiline values (PEM keys, etc.) are handled.
Inputs
| input | required | default | notes |
|---|---|---|---|
role-id |
yes | — | ${{ secrets.BAO_ROLE_ID }} |
secret-id |
yes | — | ${{ secrets.BAO_SECRET_ID }} |
secrets |
yes | — | the mapping block above |
bao-addr |
no | https://bao.spikersoft.com |
override for testing |
Notes
- Tokens issued to CI are short-lived (15m) and read-only, scoped to the
repo's own tree +
secret/ci/shared/*by its policy. The action revokes its token when done. - If a path is outside the role's policy the step fails loudly (
permission denied) rather than exporting an empty value — verified inprovision-ci-approles.sh's policy scoping. - Runner needs
curlandjq(present on the standard act_runner images). - Rotation:
secret_ids expire in 90 days — re-runprovision-ci-approles.shand update the two Actions secrets; nothing else changes.