ID token
A job-scoped signed JWT requested with an audience.
Chapter 11 of 16 · CI user · GitLab pipeline
Exchange a job-scoped GitLab OIDC ID token for narrow Vault access without storing a reusable CI credential.
A GitLab project design and a platform-supplied Vault JWT role; no live pipeline is required.
A protected GitLab job can request one file-delivered field through an audience-bound ID token.
| Owner | Consumer-facing responsibility |
|---|---|
| You own | Owns job identity, protected refs, logs, artifacts, cancellation, and cleanup. |
| Platform team owns | Defines the exact value or file contract consumed by the job. |
| Vault owns | Owns JWT trust, claim bindings, policy, token TTL, and audit. |
A job-scoped signed JWT requested with an audience.
The recipient value Vault validates.
Project, namespace, ref, or protection constraints.
A temporary file whose variable value is its path.
vault-read:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.internal
secrets:
LOCKBOX_CONFIG:
vault: orders-api/feature_mode@lockbox-kv
token: $VAULT_ID_TOKEN
script:
- test -s "$LOCKBOX_CONFIG"
- test "$(wc -l < "$LOCKBOX_CONFIG")" -eq 1
{
"role_type": "jwt",
"policies": ["lockbox-gitlab"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_audiences": ["https://vault.example.internal"],
"bound_claims": {
"project_id": "42",
"ref_protected": "true"
}
}| Symptom | JWT login reports audience or claim failure. |
|---|---|
| Likely causes | Audience differs, issuer metadata changed, ref unprotected, or project claims do not match. |
| Inspect safely | Compare audience, project path, ref, protection, job time, and role name. |
| Do not print | ID token, Vault token, secret file, claims payload, or runner environment. |
Break it: Change the job ID-token audience so it no longer matches the Vault role.
Fix it: Restore the exact audience and re-check project, ref, protection, issuer, auth path, and mount.
Escalate with time, endpoint, auth path, role, mount, namespace when relevant, status code, request ID, and sanitized error class. Never attach a credential or response body.
Platform-supplied external configuration is represented by reserved example inputs; this lab applies nothing.
$0; offline validation with no external resource changes
Use the tracked example and offline validator; real GitLab, AWS, and Kubernetes configuration remains platform-owned.
Review the provided CI example and run its offline checker for audience, file delivery, logging, artifacts, and legacy token use.
cd learn-vault/labs/gitlab-ci
python3 check_example.py
sed -n '1,80p' .gitlab-ci.example.yml
git grep -n 'CI_JOB_JWT' -- . ':!learn-vault/labs/gitlab-ci/check_example.py' && exit 1 || true
Complete every item in the Verify section using metadata-only evidence; no secret value appears in terminal output or tracked files.
Use the Failure drill and Break it / fix it evidence fields. Stop before broadening policy, weakening identity or TLS checks, or copying secret-bearing diagnostics.
No external resources exist. Delete any copied job output before sharing logs.
The release job can read one Lockbox path from protected refs, with no reusable Vault variable.
Vault must know the token was minted for it.
Another valid GitLab job must not match this role.
A temporary file path.
No. Artifacts outlive the job and broaden access.
CI exchanges its existing job identity for short-lived access constrained to the exact trusted pipeline context.