DeployU
Interviews / DevOps & Cloud Infrastructure / Everyone has admin access to Jenkins. Implement proper RBAC with team-based permissions.

Questions

Everyone has admin access to Jenkins. Implement proper RBAC with team-based permissions.

practical Security & RBAC Interactive Quiz Code Examples

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.

Wrong Approach

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.

Right Approach

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

PrincipleImplementationWhy
Least PrivilegeStart with no access, add as neededReduces blast radius of compromises
Team IsolationFolder-based permissionsTeams can’t affect each other
Credential ScopingFolder credentialsTeams only see their secrets
Audit TrailLog all permission changesCompliance and forensics
Regular ReviewWeekly permission auditsCatch permission creep

Practice Question

Why is folder-based authorization preferred over global matrix authorization for multi-team Jenkins installations?