Chapter 07Lesson 05~190 minutes

Checkpoint Lab — Secrets Management, External Secret Providers, Protected Data, Rotation, and Least-Privilege Patterns

The checkpoint implements a fully synthetic local secret provider with one authorized principal and one denied principal. You will rotate from version v1 to v2, prove that old material is no longer retrievable, capture only non-secret evidence, document incident/revocation actions, and clean up every temporary file created by the lab.

Checkpoint labAuthorized vs deniedRotationEvidence packetIncident response

Learning objectives

  • Run one authorized and one denied synthetic-provider job with no real credentials and no secret values in logs or artifacts.
  • Rotate a generated secret from v1 to v2 and prove v1 becomes unavailable while v2 remains retrievable to the authorized principal.
  • Inject a controlled authorization or stale-version failure and repair the causal trust/rotation state rather than weakening access controls.
  • Produce an evidence packet containing source/ref/SHA, pipeline/job/runner identity, provider/principal/version metadata, digests, denial evidence, assumptions, and cleanup.
  • Document the incident steps that would be required if a real secret were exposed and bridge the model into Chapter 08 rules and conditional execution.
Checkpoint safety: this lab generates random synthetic values at runtime and never persists them outside a temporary directory. The evidence packet must contain no secret files, no raw tokens, no private keys, and no reusable credential strings. If you adapt the model to a real provider, do so only in an authorized disposable environment.

1. Checkpoint goal and acceptance evidence

Prove five independent states:

  1. The pipeline/job identity is known: source/ref/SHA, pipeline ID, job ID, runner.
  2. An authorized principal can retrieve only the active synthetic secret version.
  3. An unauthorized principal is denied and receives no secret file.
  4. After rotation v1 → v2, v1 is denied and v2 succeeds for the authorized principal.
  5. All temporary secret material is deleted while non-secret evidence remains.

2. Checkpoint pipeline: same provider model, separate authorization outcomes

stages: [verify]

default:
  before_script:
    - set -eu
    - mkdir -p ch07-evidence
    - printf 'source=%s\n' "$CI_PIPELINE_SOURCE" | tee ch07-evidence/context.txt
    - printf 'ref=%s\n' "$CI_COMMIT_REF_NAME" | tee -a ch07-evidence/context.txt
    - printf 'sha=%s\n' "$CI_COMMIT_SHA" | tee -a ch07-evidence/context.txt
    - printf 'pipeline=%s job=%s\n' "$CI_PIPELINE_ID" "$CI_JOB_ID" | tee -a ch07-evidence/context.txt

provider_checkpoint:
  stage: verify
  script:
    - |
      PROVIDER_DIR="$(mktemp -d)"
      trap 'rm -rf "$PROVIDER_DIR"' EXIT
      mkdir -p "$PROVIDER_DIR/versions"
      umask 077
      openssl rand -hex 32 > "$PROVIDER_DIR/versions/v1"
      openssl rand -hex 32 > "$PROVIDER_DIR/versions/v2"
      printf 'v1\n' > "$PROVIDER_DIR/active"

      provider_fetch() {
        principal="$1"; requested="$2"; output="$3"
        active="$(cat "$PROVIDER_DIR/active")"
        if [ "$principal" != 'project/ch07:deploy' ]; then return 77; fi
        if [ "$requested" != "$active" ]; then return 78; fi
        cp "$PROVIDER_DIR/versions/$requested" "$output"
        chmod 600 "$output"
      }

      # 1) Authorized v1 succeeds.
      provider_fetch 'project/ch07:deploy' 'v1' "$PROVIDER_DIR/authorized-v1"
      printf 'authorized_v1=allowed\n' | tee ch07-evidence/results.txt
      printf 'authorized_v1_sha256=%s\n' "$(sha256sum "$PROVIDER_DIR/authorized-v1" | awk '{print $1}')" | tee -a ch07-evidence/results.txt
      rm -f "$PROVIDER_DIR/authorized-v1"

      # 2) Unauthorized principal is denied.
      set +e
      provider_fetch 'fork/untrusted' 'v1' "$PROVIDER_DIR/denied"
      denied_rc=$?
      set -e
      test "$denied_rc" -eq 77
      test ! -e "$PROVIDER_DIR/denied"
      printf 'unauthorized_v1=denied rc=%s\n' "$denied_rc" | tee -a ch07-evidence/results.txt

      # 3) Rotate to v2.
      printf 'v2\n' > "$PROVIDER_DIR/active"

      # 4) v1 is now revoked.
      set +e
      provider_fetch 'project/ch07:deploy' 'v1' "$PROVIDER_DIR/revoked"
      old_rc=$?
      set -e
      test "$old_rc" -eq 78
      test ! -e "$PROVIDER_DIR/revoked"
      printf 'authorized_v1_after_rotation=revoked rc=%s\n' "$old_rc" | tee -a ch07-evidence/results.txt

      # 5) v2 succeeds.
      provider_fetch 'project/ch07:deploy' 'v2' "$PROVIDER_DIR/authorized-v2"
      printf 'authorized_v2=allowed\n' | tee -a ch07-evidence/results.txt
      printf 'authorized_v2_sha256=%s\n' "$(sha256sum "$PROVIDER_DIR/authorized-v2" | awk '{print $1}')" | tee -a ch07-evidence/results.txt
      rm -f "$PROVIDER_DIR/authorized-v2"

  artifacts:
    when: always
    paths:
      - ch07-evidence/context.txt
      - ch07-evidence/results.txt
    expire_in: 1 day
Why these artifacts are acceptable: they contain only identifiers, result codes, versions, and digests of high-entropy randomly generated synthetic values. The secret files themselves remain under mktemp and are deleted by the trap. Never reuse this digest policy for low-entropy real passwords without a threat-model review.

3. Predict the state transitions before running

Step Expected provider state Expected evidence
Authorized v1 active=v1; allowed principal requests v1. Allowed; temporary file exists only until explicit removal.
Denied v1 active=v1; untrusted principal requests v1. Return 77; no output file.
Rotate active pointer changes v1 → v2. No secret value logged.
Old v1 after rotation allowed principal requests non-active v1. Return 78; no output file.
Authorized v2 active=v2; allowed principal requests v2. Allowed; new synthetic digest differs from v1.

4. Run on a disposable branch and preserve immutable identity

LAB_BRANCH="ch07/checkpoint"
git switch -c "$LAB_BRANCH"
git add -- .gitlab-ci.yml
git diff --cached --check
git commit -m "ch07: secret lifecycle checkpoint"
LAB_SHA="$(git rev-parse HEAD)"
printf '%s\n' "$LAB_SHA"
git push -u origin "$LAB_BRANCH"

In GitLab, record the pipeline ID, job ID, pipeline source, ref, SHA, runner identity, and job status. Confirm the job SHA equals the recorded LAB_SHA. A green job proves the assertions in this synthetic provider simulation; it does not prove a real provider’s policy or external target state.

5. Inspect the evidence packet without opening secret material

Download or browse only the two evidence text files. Expected result lines resemble:

authorized_v1=allowed
authorized_v1_sha256=<synthetic-digest>
unauthorized_v1=denied rc=77
authorized_v1_after_rotation=revoked rc=78
authorized_v2=allowed
authorized_v2_sha256=<different-synthetic-digest>

The digests prove the synthetic v1/v2 values differ without retaining the values. The denial codes prove policy outcomes in the simulation.

6. Failure injection: stale version or wrong principal

Run exactly one controlled failure before the successful checkpoint:

  • Change the first authorized request from v1 to v2 while active is still v1. Expected return 78.
  • Or change the authorized principal to project/ch07:build. Expected return 77.

Preserve the failing job ID and non-secret result. Repair only the requested version/principal mapping. Do not broaden provider_fetch policy.

7. Real-world provider mapping and assumptions

If this were Vault/AWS/Azure/GCP/GitLab Secrets Manager, the checkpoint would additionally record:

  • provider/offering/tier and exact integration mode;
  • GitLab/Runner version assumptions where the feature requires them;
  • ID-token audience and expected trust-policy claims (never token value);
  • secret logical reference/version and provider policy identity;
  • provider audit event or denial status;
  • rotation/revocation record and owner.

The course does not require those paid/external systems; this is the architecture mapping only.

8. Incident drill: what if the secret had been real?

Write a short incident note containing these actions in order:

  1. Mark the credential as potentially compromised and preserve pipeline/job/source identifiers.
  2. Revoke or disable the exposed credential at the authoritative provider.
  3. Issue/activate replacement material only for the intended workload.
  4. Prove old material is denied and new material succeeds.
  5. Review provider/service audit logs for suspicious use during the exposure window.
  6. Remove secret-bearing logs/artifacts/caches where authorized and fix the source code/configuration that caused exposure.
  7. Document owner, timeline, affected resources, and preventive control.

9. Cleanup and rollback

  • The job trap deletes the provider temp directory automatically; the job should not artifact it.
  • Delete the disposable ch07/checkpoint branch only after recording its SHA and pipeline/job IDs.
  • Remove any synthetic LAB_FAKE_CREDENTIAL project variable or protected lab branch created in Lesson 2.
  • Keep only the non-secret evidence packet and assumptions/limitations note.

10. Required evidence packet

Source identity

Project/path, pipeline source, ref, immutable SHA, pipeline ID.

Job identity

Job ID/name/status and first-failure job ID before repair.

Runner context

Runner ID/description/version/executor where visible.

Provider model

Local synthetic provider, active version, allowed principal, denial code meanings.

Authorized retrieval

Allowed result + temporary-file lifecycle + synthetic digest; no value.

Denied retrieval

Unauthorized principal, expected denial code, proof no output file existed.

Rotation/revocation

v1 allowed before rotation, v1 denied after rotation, v2 allowed.

Assumptions

Free/disposable simulation; no external provider; no real credentials; current docs verified 2026-09-11.

Cleanup

Temporary provider removed; synthetic settings/branch removed as appropriate.

Incident note

Real-world revocation, audit, replacement, cleanup, and preventive-control sequence.

11. What Chapter 07 adds to the operating model

You can now separate sensitive-value mechanics from true secret lifecycle. A secure pipeline knows which job identity is authorized, requests only the necessary secret reference, avoids log/artifact exposure, uses short-lived/federated identity where possible, rotates and revokes old material, and preserves non-secret evidence.

Chapter 08 moves into workflow:rules and job rules. That topic becomes much safer once you understand why pipeline-source/ref decisions determine which jobs—and therefore which secret capabilities—can exist at all.

Next lesson

Next: workflow:rules, Job rules, if Expressions, changes, exists, Pipeline Sources, and Conditional Execution: Concepts, Architecture, and Mental Model

Continue with the next lesson in the course sequence and carry forward the evidence-first GitLab CI/CD operating model.

Knowledge check

What is the most important checkpoint proof after rotating v1 to v2?

Why does the denied job not need access to a real secret to teach authorization?

What should be retained from the secret retrieval job?

If the real credential had appeared in an artifact, what comes before deleting the artifact?

How does Chapter 07 prepare for rules in Chapter 08?

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.