When implementing Jenkins authorization with Project-based Matrix Security, we often encounter a specific access pattern requirement:
Desired Permissions:
1. Authenticated users (specific group) → Full access
2. Anonymous users → Read-only for most jobs
3. Selected projects → Complete block for anonymous
The standard approach of removing global anonymous permissions and then granting them per-project fails because:
- Jenkins requires minimum global permissions for basic UI rendering
- Project-specific permissions only apply after initial access is granted
Here's the proper way to implement this in Jenkins:
1. Go to Manage Jenkins → Configure Global Security
2. Authorization: Select "Project-based Matrix Authorization Strategy"
3. Under 'Global' permissions:
- Anonymous: Check 'Overall/Read' (minimal global access)
4. For each project's configuration:
- For public jobs: Add 'Job/Read' for Anonymous
- For restricted jobs: Leave Anonymous permissions blank
For a concrete implementation, let's examine the config.xml
differences:
<authorizationStrategy class="hudson.security.ProjectMatrixAuthorizationStrategy">
<permission>hudson.model.Hudson.Read:anonymous</permission>
</authorizationStrategy>
<property class="hudson.security.AuthorizationMatrixProperty">
<permission>hudson.model.Item.Read:anonymous</permission>
</property>
<property class="hudson.security.AuthorizationMatrixProperty"/>
Problem: Anonymous users still see restricted jobs in the main list
Solution: Add the following to restricted jobs:
<property class="hudson.security.AuthorizationMatrixProperty">
<permission>hudson.model.Item.Discover:anonymous</permission>
</property>
This shows the job exists but blocks access when clicked.
For bulk updates, use the Jenkins script console:
import hudson.security.*
import jenkins.model.*
def instance = Jenkins.get()
def strategy = new ProjectMatrixAuthorizationStrategy()
strategy.add(hudson.model.Hudson.READ, 'anonymous')
instance.setAuthorizationStrategy(strategy)
// Apply to specific jobs
Jenkins.instance.getAllItems(Job.class).each { job ->
if (job.name in ['secret-project1', 'secret-project2']) {
job.removeProperty(hudson.security.AuthorizationMatrixProperty)
} else {
def prop = new AuthorizationMatrixProperty()
prop.add(hudson.model.Item.READ, 'anonymous')
job.addProperty(prop)
}
}
When implementing granular access control in Jenkins, the matrix-based security model presents unique configuration challenges. The core issue emerges from Jenkins' permission hierarchy where certain base permissions (like Overall/Read
) act as prerequisites for other permissions.
Here's the proper setup using Project-based Matrix Authorization:
// Global Security Settings (configured at /configureSecurity/)
1. Enable "Project-based Matrix Authorization Strategy"
2. Add permission groups:
- Authenticated users: Full permissions
- Anonymous: Only these specific permissions:
* Overall/Read
* Job/Read (for viewing job list)
* Job/Discover (for basic visibility)
3. For restricted projects, explicitly DENY all permissions to Anonymous
The key is understanding Jenkins' permission dependencies:
- Job/Read requires Overall/Read: Without the global read permission, job-level permissions won't function
- Discover vs Read:
Job/Discover
shows the job exists, whileJob/Read
shows detailed configuration - Workspace protection: To completely block workspace access, ensure
Workspace/View
is not granted
For sensitive projects, add this in the project's configuration page:
<project>
<properties>
<hudson.security.AuthorizationMatrixProperty>
<permission>hudson.model.Item.Read:anonymous</permission>
<permission>hudson.model.Item.Discover:anonymous</permission>
<!-- Explicitly deny all other permissions -->
<permission>hudson.model.Item.Build:anonymous</permission>
<permission>hudson.model.Item.Workspace:anonymous</permission>
<permission>hudson.model.Item.Configure:anonymous</permission>
<permission>hudson.model.Item.Delete:anonymous</permission>
</hudson.security.AuthorizationMatrixProperty>
</properties>
</project>
If anonymous users get redirected to login:
- Verify
Overall/Read
is enabled globally - Check no plugins override core permissions (like the Strict Crumb Issuer plugin)
- Examine Jenkins system logs for ACL rejection messages
For more complex scenarios, consider using the Role Strategy plugin:
// Sample role definition
role('anonymous-viewer', {
permissions: [
'hudson.model.Hudson.Read',
'hudson.model.Item.Discover',
'hudson.model.Item.Read'
],
pattern: '.*(?<!secret-project).*' // Regex to exclude certain projects
})