Credentials Store, Secret Text, Files, SSH Keys, Username/Password, Binding, and Secret Hygiene: Guided Hands-On Workflow and Core Operations
Create only fake credentials in a disposable folder, bind them for the shortest practical lifetime on a trusted lab agent, and prove safe use without revealing values.
Learning objectives
- Create disposable secret text, username/password, SSH private-key, and secret-file credentials without placing values in SCM.
- Store the credentials in a lab folder so descendant jobs can use them while unrelated jobs cannot.
-
Bind credentials with
withCredentials, prove non-secret metadata and temporary-file behavior, and avoid console disclosure. - Compare safe secret-file placement with a risky nested-workspace pattern before using it.
- Capture an evidence packet that records identity, scope, build, agent, and cleanup state while excluding all secret values.
1. Lab boundary and preflight
This lab is deliberately local/disposable. Use Jenkins
2.568.3 LTS with Java 21, one trusted disposable agent
labeled trusted-cred-lab, and the plugin versions
listed in the references. Do not use production credentials,
employer repositories, a production controller, or a multi-tenant
agent.
# Agent-side, read-only preflight
java -version
git --version
id
pwd
umask
On the controller, record the core/Java/plugin baseline. Confirm
that routine builds do not execute on the built-in controller node.
The lab should use a dedicated disposable folder named
cred-lab.
2. Create four fake credentials in the folder store
Create cred-lab, open
Credentials → Folder, and add these lab-only
values. The descriptions should say
DISPOSABLE TRAINING ONLY.
| ID | Kind | Fake input | Purpose |
|---|---|---|---|
lab-secret-text |
Secret text | LAB_ONLY_TOKEN_2026 |
Opaque token binding |
lab-basic-auth |
Username/password |
lab-reader / LAB_ONLY_PASSWORD_42
|
Paired variables |
lab-secret-file |
Secret file |
A temporary file containing only
profile=lab-only
|
File-binding lifecycle |
lab-ssh-key |
SSH username with private key | A newly generated disposable Ed25519 key | Private-key file binding |
Generate the disposable SSH key outside SCM and upload it to the folder credential. Never paste the private key into chat, logs, lesson evidence, or source control.
mkdir -p ./cred-lab-input && chmod 700 ./cred-lab-input
printf '%s\n' 'profile=lab-only' > ./cred-lab-input/client.conf
chmod 600 ./cred-lab-input/client.conf
ssh-keygen -t ed25519 -f ./cred-lab-input/id_ed25519 -N '' -C 'jenkins-cred-lab'
ssh-keygen -lf ./cred-lab-input/id_ed25519.pub
3. Bind values narrowly without printing them
Create cred-lab/binding-demo as a Pipeline job. The
Pipeline deliberately does not contact any external service. It
proves only that Jenkins can resolve the IDs and that the agent
receives the correct binding forms.
pipeline {
agent { label 'trusted-cred-lab' }
options { skipDefaultCheckout(true); timestamps() }
stages {
stage('Identity') {
steps {
sh '''
set -eu
printf 'job=%s build=%s node=%s workspace=%s\\n' \
"$JOB_NAME" "$BUILD_NUMBER" "$NODE_NAME" "$WORKSPACE"
'''
}
}
stage('Bounded credential use') {
steps {
withCredentials([
string(credentialsId: 'lab-secret-text', variable: 'LAB_TOKEN'),
usernamePassword(credentialsId: 'lab-basic-auth',
usernameVariable: 'LAB_USER', passwordVariable: 'LAB_PASS'),
file(credentialsId: 'lab-secret-file', variable: 'LAB_CONFIG'),
sshUserPrivateKey(credentialsId: 'lab-ssh-key',
keyFileVariable: 'LAB_SSH_KEY', usernameVariable: 'LAB_SSH_USER')
]) {
sh '''
set -eu
set +x
test -n "$LAB_TOKEN"
test -n "$LAB_USER"
test -n "$LAB_PASS"
test -f "$LAB_CONFIG"
test -f "$LAB_SSH_KEY"
printf 'credential bindings are present; values intentionally not printed\\n'
printf 'secret-file-parent=%s\\n' "$(dirname "$LAB_CONFIG")"
printf 'ssh-key-parent=%s\\n' "$(dirname "$LAB_SSH_KEY")"
stat -c 'secret-file mode=%a owner=%U size=%s' "$LAB_CONFIG"
stat -c 'ssh-key mode=%a owner=%U size=%s' "$LAB_SSH_KEY"
ssh-keygen -lf "$LAB_SSH_KEY"
'''
}
}
}
}
}
4. Understand secret-file placement before changing directories
The Credentials Binding documentation warns about binding a file
from inside a nested dir. The following pattern can
place the temporary file under the browsable workspace subtree:
// RISKY pattern — use only with fake lab data for analysis, not real secrets
dir('subdir') {
withCredentials([file(credentialsId: 'lab-secret-file', variable: 'FILE')]) {
sh 'set +x; test -f "$FILE"'
}
}
Prefer binding outside the nested directory so the secret-file temporary directory is outside that subdirectory, or use an isolated workspace strategy designed for secret-bearing work:
withCredentials([file(credentialsId: 'lab-secret-file', variable: 'FILE')]) {
dir('subdir') {
sh 'set +x; test -f "$FILE"; ./consume-lab-config "$FILE"'
}
}
Do not archive or stash the workspace while the binding is active. A secret copied into another file is no longer protected by the binding cleanup.
5. Prove folder context without broadening it
Create a second disposable Pipeline job
credential-outsider at the Jenkins root or in a
different folder. Attempt to resolve only the ID
lab-secret-text. The expected result is that Jenkins
cannot find an accessible credential with that ID in the outsider
context. Preserve the resulting error as scope evidence.
pipeline {
agent { label 'trusted-cred-lab' }
stages {
stage('Expected denial') {
steps {
withCredentials([string(credentialsId: 'lab-secret-text', variable: 'X')]) {
sh 'set +x; test -n "$X"'
}
}
}
}
}
6. Expected observations and evidence
-
cred-lab/binding-demoresolves all four IDs and runs only ontrusted-cred-lab. - The console shows no credential values; it shows only non-secret metadata.
- Secret-file and SSH-key paths exist only while their binding block executes.
- The outsider job fails before it can use the folder credential.
- No workspace artifact, stash, report, or archive contains credential values or temporary credential files.
7. Small challenge: choose the correct layer
A job inside cred-lab can resolve
lab-secret-text, but an SSH command fails host-key
verification. Which layer should you change?
Answer after investigation: not the credential scope. Credential resolution already succeeded. Diagnose the SSH client/known-host trust configuration on the agent. Never disable host-key verification to make the command pass.
8. Cleanup and rollback
-
Delete the outsider job and
cred-labfolder after recording non-secret evidence. - Confirm the four lab credential IDs no longer appear in the folder store.
-
Delete
./cred-lab-inputfrom the machine where the fake input files were created. - Stop/remove only the disposable agent/controller resources created for this course lab.
- Never delete unrelated credentials, folders, nodes, or build history.
Knowledge check
Why does the lab put credentials in the disposable folder instead of the root global store?
Folder storage narrows the context to descendant items and demonstrates least privilege. Root-global credentials would be visible to a much wider set of jobs.
Why does the safe example use set +x before
commands that reference secret variables?
Shell tracing can echo expanded command arguments. Disabling tracing reduces accidental disclosure, though it does not make the agent safe from malicious code.
What evidence can you record about a secret-file binding without recording the secret?
Credential ID/type, build/job identity, binding start/end, temporary parent path, owner/mode metadata, file existence during binding, and absence after cleanup—never the contents.
Why is printing the SSH public fingerprint acceptable while printing the private key is not?
A public-key fingerprint is not secret and can prove which disposable key was used. The private key is the sensitive credential and must never be logged.
A job in another folder cannot see
lab-secret-text. Is that a build failure or
evidence?
It is expected authorization/scope evidence if the lab intentionally placed the credential in a different folder. Preserve the denial and do not broaden scope just to make the build green.
Official references and version notes
- Jenkins Handbook — Using credentials — credential kinds, system/global scope, IDs and controller-side encrypted storage.
- Jenkins Handbook — Credentials security — limit access, protect secrets, and treat credential use as a trust-boundary decision.
- Using a Jenkinsfile — Handling credentials — Pipeline credential helpers and safe binding patterns.
-
Credentials Binding step reference
— current
withCredentialsbindings and file-placement cautions. -
Credentials plugin
— version
1511.v2e3cb_0008ef0in this lab baseline. -
Credentials Binding plugin
— version
728.v902a_273b_8947. -
SSH Credentials plugin
— version
372.va_250881b_08cd. -
Folders plugin
— version
6.1106.v3a_d9a_6d2465e; provides per-folder credential stores when used with Credentials. -
Jenkins LTS changelog
and
Java support policy
— lab baseline
Jenkins 2.568.3 LTS, Java 21; 2.568.3 is tested with Java 21 and 25.
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.