Checkpoint Lab — Secret Masking Limits, Credentials Scope, External Secret Providers, Vault Integration, and Rotation Patterns
Prove an end-to-end provider-backed secret lifecycle: fetch a fake secret without revealing it, rotate the provider version, verify a new build observes the new version, revoke build tokens, and retain provider/Jenkins evidence that contains no secret value.
Learning objectives
- Predict Jenkins and Vault state changes before each build and rotation.
- Authenticate with a folder-scoped AppRole bootstrap secret and mint a short-lived per-build token.
- Fetch a fake KV-v2 value without logging or archiving it and record only version/TTL/identity metadata.
- Rotate the provider value and prove the next build observes the new version with no application-secret update in Jenkins.
- Produce a reviewed evidence packet combining Jenkins build identity and provider audit paths while excluding secrets.
1. Scenario and success criteria
You operate vault-lab/rotation-checkpoint. The
application credential lives only in local disposable Vault KV v2.
Jenkins stores two folder-scoped AppRole bootstrap identifiers, uses
them on a trusted vault-lab agent to mint a five-minute
token, reads the current value, performs a local non-logging
validation, revokes the token, and archives only safe metadata.
Success requires two builds: build A records KV version 1; an authorized setup operator rotates the provider to version 2; build B records KV version 2 without changing the application secret in Jenkins. Both builds preserve immutable build/source identities and provider audit evidence.
2. Pinned assumptions
| Component | Baseline | Why recorded |
|---|---|---|
| Jenkins | 2.568.3 LTS | Core behavior/security baseline |
| Java | 21 | Controller/agent runtime baseline |
| Credentials | 1511.v2e3cb_0008ef0 | Credential API/storage baseline |
| Credentials Binding | 728.v902a_273b_8947 | withCredentials/masking baseline |
| Vault Jenkins plugin | 384.vda_86ec66c537 (optional) | Not required by mandatory CLI path; recorded for comparison |
| Vault CLI/server | 2.1.0 | Provider behavior/tool identity |
| Vault topology | dev mode, loopback only | Free/disposable simulation; never production |
3. Preflight and exact disposable resource guard
-
Jenkins folder must be exactly
vault-lab; stop if it already contains non-training work. -
Agent label must be
vault-laband point to the same disposable host as loopback Vault. -
Vault mount must be
jenkins-lab/; auth rolejenkins-lab; policyjenkins-lab-read. -
Jenkins credential IDs must be
vault-lab-role-idandvault-lab-secret-id; no applicationapi_keycredential exists in Jenkins. -
Vault audit file must be exactly
/tmp/jenkins-vault-audit.json. -
Only fake values
LAB_PROVIDER_VALUE_V1_ONLYandLAB_PROVIDER_VALUE_V2_ONLYmay be used.
4. Make predictions before execution
| Action | Prediction | Independent verification |
|---|---|---|
| Build A | New Jenkins build/run; short-lived Vault token; provider current_version remains 1 | Jenkins build metadata + provider.txt + audit paths |
| Rotate KV | Provider current_version becomes 2; Jenkins job/credential IDs unchanged |
vault kv metadata get + Jenkins configuration
review
|
| Build B | New token; current version 2; build A remains historical version 1 evidence | Compare archived provider.txt files + audit timing |
| Token cleanup | Each build token is self-revoked after read | Successful self-revoke path in audit; no token value retained |
5. Provider setup
Use the Lesson 2 setup exactly: Vault 2.1.0 dev mode bound to
127.0.0.1:8200, KV-v2 mount jenkins-lab,
AppRole enabled, policy limited to
jenkins-lab/data/app,
jenkins-lab/metadata/app, token self-lookup and
self-revoke, role token TTL 5 minutes/max 10 minutes, SecretID TTL
30 minutes, and file audit enabled.
vault kv put jenkins-lab/app api_key='LAB_PROVIDER_VALUE_V1_ONLY'
vault kv metadata get -format=json jenkins-lab/app | jq '.data.current_version'
6. Checkpoint Pipeline
pipeline {
agent { label 'vault-lab' }
options { skipDefaultCheckout(true); timestamps(); disableConcurrentBuilds() }
stages {
stage('Preflight evidence') {
steps {
sh '''
set -eu
rm -rf evidence && mkdir evidence
printf 'job=%s\nbuild=%s\nurl=%s\nnode=%s\nworkspace=%s\n' \
"$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL" "$NODE_NAME" "$WORKSPACE" \
> evidence/jenkins.txt
vault version > evidence/vault-cli.txt
'''
}
}
stage('Provider fetch') {
steps {
withCredentials([
string(credentialsId: 'vault-lab-role-id', variable: 'VAULT_ROLE_ID'),
string(credentialsId: 'vault-lab-secret-id', variable: 'VAULT_SECRET_ID')
]) {
sh '''
set -eu; set +x; umask 077
export VAULT_ADDR='http://127.0.0.1:8200'
TOKEN_FILE=$(mktemp)
trap 'rm -f "$TOKEN_FILE"' EXIT
vault write -format=json auth/approle/login \
role_id="$VAULT_ROLE_ID" secret_id="$VAULT_SECRET_ID" \
| jq -r '.auth.client_token' > "$TOKEN_FILE"
chmod 600 "$TOKEN_FILE"
export VAULT_TOKEN="$(cat "$TOKEN_FILE")"
VERSION=$(vault kv metadata get -format=json jenkins-lab/app | jq -r '.data.current_version')
TTL=$(vault token lookup -format=json | jq -r '.data.ttl')
APP_SECRET=$(vault kv get -field=api_key jenkins-lab/app)
test -n "$APP_SECRET"
printf 'provider=vault\nauth=approle\nmount=jenkins-lab\npath=app\nkv-version=%s\ntoken-ttl-seconds=%s\nlocal-consumer-ok=true\n' \
"$VERSION" "$TTL" > evidence/provider.txt
unset APP_SECRET
vault token revoke -self >/dev/null
unset VAULT_TOKEN
'''
}
}
}
stage('Evidence safety gate') {
steps {
sh '''
set -eu
! grep -R -n -E 'LAB_PROVIDER_VALUE_V[12]_ONLY|hvs\.' evidence
'''
archiveArtifacts artifacts: 'evidence/*.txt', fingerprint: true
}
}
}
post {
always {
sh 'rm -f .vault-token 2>/dev/null || true'
}
}
}
7. Run build A and freeze its evidence
- Record build number/URL/cause and Jenkinsfile source SHA if SCM-backed.
- Verify
kv-version=1. - Verify token TTL is bounded and no token/value appears in evidence.
- Extract a reviewed provider-audit summary containing only timestamp, request operation/path, and error/success context.
- Do not edit build A after rotation; it is historical evidence.
8. Rotate from version 1 to version 2
In the authorized setup terminal:
vault kv put jenkins-lab/app api_key='LAB_PROVIDER_VALUE_V2_ONLY'
vault kv metadata get -format=json jenkins-lab/app \
| jq '{current_version:.data.current_version, updated_time:.data.updated_time}'
Do not update the Jenkins application secret—there is none. Do not expose either value to prove rotation. Provider version metadata is sufficient.
9. Run build B and compare
- Build B must have a new Jenkins build number and new provider authentication event.
- Its evidence must report
kv-version=2. - Build A must still report version 1.
- The two Jenkins credential IDs remain unchanged; provider application secret was never copied into Jenkins.
- Both builds revoke their own short-lived Vault token after successful use.
10. Create a value-free provider audit summary
Review the audit file locally. Extract only fields needed for this
checkpoint; do not archive raw provider logs by default. A typical
summary should show chronological request paths such as
auth/approle/login,
auth/token/lookup-self,
jenkins-lab/metadata/app,
jenkins-lab/data/app, and
auth/token/revoke-self. If your Vault version
structures JSON differently, adapt the read-only extraction rather
than dumping the entire record.
11. Required evidence packet
-
baseline.md: Jenkins/Java/plugin/Vault versions and assumption timestamp. -
source.md: job full name, Jenkinsfile/source SHA, build cause. -
build-a-jenkins.txtandbuild-b-jenkins.txt: build/node/workspace identity. -
build-a-provider.txtandbuild-b-provider.txt: auth method, path, KV version, TTL, consumer success. -
rotation.md: provider metadata showing transition 1 → 2 and operator/time. -
audit-summary.md: reviewed provider request paths/times/results with no token/secret value. -
security-review.md: confirmation that no secret value/token/private bootstrap secret is in logs/artifacts/workspace evidence. -
assumptions-limitations.md: dev-mode loopback simulation, KV is versioned static data rather than a dynamic leased secret, and production workload identity/TLS/HA are outside the mandatory lab.
12. Cleanup and rollback
- Archive/download only the reviewed evidence packet.
-
Delete the disposable Jenkins
vault-labfolder only after confirming it contains no unrelated work. - Terminate the Vault dev server; its in-memory provider state disappears.
-
Delete
/tmp/jenkins-vault-audit.jsonand/tmp/jenkins-lab-read.hclafter evidence review. - Remove the disposable agent/controller resources used only for the lab.
- In a real incident or production rotation, never substitute “stop the server” for provider-supported revocation, rotation, backup, audit, and recovery procedures.
13. What Chapter 16 adds to the production operating model
You can now treat secret handling as a lifecycle across independent systems: Jenkins authorization and source trust, provider authentication and authorization, token/lease lifetime, secret version/rotation, trusted agent use, masking behavior, external side effects, revocation, and audit evidence. A successful build is not proof of safe secret handling; a masked console is not proof of containment.
Chapter 17 moves from identity/secret trust into execution capacity: nodes, agents, labels, executors, workspaces, offline causes, and agent-capacity design.
Knowledge check
Answer before revealing the explanation.
1. What proves that build 2 consumed the rotated secret without revealing it?
Build 2 records the new KV current-version metadata, succeeds in a local non-logging consumer check, and has matching Vault audit request paths; neither Jenkins evidence nor audit output contains the value.
2. Why does the checkpoint record token TTL but not token/accessor values?
TTL is operational evidence of bounded lifetime. Tokens and accessors are sensitive or operationally powerful identifiers and are unnecessary for proving the intended lifecycle.
3. What prediction should be made before rotation?
Predict that provider KV current_version increments, Jenkins job configuration remains unchanged, the next build authenticates again, and the next evidence packet records the new version while the previous build record remains immutable.
4. How do you prove a per-build token was not left active?
The Pipeline performs self-revocation after use and provider audit evidence records the lifecycle; subsequent provider calls with that token are not attempted because its value is never retained.
5. What production lesson should survive after the dev lab is deleted?
Provider identity should be least-privilege and short-lived, secret values should be fetched only in trusted bounded execution, masking should never be treated as containment, and rotation/revocation must be independently observable.
Official references and version notes
-
Jenkins LTS changelog
and
Java support policy
— lab baseline
Jenkins 2.568.3 LTS, Java 21; this LTS line is tested with Java 21 and 25. - Jenkinsfile — Handling credentials and Credentials Binding step reference — bounded credential binding and masking caveats.
-
Credentials Binding plugin
— baseline
728.v902a_273b_8947; masking is intended to reduce accidental disclosure, not prevent a build from exfiltrating a value it can read. -
Credentials plugin
— baseline
1511.v2e3cb_0008ef0. -
HashiCorp Vault Jenkins plugin
— optional integration baseline
384.vda_86ec66c537; review current security advisories before installation or upgrade. - Vault dev server mode — explicitly disposable/insecure development mode; never a production configuration.
- Vault AppRole auth, tokens and TTL, and leases, renewal, and revocation.
- Vault KV v2 — versioned static secret data. KV values are versioned but are not dynamic leased credentials.
- Vault audit devices — provider-side request evidence without intentionally logging cleartext secrets.
-
Vault release notes
— lab tool baseline
Vault 2.1.0, released 2026-09-01.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.