Jenkins security focuses on controller isolation, plugin hygiene, strict credential management, matrix-based security, and preventing unauthorized script execution. As the most widely adopted CI/CD server with 1,800+ plugins, maintaining a secure Jenkins installation requires diligent attention to multiple security layers.
// Jenkins initialization script (init.groovy.d/security.groovy)
import jenkins.model.*
import hudson.security.*
def instance = Jenkins.getInstance()
// Disable CLI over remoting
instance.setDisableRememberMe(true)
// Set security realm
def securityRealm = new HudsonPrivateSecurityRealm(false)
instance.setSecurityRealm(securityRealm)
// Set authorization strategy
def authorizationStrategy = new FullControlOnceLoggedInAuthorizationStrategy()
instance.setAuthorizationStrategy(authorizationStrategy)
instance.save()
// Configure matrix-based security in script console
import hudson.security.*
import jenkins.model.*
def instance = Jenkins.getInstance()
def strategy = new GlobalMatrixAuthorizationStrategy()
strategy.add(Jenkins.ADMINISTER, "admin-team")
strategy.add(Jenkins.READ, "authenticated")
strategy.add(Item.READ, "authenticated")
strategy.add(Item.BUILD, "developers")
strategy.add(Item.CONFIGURE, "team-leads")
instance.setAuthorizationStrategy(strategy)
instance.save()
# Restrict Jenkins network access
# Firewall example (iptables)
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP
iptables -A INPUT -p tcp --dport 50000 -s 10.0.0.0/8 -j ACCEPT
// Check for vulnerable plugins (script console)
import jenkins.model.*
def plugins = Jenkins.getInstance().getPluginManager().getPlugins()
plugins.each { plugin ->
println "${plugin.getShortName()}:${plugin.getVersion()}"
}
# List installed plugins with versions
java -jar jenkins-cli.jar -s http://localhost:8080/ list-plugins 2>/dev/null
# Or via API
curl -s http://localhost:8080/pluginManager/api/json?depth=1 | jq '.plugins[] | {shortName, version, active}'
// Disable automatic updates (config.xml)
// <disableRememberMe>true</disableRememberMe>
// <disabledSecurityWarnings>false</disabledSecurityWarnings>
// Use infrastructure-as-code for plugin management
plugins {
test('org.jenkins-ci.plugins:git:5.2.0')
test('org.jenkins-ci.plugins.workflow:workflow-aggregator:596.v8c21c963d92d')
test('org.jenkins-ci.plugins:credentials:1319.v7eb_51b_3a_c97b')
}
// Access credentials in pipeline (Jenkinsfile)
pipeline {
agent any
stages {
stage('Deploy') {
steps {
withCredentials([
usernamePassword(
credentialsId: 'deploy-credentials',
usernameVariable: 'DEPLOY_USER',
passwordVariable: 'DEPLOY_PASS'
),
file(
credentialsId: 'kubeconfig',
variable: 'KUBECONFIG'
)
]) {
sh './deploy.sh'
}
}
}
}
}
// Secure Jenkinsfile with approved signatures
// Script Approval: Manage Jenkins > In-process Script Approval
pipeline {
agent {
label 'docker-agent' // Use agent, not controller
}
options {
timeout(time: 1, unit: 'HOURS')
disableConcurrentBuilds()
}
stages {
stage('Build') {
steps {
script {
// Use sandbox for Groovy scripts
}
}
}
}
}
// Isolate untrusted PR builds
pipeline {
agent {
kubernetes {
yaml '''
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
'''
}
}
stages {
stage('Build') {
steps {
sh 'mvn clean install'
}
}
}
}
// Configure external secret manager (example with HashiCorp Vault)
// Jenkins > Manage Jenkins > Credentials > System > HashiCorp Vault
// Access Vault secrets in pipeline
pipeline {
agent any
stages {
stage('Deploy') {
steps {
withVaultSecret(
vaultSecret: 'secret/data/prod/db',
secretValues: [
[vaultKey: 'username', envVar: 'DB_USER'],
[vaultKey: 'password', envVar: 'DB_PASS']
]
) {
sh './deploy.sh'
}
}
}
}
}
<!-- Secure config.xml -->
<hudson>
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
<disableSignup>true</disableSignup>
<enableCaptcha>false</enableCaptcha>
</securityRealm>
<authorizationStrategy class="hudson.security.GlobalMatrixAuthorizationStrategy">
<permission>hudson.model.Hudson.Administer:admin-team</permission>
</authorizationStrategy>
<disableRememberMe>true</disableRememberMe>
</hudson>
// Configure logging (Manage Jenkins > System Log)
// Add log recorder for security events
// Log levels to configure:
// hudson.security - Security events
// jenkins.model - Jenkins core events
// hudson.model - Build events
# Check Jenkins version
java -jar jenkins-cli.jar -s http://127.0.0.1:8080/ version 2>/dev/null
# Review installed plugins
curl -s http://127.0.0.1:8080/pluginManager/api/json?depth=1 | jq '.plugins[] | select(.active==true) | {shortName, version}'
# Check for security issues
curl -s http://127.0.0.1:8080/manage 2>/dev/null | grep -i "security\|warning"
# Review recent builds
curl -s http://127.0.0.1:8080/api/json?tree=jobs[name,color] 2>/dev/null | jq '.jobs[]'
# Emergency lockdown script
curl -X POST http://localhost:8080/cli/logout -H "Jenkins-Crumb: <crumb>"
# Backup configuration
tar -czf jenkins-backup-$(date +%Y%m%d).tar.gz \
/var/lib/jenkins \
/etc/jenkins
# Restore from backup
# Stop Jenkins, restore files, start Jenkins
# Check Jenkins version
java -jar jenkins-cli.jar -s http://127.0.0.1:8080/ version 2>/dev/null || echo "CLI not available"
# Check security configuration
curl -s http://127.0.0.1:8080/api/json 2>/dev/null | jq '{useSecurity, slaveAgentPort}'
# Review plugin list
curl -s http://127.0.0.1:8080/pluginManager/api/json?depth=1 2>/dev/null | jq '.plugins[] | select(.active==true) | "\(.shortName):\(.version)"'
# Check authorization strategy
grep -R "authorizationStrategy\|securityRealm" /var/lib/jenkins/config.xml 2>/dev/null | head -10
# Verify listening ports
sudo ss -tulpn | grep -E ':8080|:50000'
# Check for anonymous access
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/ 2>/dev/null
# Review security logs
grep -i "security\|auth\|denied\|failed" /var/log/jenkins/jenkins.log 2>/dev/null | tail -20
# Check build agents
curl -s http://127.0.0.1:8080/computer/api/json 2>/dev/null | jq '.computer[] | {displayName, offline, numExecutors}'
# Verify script approval list
curl -s http://127.0.0.1:8080/scriptText 2>/dev/null | head -5