Chapter 07Lesson 01~145 minutes

Secrets Management, External Secret Providers, Protected Data, Rotation, and Least-Privilege Patterns: Concepts, Architecture, and Mental Model

Secrets are not just variables with more careful names. A production secret has an owner, storage system, authorization decision, retrieval path, lifetime, rotation and revocation procedure, exposure surface, and audit trail. This lesson separates those states so a pipeline can prove who was allowed to retrieve which secret reference without ever treating log masking as the security boundary.

SecretsLeast privilegeAuthorizationRotationAudit evidence

Learning objectives

  • Separate secret storage, authorization, retrieval, consumption, log redaction, rotation, revocation, and audit evidence instead of calling all of them “secret management.”
  • Explain why masked/hidden/protected CI/CD variables are useful controls but are not equivalent to an external secrets-management system.
  • Describe the job-identity → provider trust policy → narrow secret request → temporary materialization → cleanup lifecycle for external secrets.
  • Explain why OIDC/ID-token audience and provider-side claims are authorization boundaries, and why an ID token must never be printed or persisted.
  • Identify the minimum evidence needed to prove secret access without preserving the secret value itself.
Chapter boundary: Chapter 06 taught how variables are scoped and resolved. Chapter 07 asks a different question: should this sensitive value exist as a broadly available CI/CD variable at all? The answer depends on secret lifetime, retrieval authorization, rotation, provider trust, and incident response—not just masking.

1. The problem: a hidden string is not a secret lifecycle

A pipeline can store a value in GitLab settings, mark it masked, hide it in the UI, protect it to a branch, and still have a weak secret-management design. Once an authorized job receives the value, repository-controlled code can read it. If the value is long-lived, shared broadly, never rotated, or copied into artifacts, the real risk exists outside the masking feature.

A production secret therefore needs several independently reviewable states: where it is stored, which workload identity may request it, which exact secret reference/version is requested, how it reaches the process, how long it remains valid, how it is rotated or revoked, and what evidence can be retained without retaining the secret.

2. The secret-lifecycle mental model

Causal secret lifecycle — storage is only one node
            flowchart TD
              A[Pipeline source + immutable SHA] --> B[Authorized job identity]
              B --> C[Provider trust policy]
              C --> D[Secret reference + version]
              D --> E[Narrow retrieval]
              E --> F[Temporary file / in-memory use]
              F --> G[Application or tool]
              G --> H[Redacted non-secret evidence]
              H --> I[Rotation / revocation]
              I --> J[Audit + incident evidence]
          

The critical arrow is job identity → provider trust policy. A provider does not become safe because GitLab generated a token. The provider must validate issuer, audience, project/ref/environment or other claims that match the intended trust boundary. Retrieval then requests one specific secret—not every credential in a vault.

3. Core objects: name each boundary before using it

Object Meaning Evidence without secret value
Secret reference A provider/path/name identifying sensitive material. Provider name, logical path/name, version/rotation metadata.
Job identity The workload allowed to request a secret. Pipeline/job ID, source/ref/SHA, ID-token claims decoded only when safe, runner identity.
Provider trust policy Rules deciding which identities may authenticate/authorize. Expected issuer/audience/project/ref/environment bindings; provider policy revision.
Secret material The sensitive bytes used by a tool/process. Presence, temporary path, byte count, digest only when appropriate; never raw value.
Rotation Replacement of active material with a newer version. Old/new version IDs, change timestamp, owner, validation result.
Revocation Removal of the old credential’s ability to authorize. Denied old version/token and provider/audit event.
Audit evidence Non-secret record of what happened. Who/what requested which reference, when, with which result.

4. Variables are a transport/configuration mechanism, not a complete vault

GitLab’s pipeline-security guidance explicitly treats CI/CD variables as less secure than dedicated secret-management providers for sensitive data. Variables are convenient, but they are stored in project/group/instance settings, can be overridden according to precedence, and can be exposed by misconfigured or malicious job code.

If sensitive data must temporarily remain in a CI/CD variable, use the available controls together: mask it, hide it, protect it where practical, scope it narrowly, restrict who can run code in the trusted context, and keep the lifetime short. Those controls reduce accidental exposure; they do not replace rotation, provider authorization, or incident response.

Masking is not authorization. GitLab replaces matching values in normal job logs, but transformed output can evade masking and job code that receives the value can send it elsewhere. Treat masking as log hygiene, not a sandbox.

5. External providers change the retrieval model

Current GitLab supports integrations with HashiCorp Vault, Google Cloud Secret Manager, Azure Key Vault, and AWS Secrets Manager. Unlike ordinary CI/CD variables that can be present in jobs by configuration scope, external secrets are explicitly requested by the job. GitLab’s documented provider integrations are currently Premium/Ultimate.

The design benefit is not simply “the secret lives somewhere else.” A mature provider supplies a separate authorization policy, versions, rotation, revocation, access audit, and narrower retrieval. The CI configuration should identify the required secret reference while the provider decides whether the job identity is allowed to receive it.

6. Federated identity: authenticate the job, not a copied long-lived key

GitLab id_tokens can mint OIDC-compatible ID tokens for a job. ID tokens are available across GitLab tiers, but the external service must still validate the token correctly. Use an explicit aud that represents the intended relying service and bind provider policy to meaningful GitLab claims.

secret_consumer:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.invalid
  script:
    - test -n "$VAULT_ID_TOKEN"
    - echo "id_token_present=yes"   # never print the token

This proves only that the job received an ID token. It does not prove the provider will authorize the job. Provider-side trust policy should restrict the intended issuer/audience and relevant project/ref/environment identity rather than accepting every token from the GitLab instance.

7. The secrets keyword requests narrow material

For supported integrations, GitLab Runner resolves a job’s requested secret before or around job execution according to current provider semantics. A Vault example uses a job ID token plus a specific provider path. By default, retrieved secret values are commonly exposed as temporary files and the environment variable contains the file path.

vault_consumer:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.invalid
  secrets:
    DB_PASSWORD:
      vault: lab/db/password@ops
      token: $VAULT_ID_TOKEN
  script:
    - test -f "$DB_PASSWORD"
    - printf 'secret_file_present=yes\n'
    - printf 'secret_bytes=%s\n' "$(wc -c < "$DB_PASSWORD")"
Example-only provider coordinates: example.invalid and lab/db/password are documentation placeholders. Do not copy a production secret path, token, or provider endpoint into course evidence.

8. GitLab Secrets Manager is a separate, evolving product boundary

GitLab now documents a native Secrets Manager for projects and groups. At the current documentation baseline it is Premium/Ultimate, Limited Availability on GitLab.com, Beta on Self-Managed, and uses GitLab Credits/billing semantics on GitLab.com. CI access currently requires GitLab Runner 19.0 or later.

That makes it useful to understand architecturally, but unsuitable as a mandatory beginner lab dependency. The required course path stays free/disposable-compatible with a faithful local provider simulation. When evaluating the native manager in a real organization, re-check availability, permissions, Runner compatibility, branch/environment scope, billing, and deletion/transfer semantics at that time.

9. Rotation and revocation are different completion conditions

Rotation creates or activates new material. Revocation proves old material no longer authorizes access. A rotation procedure is incomplete if the old key/token still works indefinitely.

  1. Create/activate v2 while v1 still exists if the service needs overlap.
  2. Update the authorized consumer to use v2 and verify the exact workload.
  3. Revoke/deactivate v1.
  4. Prove v1 is denied and v2 succeeds.
  5. Record evidence and remove temporary material from the runner/workspace.

For an exposed credential, rotate/revoke first; merely deleting the job log or repository line does not make the leaked credential safe again.

10. Read-only inspection before any secret mutation

printf 'pipeline_source=%s\n' "$CI_PIPELINE_SOURCE"
printf 'pipeline_id=%s job_id=%s\n' "$CI_PIPELINE_ID" "$CI_JOB_ID"
printf 'ref=%s sha=%s\n' "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"
printf 'runner_id=%s\n' "${CI_RUNNER_ID:-unknown}"
printf 'runner_desc=%s\n' "${CI_RUNNER_DESCRIPTION:-unknown}"
# Inspect only metadata for the specific synthetic secret/provider you own.
# Never run: env, printenv, set -x, or token-decoding that prints credentials.

For real providers, record the provider name, secret reference/version, trust-policy identity, and retrieval result—not the secret value. For project/group variables, inspect visibility/protection/scope metadata only.

11. Secret-management mistakes to eliminate early

  • “Masked means safe from malicious jobs.” It does not; code receiving the secret can transform or exfiltrate it.
  • “Protected branch means every job on it deserves every secret.” Secret authorization should still be workload-specific and least privilege.
  • “Moving a password to a group variable creates centralized secrets management.” It centralizes storage but can broaden inheritance and does not add provider-side lifecycle controls.
  • “OIDC removes authorization design.” OIDC authenticates identity; provider trust policy decides what that identity may do.
  • “Rotation is done when v2 works.” You must also prove v1 is revoked.
  • “Deleting leaked output fixes exposure.” Treat the credential as compromised and rotate/revoke it.
Next lesson

Guided Hands-On Workflow and Core Operations

Use only generated synthetic values to compare a masked/protected variable with a job-scoped provider simulation, then rotate and revoke a fake secret without logging it.

Knowledge check

Why is a masked CI/CD variable not equivalent to a secrets manager?

What must an external provider validate besides “this token came from GitLab”?

What proves a rotation is complete?

Should an evidence packet contain a raw secret so an auditor can reproduce the run?

Why is the native GitLab Secrets Manager optional in this chapter?

Official references and version notes

  • Pipeline security — current guidance that CI/CD variables are less secure than dedicated secret-management providers and that sensitive values should use stronger secret-management controls where possible.
  • CI/CD variables — masking, hiding, protection, fork/MR behavior, file variables, and explicit warnings that masking is not a defense against malicious job code.
  • External secrets in CI/CD — current supported provider integrations and tier/offering requirements.
  • OIDC authentication using ID tokens — job-scoped ID tokens, audiences, claims, and third-party trust boundaries.
  • HashiCorp Vault secrets — id_tokens, secrets:vault, file materialization, and provider-side authorization.
  • GitLab Secrets Manager — current limited/beta availability, permissions, branch/environment scoping, Runner requirements, rotation reminders, and file-by-default behavior.
  • Secret detection — defense-in-depth for accidentally committed credentials; detection is not a substitute for rotation after exposure.
Version and compatibility note

Secret-management behavior in this chapter was rechecked against current primary GitLab documentation on 2026-09-11. External-secret integrations documented by GitLab are currently Premium/Ultimate; ID tokens are available on Free/Premium/Ultimate. GitLab Secrets Manager is availability/billing sensitive and currently requires GitLab Runner 19.0 or later for CI access, so it is discussed as an optional current-platform path rather than a mandatory lab dependency. The mandatory exercises use generated synthetic values and a local provider simulation.

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.