Chapter 27Lesson 03~205 minutes

ID Tokens, OIDC Workload Identity, Cloud Federation, Vault Authentication, and Secretless Deployments: Configuration, Design Choices, and Tradeoffs

Choose deliberately between static secrets and federation, broad and narrow trust, service-specific audiences, and direct-provider versus Vault-broker designs.

DesignFederationVaultProvider trustPortability

Learning objectives

  • Choose between static secrets and OIDC federation based on provider support, blast radius, operational burden and auditability.
  • Design trust conditions around stable project identity plus ref/environment context instead of broad wildcard subjects.
  • Use service-specific audiences and avoid accidental cross-service token reuse.
  • Compare direct cloud federation with a Vault-broker architecture.
  • Predict the repository, GitLab, provider and external states affected by each design.

1. Start with authorization invariants, not provider syntax

A provider-specific JSON policy is implementation detail. First define the invariant in plain language: “Only jobs running in project 42001, from protected main, targeting staging, may assume role staging-writer, and that role may mutate only the staging namespace.” Then encode the same invariant using the provider's supported claims and policy language.

This keeps cloud migration possible. AWS, Azure, GCP and Vault use different federation mechanics, but they can all enforce a narrow trust relationship.

2. Static secret versus OIDC federation

Choice Advantages Risks / operational cost Evidence
Static provider secret Works with providers that lack OIDC; simple mental model. Rotation, storage, copying, leak persistence, offboarding, secret exposure to eligible jobs. Secret version/owner/rotation + scope + use logs; never value.
OIDC federation No stored cloud key; short-lived credentials; job context can enter authorization. Provider trust policy becomes critical; issuer/JWKS reachability and claim mapping must be correct. Token request/audience + provider decision + role/scope/expiry + external action.

Federation is preferable where supported, but “secretless” describes the absence of a stored long-lived provider secret in GitLab. The provider still issues a credential after exchange, and that temporary credential must be protected.

3. Broad project trust versus ref/environment-specific trust

Too broad:
  issuer = GitLab
  subject starts with project_path:academy/app
  => every branch may assume deployment role

Narrower invariant:
  issuer = GitLab
  audience = deployment STS
  project/job_project ID = expected project
  ref = main
  ref_protected = true
  environment = production
  deployment_tier = production
  => only the intended deployment context can exchange

Do not assume every provider supports every GitLab custom claim in exactly the same way. GitLab's current docs explicitly note that AWS on GitLab.com exposes additional GitLab condition keys, whereas self-managed/Dedicated AWS federation supports only sub as an AWS condition key. Design to the actual provider/offering capability and document the limitation.

4. One broad audience versus service-specific audiences

deploy_cloud:
  id_tokens:
    CLOUD_ID_TOKEN:
      aud: https://sts.cloud.example
  script:
    - ./cloud-exchange "$CLOUD_ID_TOKEN"

read_vault:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  script:
    - ./vault-exchange "$VAULT_ID_TOKEN"

Separate audiences make accidental token reuse visible. GitLab also permits an audience array, but granting one token multiple relying parties increases the surface in which that assertion might be accepted. Use multiple audiences only when the relying-party design truly requires it.

5. Direct provider federation versus Vault broker

Architecture Good fit Tradeoffs
Direct GitLab → cloud provider Few providers/roles; provider has strong OIDC federation; team owns cloud IAM. Simpler path, but trust policy is duplicated across providers/accounts.
GitLab → Vault → provider/secret Central secret governance, dynamic credentials, multiple backends, policy managed by security/platform team. Extra service availability/operations; Vault policy/auth method becomes critical; native GitLab Vault integration is Premium/Ultimate.

Vault can reduce provider-specific logic in application pipelines, but it is not automatically safer. A broad Vault role that returns powerful credentials simply centralizes the same authorization flaw.

6. Subject design and stable identifiers

The default sub is project-path/ref-oriented. Current GitLab also offers stable project/namespace IDs and job-project variants. Prefer trust rules that cannot silently follow a renamed group/project into unintended authority. On providers with only sub support, keep the subject exact and pair it with branch/tag protections and careful project administration.

Do not authorize by mutable user identity alone. GitLab warns against using user_login or user_email as the only condition where providers expose those claims.

7. Environments make deployment intent observable

If a job declares an environment, current ID tokens can carry environment name, protection state, deployment tier and action. This lets a provider distinguish a staging job from a production job even when both run from the same project/ref. That is useful defense in depth, but only if the environment declaration itself is governed and the provider checks it.

8. Token lifetime versus job timeout

Because ID-token expiry follows the job timeout when specified, very long deployment timeouts also lengthen the assertion lifetime. Set realistic job timeouts and design provider credential sessions separately. Do not increase job timeouts merely to “fix” an exchange that fails for an unrelated audience or trust-condition problem.

9. Keep repository, GitLab, runner and provider state distinct

State layer Evidence to capture Why it matters
Source/revision CI_PIPELINE_SOURCE, ref/ref type, CI_COMMIT_SHA, pipeline/job IDs Workload identity must be tied to the exact job and source hypothesis being authorized.
Compiled CI configuration Merged YAML, id_tokens request, rules result, environment declaration, CI config SHA Proves which token request and deployment job GitLab actually compiled.
Job/runner Job ID, runner ID/environment, executor/image/tool versions The token belongs to a job execution context, not to a repository in the abstract.
OIDC token request Token variable name, audience, intended relying party The audience is an authorization boundary; do not mint one broad token for unrelated services by default.
Token claims Issuer, subject, project/job IDs, ref, ref_protected, environment, SHA, expiry These are provider inputs. Record safe selected claims, never the raw live JWT.
Provider/Vault trust Issuer/JWKS, audience, bound subject/IDs/ref/environment conditions, role/policy version The external service—not GitLab—decides whether the presented identity is trusted.
Short-lived credential Credential class, role, scope, issue/expiry metadata; never secret material Federation reduces stored-secret lifetime only if the exchanged credential is itself narrow.
External action Exact API/resource/namespace changed, request/result ID, read-back verification Authentication success is not deployment success or target health.
Governance Trust-policy review, owner, emergency change evidence, expiration/rotation expectations Provider-side trust is production authorization code and must be reviewable.

10. Worked design: production deployment

Suppose a team deploys one artifact to staging and production. The production role may write only one namespace. The project is private and the production environment is protected. A safe design can require: exact job project ID, main, protected ref, production environment/tier, a production-only audience, and a provider role whose permissions cover only the production namespace. The pipeline rule and protected environment control reduce accidental job creation; the provider policy independently limits credential issuance.

Decision Choice Why Observable proof
Identity OIDC, not static key Short-lived job-specific assertion. id_tokens config + provider exchange metadata.
Audience Production STS only No cross-service reuse. aud request + provider expected audience.
Project Stable job project ID Survives path rename and handles job-project context. Selected safe claim + provider condition.
Ref Protected main Feature branches cannot exchange. ref, ref_protected, denied negative test.
Environment production + production tier Staging job cannot assume production role. Environment claims + provider conditions.
Provider permission One namespace write Short-lived does not mean broad. Role policy hash + successful bounded action + denied out-of-scope action.

11. Tier/offering and trust prerequisites

  • ID tokens: Free/Premium/Ultimate on GitLab.com, Self-Managed, Dedicated.
  • Native GitLab external-secrets/Vault keyword flows: currently Premium/Ultimate.
  • AWS/Azure/GCP federation: GitLab side is available on Free; the provider account and IAM privileges needed to configure federation are external prerequisites.
  • Self-managed GitLab: the relying party generally needs reachable OIDC discovery/signing metadata; do not weaken TLS to work around reachability.

12. Migration from static keys

Migrate one role/action at a time. First establish federation and prove allowed + denied cases. Then run read-only or no-op operations. Next grant the narrow mutation. Only after the federated path is verified should you revoke the old long-lived key. Preserve evidence of the revocation and ensure rollback does not mean silently reintroducing a forgotten administrator key.

Knowledge check

Why can direct cloud federation be safer operationally than a static secret?

Why is a single audience for Vault and cloud STS usually a poor default?

A provider supports only sub. What should you do?

Does a protected GitLab environment replace provider least privilege?

What is the safe rollback during a federation migration?

Next lesson

Bridge to diagnostics

Next, you will diagnose failures by identifying the exact layer: token request, claims, audience, provider trust, temporary role permission, or external action.

Version and compatibility note

GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.

Official references and version notes

Documentation verification date: 2026-09-12. GitLab documents explicit id_tokens as available on Free, Premium, and Ultimate across GitLab.com, Self-Managed, and Dedicated. CI_JOB_JWT/CI_JOB_JWT_V2 were removed in GitLab 17.0; use explicit ID tokens instead. ID tokens are RS256-signed. Their expiry is the job timeout when one is specified, otherwise five minutes. Current custom claims include project/namespace identity, job identity, ref/ref protection, pipeline source, runner identity, source SHA, CI configuration identity, and—when the job declares an environment—environment name/protection/tier/action. GitLab recommends stable IDs such as project/namespace IDs in trust policy conditions where the target provider supports them. Native secrets:vault integration is Premium/Ultimate, while ID-token federation itself is Free. Provider claim support is not uniform. Always verify the exact provider/offering guidance before translating a conceptual condition into AWS IAM, Entra federated credentials, Google CEL/attribute conditions, or Vault bound claims.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.