Questions
Everyone has admin access to Jenkins. Implement proper RBAC with team-based permissions.
The Scenario
During a security audit, you discover that all 200 developers have admin access to Jenkins:
Current State:
- 200 users with admin access
- Any developer can:
- View/modify all jobs
- Access all credentials
- Install/remove plugins
- Modify global configuration
- Delete production jobs
Recent Incidents:
- Developer accidentally deleted production deployment job
- Credentials exposed when someone added debug output
- Unauthorized plugin installation caused security vulnerability
Compliance requires least-privilege access, audit logging, and separation of duties.
The Challenge
Implement a role-based access control system that gives teams access only to their own resources while maintaining operational efficiency.
A junior engineer might just create a few global roles (admin, developer, viewer), use basic matrix authorization without folder-level permissions, or implement RBAC without testing which could lock everyone out. These approaches either don't provide enough granularity or create security gaps.
A senior engineer implements folder-based access control with team isolation, uses role strategy plugin for manageable permissions, integrates with enterprise identity providers (LDAP/SAML), implements audit logging, and tests thoroughly in a staging environment before rolling out.
Step 1: Install Required Plugins
// Required plugins for RBAC
// Install via Plugin Manager or JCasC
plugins:
- role-strategy # Role-based authorization
- folder-auth # Folder-level permissions
- audit-trail # Audit logging
- oic-auth # OpenID Connect (optional)
- saml # SAML SSO (optional)
- ldap # LDAP integration (optional)Step 2: Configure Folder-Based Authorization
# JCasC configuration for Role Strategy Plugin
jenkins:
authorizationStrategy:
roleBased:
roles:
global:
# Platform/Admin team - full access
- name: "admin"
permissions:
- "Overall/Administer"
entries:
- group: "platform-admins"
# Read-only for all authenticated users
- name: "authenticated"
permissions:
- "Overall/Read"
- "Job/Read"
- "View/Read"
entries:
- group: "authenticated"
# Ability to see agents (for debugging)
- name: "agent-viewer"
permissions:
- "Agent/Connect"
- "Agent/Build"
entries:
- group: "developers"
items:
# Development team permissions
- name: "developer"
permissions:
- "Job/Build"
- "Job/Cancel"
- "Job/Read"
- "Job/Workspace"
- "Run/Replay"
- "Run/Update"
entries:
- group: "developers"
pattern: ".*"
# Release manager permissions
- name: "release-manager"
permissions:
- "Job/Build"
- "Job/Cancel"
- "Job/Configure"
- "Job/Create"
- "Job/Delete"
- "Job/Read"
- "Job/Workspace"
- "Run/Delete"
- "Run/Replay"
- "Run/Update"
- "Credentials/View"
entries:
- group: "release-managers"
pattern: ".*"
agents:
- name: "agent-admin"
permissions:
- "Agent/Build"
- "Agent/Configure"
- "Agent/Connect"
- "Agent/Create"
- "Agent/Delete"
- "Agent/Disconnect"
entries:
- group: "platform-admins"Step 3: Implement Folder-Based Team Isolation
// Create folder structure via Job DSL
folder('teams') {
displayName('Teams')
description('Team-specific jobs')
}
['payments', 'platform', 'mobile', 'data-engineering'].each { team ->
folder("teams/${team}") {
displayName(team.capitalize())
description("Jobs for ${team} team")
// Team-specific authorization
properties {
authorizationMatrix {
// Team members can build and view
permissions([
"Job/Build:${team}-developers",
"Job/Cancel:${team}-developers",
"Job/Read:${team}-developers",
"Job/Workspace:${team}-developers",
"Run/Replay:${team}-developers",
])
// Team leads can configure
permissions([
"Job/Configure:${team}-leads",
"Job/Create:${team}-leads",
"Job/Delete:${team}-leads",
"Credentials/View:${team}-leads",
])
// Platform team has access everywhere
permissions([
"Job/Build:platform-admins",
"Job/Configure:platform-admins",
"Job/Create:platform-admins",
"Job/Delete:platform-admins",
])
}
}
}
}Step 4: Configure LDAP/SSO Integration
# JCasC LDAP configuration
jenkins:
securityRealm:
ldap:
configurations:
- server: "ldaps://ldap.company.com"
rootDN: "dc=company,dc=com"
userSearchBase: "ou=users"
userSearchFilter: "uid={0}"
groupSearchBase: "ou=groups"
groupSearchFilter: "(member={0})"
groupMembershipStrategy:
fromGroupSearch:
filter: "(&(cn={0})(objectclass=groupOfNames))"
managerDN: "cn=jenkins,ou=service-accounts,dc=company,dc=com"
managerPasswordSecret: "${LDAP_MANAGER_PASSWORD}"
cache:
size: 100
ttl: 300# Alternative: SAML SSO configuration
jenkins:
securityRealm:
saml:
displayNameAttribute: "displayName"
emailAttribute: "email"
groupsAttribute: "groups"
idpMetadataConfiguration:
url: "https://idp.company.com/metadata"
maximumAuthenticationLifetime: 86400
usernameCaseConversion: "lowercase"Step 5: Implement Credential Access Control
# JCasC credential domains and folder-scoped credentials
credentials:
system:
domainCredentials:
- domain:
name: "production"
description: "Production environment credentials"
specifications:
- hostnameSpecification:
includes: "*.prod.company.com"
credentials:
- usernamePassword:
id: "prod-deploy-creds"
username: "deployer"
password: "${PROD_DEPLOY_PASSWORD}"
scope: GLOBAL
description: "Production deployment credentials"
- domain:
name: "staging"
description: "Staging environment credentials"
specifications:
- hostnameSpecification:
includes: "*.staging.company.com"
credentials:
- usernamePassword:
id: "staging-deploy-creds"
username: "deployer"
password: "${STAGING_DEPLOY_PASSWORD}"
# Folder-scoped credentials in job DSL
folder('teams/payments') {
properties {
folderCredentialsProvider {
domainCredentials {
domainCredentials {
domain {
name: "payments-secrets"
}
credentials {
usernamePassword {
id: "payments-db-creds"
username: "payments_app"
password: "${PAYMENTS_DB_PASSWORD}"
scope: "GLOBAL"
}
}
}
}
}
}
}Step 6: Implement Audit Logging
# JCasC audit trail configuration
unclassified:
auditTrail:
logBuildCause: true
pattern: ".*/(?:configSubmit|doDelete|postBuildResult|enable|disable|cancelQueue|stop|toggleLogKeep|doWipeOutWorkspace|createItem|createView|toggleOffline|cancelQuietDown|quietDown|restart|safeRestart|safeExit|exit|doDisconnect|launchSlaveAgent|doCreateItem|doCreateView)/?.*"
loggers:
- syslog:
syslogHost: "syslog.company.com"
facility: "USER"
messageHostname: "jenkins-master"
- file:
log: "/var/log/jenkins/audit.log"
limit: 100
count: 10
# Security configuration
globalConfigFiles:
configs:
- json:
id: "security-config"
name: "Security Configuration"
content: |
{
"sessionTimeout": 480,
"disableRememberMe": true,
"preventCSRF": true
}Step 7: Create Permission Validation Pipeline
// Pipeline to validate and report on permissions
pipeline {
agent any
triggers {
cron('0 8 * * 1') // Weekly Monday 8 AM
}
stages {
stage('Audit Permissions') {
steps {
script {
def report = generatePermissionReport()
writeFile file: 'permission-report.html', text: report
// Check for violations
def violations = checkForViolations()
if (violations) {
emailext(
to: 'security-team@company.com',
subject: 'Jenkins Permission Violations Detected',
body: violations,
attachmentsPattern: 'permission-report.html'
)
}
}
}
}
}
}
def generatePermissionReport() {
def report = new StringBuilder()
report.append("<html><body><h1>Jenkins Permission Report</h1>")
// List all users with admin access
def authStrategy = Jenkins.instance.authorizationStrategy
if (authStrategy instanceof RoleBasedAuthorizationStrategy) {
def roleMap = authStrategy.getRoleMap(RoleBasedAuthorizationStrategy.GLOBAL)
roleMap.each { role, sids ->
report.append("<h2>Role: ${role.name}</h2>")
report.append("<ul>")
sids.each { sid ->
report.append("<li>${sid}</li>")
}
report.append("</ul>")
}
}
report.append("</body></html>")
return report.toString()
}
def checkForViolations() {
def violations = []
// Check for users with excessive permissions
Jenkins.instance.allItems(Folder.class).each { folder ->
def props = folder.properties.get(AuthorizationMatrixProperty.class)
if (props) {
props.getGroups().each { sid ->
def perms = props.getGrantedPermissions()
if (perms.find { it.key.name.contains("Administer") }) {
violations.add("${sid} has admin access to folder: ${folder.fullName}")
}
}
}
}
return violations ? violations.join("\n") : null
} RBAC Best Practices
| Principle | Implementation | Why |
|---|---|---|
| Least Privilege | Start with no access, add as needed | Reduces blast radius of compromises |
| Team Isolation | Folder-based permissions | Teams can’t affect each other |
| Credential Scoping | Folder credentials | Teams only see their secrets |
| Audit Trail | Log all permission changes | Compliance and forensics |
| Regular Review | Weekly permission audits | Catch permission creep |
Practice Question
Why is folder-based authorization preferred over global matrix authorization for multi-team Jenkins installations?