Credential Stores and Safe Binding

Scenario: One administrator token is globally available to every job and appears in a debug command line.

Never type a password into a shell command

If you've ever seen someone type curl -u admin:hunter2 https://... directly into a script, you've seen exactly the mistake this whole section exists to prevent. That password is now sitting in plain text in a file, in your shell history, and possibly in a build log for anyone with read access to find later. Jenkins' credentials system exists specifically to let a pipeline use a secret without that secret ever appearing in the Jenkinsfile source or, ideally, the log.

Analogy: Think of a Jenkins credential like a hotel room keycard system rather than handing out the actual master key. The front desk (Jenkins' credential store) holds the real keys, safely out of guests' hands. When you need to enter your room, the desk hands you a keycard programmed for just your room, for just your stay (withCredentials binds it as a temporary variable for one block of code) - and when your stay ends, that keycard stops working (the binding is torn down afterward). You never actually see or copy the master key itself; you just get a scoped, temporary way to use it.

This is the mental model for everything in this section: secrets get bound temporarily for exactly the code that needs them, never pasted directly into a script as literal text.

Pipeline example

withCredentials([string(credentialsId: 'lab-deploy-token', variable: 'TOKEN')]) { sh 'deploy < token.request' }

Stores, types, and scope

Jenkins credentials are typed records identified by a stable ID: secret text, username/password, SSH private key, certificate, and secret file are common types. System-level stores can expose credentials globally, to every job on the controller; folder-level stores limit a credential's availability to just that folder and its descendants - which is exactly the mechanism the shared-platform module used to keep each learner's deploy credential private to their own folder. Credential domains describe intended hosts or schemes, but they're matching hints for Jenkins' own UI convenience, not a hard authorization wall - don't rely on a domain restriction as your actual security boundary. Prefer a separate, narrowly-scoped service identity per purpose, stored in the narrowest folder store that can reach it, rather than one broad credential reused everywhere.

withCredentials in practice

withCredentials converts a stored record into a temporary environment variable or file, valid only for the lexical block of code inside it. The variable is automatically cleared afterward, and Jenkins' log masking recognizes many common ways a secret might accidentally get printed and redacts them.

[Pipeline] withCredentials
[Pipeline] {
[Pipeline] sh
+ curl -H 'Authorization: Bearer ****' https://api.example.com/deploy
{"status":"ok"}
[Pipeline] }

Notice the masking again here - *** instead of the real token - but understand its limits: masking defends against accidental disclosure (like an errant echo $TOKEN), not against genuinely malicious Pipeline code that deliberately encodes, compresses, or uploads the secret somewhere else on purpose. If you can't fully trust the code running in a Pipeline, masking alone is not a security boundary - see the untrusted-code section later in this module. Use single-quoted Groovy shell strings (sh 'curl -H \'Authorization: Bearer $TOKEN\' ...') so the shell* expands the variable at runtime; double-quoted Groovy interpolation can bake the literal secret value into the command text before Jenkins even gets a chance to mask it, sometimes exposing it in process listings.

Secret files need care about where they land

A file-type credential binding creates a temporary file on disk for the duration of the block. Binding it from inside a workspace subdirectory can place that file underneath a path that's browsable through Jenkins' own workspace UI - bind it outside the workspace directory, or allocate a completely separate scratch workspace, to avoid that exposure. Disable shell tracing (set -x) and verbose HTTP logging around anything handling a secret, and never archive a full environment-variable dump as a build artifact - that's an extremely common and easy way to accidentally leak every secret bound into that build, all at once, into a permanently-stored file. Any agent that receives credentials must not have untrusted co-tenants able to inspect its running processes or files.

A pitfall: one global token, used everywhere

Rotate the backing secret value while keeping its credential ID the same when every consumer should transparently move to the new value together; verify dependent jobs still work, then revoke the old identity on the external system. Inventory which jobs are actually able to bind a given credential ID - not just who can see its masked display name in the UI, since seeing a masked name reveals nothing about who can use it. The real incident to watch for: a single, globally-scoped administrator-level token, usable by every job on the controller, quietly turns ordinary Configure permission on any one of those jobs into full administrator authority over the external system that token belongs to - because anyone who can edit a Jenkinsfile can add a step that uses that credential. Scoping credentials narrowly, one per purpose and per folder, is what actually contains the blast radius when one job's configuration is compromised or misused.

Goal: Practice the concrete behavior in jenkins-credentials-binding. Learn sections have no Jenkins terminal; the Jenkins lab opens the real shared service in a new full-screen tab inside your private folder.