Skip to content

Jenkins Security: Identity, Credentials, Agents and Pipeline Trust ​

Engineering scenario: A public pull request runs tests on Jenkins, while production release credentials are also configured. Explain the attack paths and the controls required to prevent a contributor from reaching secrets, the controller or another project's workspace.

Trust boundaries first ​

A developer who can modify a Jenkinsfile can generally execute chosen Pipeline steps subject to that job's permissions. If it can bind a credential, it can often use or exfiltrate that credential despite UI masking. Treat the Jenkins controller, privileged shared libraries, SCM sources, build agents and release credentials as separate security principals.

Controls and misunderstandings ​

  • Enable a reviewed security realm and authorization strategy (matrix/role-based where installed); grant minimal Job, Agent, Credential, and Script permissions.
  • Disable executors on the built-in controller, isolate untrusted workloads from controller filesystem/network.
  • Use separate agents/pools, Unix identities and Kubernetes namespaces/accounts for different trust levels. Different directories on the same host may not be enough.
  • Limit Credentials scope and readers. A Jenkins masked secret is still accessible to pipeline code that can use the credential.
  • Keep CSRF protection enabled. Jenkins API token authentication is typically exempt from crumb requirements; username/password scripted POSTs generally need a crumb. Always use TLS.
  • Pin plugins and review security advisories. A plugin adds code inside the controller's trust boundary; manage plugin and script-console privileges tightly.
  • Protect artifact publishing, signing keys and deployment separately from fork PR validation.

Correct handling of an approved secret ​

groovy
pipeline {
  agent { label 'trusted-release' }
  stages {
    stage('Authorized API request') {
      steps {
        withCredentials([string(credentialsId: 'release-service-token',
                                 variable: 'TOKEN')]) {
          sh '''
            set +x
            curl --fail --silent --show-error \
              -H "Authorization: Bearer $TOKEN" \
              https://release.example.org/status
          '''
        }
      }
    }
  }
}

This illustrates credential binding syntax only. It assumes the specific job/agent is trusted, the endpoint is authorized and credential permissions are appropriately scoped. Prefer ephemeral identity federation when supported, and avoid passing secrets in command arguments if process-list visibility or audit policy makes that unacceptable. For high assurance, use a client capable of credentials from a protected file/descriptor.

Groovy interpolation pitfall ​

groovy
// WRONG: Groovy interpolation can expose a secret too early.
// sh "curl -H 'Authorization: Bearer <secret-from-Groovy>' ..."
//
// Better: single-quoted Groovy script so the shell expands a
// scoped environment variable only at execution time.

Masking is best-effort and is not a leak-prevention boundary. set +x reduces command echo, but child processes, crash reports, verbose tools and privileged co-tenants can still expose secrets. Never print environments or rely on log redaction to enforce policy.

Attack walk-through: untrusted change request ​

  1. Attacker submits a PR changing the test script.
  2. Trusted Jenkinsfile invokes ./scripts/test.sh from the PR.
  3. The script runs with the agent's OS permissions.
  4. If privileged credentials are present, it can access them or exfiltrate through network access.
  5. Even without a bound secret, shared Docker socket, host mount, insecure kubeconfig or broad cloud instance role can create escalation paths.

The fix is agent- and credential-level isolation, not simply using a trusted Jenkinsfile.

Incident response ​

If a token was exposed, revoke it and its downstream sessions; inspect job logs, agent filesystem, network egress, artifact registry and cloud audits. Identify all jobs/branches that could read the credential. Rotate keys, remove overbroad grants and replace compromised agents. Avoid publishing logs that themselves reveal the leaked values.

Spoken explanation ​

“I'd separate Jenkins authentication, Pipeline execution and release identity. Untrusted source code runs only on isolated agents without registry-push or cloud-deploy credentials, regardless of the Jenkinsfile trust policy. I'd use scoped credential bindings, API tokens and TLS, restrict trusted libraries and admin scripts, and keep plugins current. Masking reduces accidental disclosure but does not prevent a malicious build from extracting a credential it can access.”

References: Jenkins security · Credential binding · Multibranch credential risks

Practical engineering knowledge, openly shared.