Jenkins Security
Secure the Pipeline Tool, Not Just What Runs In It
Every other DevSecOps page on this site is about using CI/CD to enforce security - SAST/SCA/DAST gates, policy-as-code, IaC scanning. This page is about a different problem: Jenkins itself is a high-value target. It typically holds deployment credentials, cloud API keys, source code access, and the ability to run arbitrary code on every build agent. A compromised Jenkins instance is often a faster path to production than any application vulnerability.
The Script Console Is a Backdoor by Design
Jenkins' built-in Script Console runs arbitrary Groovy with the permissions of the Jenkins process - which usually means it can read every credential Jenkins holds and execute code on connected agents. It's meant for admin troubleshooting, not general access.
- Restrict
Overall/Administer(which gates Script Console access) to a small, named set of admins - never grant it broadly "to unblock people faster." - Enable Role-Based Access Control (via the Role-based Authorization Strategy plugin) instead of relying on the simple matrix authorization's coarse
Admin/Usersplit. - Audit who has
Administerquarterly - it's a common area of privilege creep.
Credentials: Use the Credentials Plugin, Never Hardcode
// INSECURE - secret visible in the Jenkinsfile, build logs, and git history
pipeline {
agent any
stages {
stage('Deploy') {
steps {
sh 'aws configure set aws_secret_access_key AKIAABCDEF1234567890'
sh './deploy.sh'
}
}
}
}
// SECURE - secret injected at runtime from Jenkins' credential store, masked in logs
pipeline {
agent any
stages {
stage('Deploy') {
steps {
withCredentials([usernamePassword(
credentialsId: 'aws-deploy-creds',
usernameVariable: 'AWS_ACCESS_KEY_ID',
passwordVariable: 'AWS_SECRET_ACCESS_KEY'
)]) {
sh './deploy.sh' // env vars available only within this block, auto-masked in console output
}
}
}
}
}
Store the actual secret in Jenkins' Credentials store (backed by a KMS-encrypted file, or better, an external secrets manager plugin pointing at Vault/AWS Secrets Manager), scope each credential to the specific folder/job that needs it, and never echo a secret variable directly (echo $AWS_SECRET_ACCESS_KEY) - Jenkins masks known credential bindings in log output, but only if you use withCredentials rather than reading the secret into a variable some other way.
Untrusted Code on Shared Agents
If Jenkins builds pull requests from external contributors (or even from less-trusted internal repos) on the same agents that hold deployment credentials, a malicious PR can potentially read those credentials or pivot to other jobs on the same agent.
- Run PR builds from forks/untrusted sources on isolated, ephemeral agents (containers or VMs spun up per-build and destroyed after) with no access to deployment credentials.
- Never auto-trigger a full build+deploy pipeline on an unreviewed PR - require a maintainer to approve running CI on external contributions (GitHub's "Approve and run" gate for forks is the equivalent pattern on GitHub Actions).
- Treat any Jenkinsfile change in a PR itself as untrusted input -
Jenkinsfileis code, and a PR can modify it to do anything the agent's permissions allow.
Webhook and Trigger Security
- Validate webhook payloads (e.g. GitHub's
X-Hub-Signature-256HMAC) rather than trusting any POST to your trigger URL - an unauthenticated build trigger endpoint lets anyone kick off jobs, potentially with attacker-controlled parameters. - Restrict which jobs can be parameterized/triggered remotely, and validate/sanitize any parameters used in shell steps (a parameterized build that interpolates a user-supplied string directly into
sh "deploy.sh ${params.ENV}"is a command injection waiting to happen).
Plugin Supply-Chain Risk
Jenkins' plugin ecosystem is large, community-maintained, and has had real supply-chain incidents (compromised or vulnerable plugins with broad permissions).
- Keep Jenkins core and plugins patched - subscribe to the Jenkins Security Advisories.
- Minimize the plugin footprint - every installed plugin is additional attack surface, especially plugins with credential or script-execution capabilities.
- Review plugin permissions before installing, the same way you'd review an npm/PyPI package before adding it as a dependency (see SCA for the general supply-chain risk model).
Best Practices
- Restrict Script Console /
Administerto a small admin group, audited regularly. - Store every secret in the Credentials plugin (or an external secrets manager) - never in a Jenkinsfile, environment default, or shell script committed to the repo.
- Isolate untrusted PR builds on ephemeral agents with no deployment credentials.
- Validate webhook signatures and sanitize any pipeline parameters used in shell steps.
- Patch Jenkins core and plugins on a schedule, and minimize installed plugins to reduce supply-chain exposure.