diff --git a/Dockerfile b/Dockerfile index d2a893f..653b98d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM jenkins/jenkins:2.107.3 +FROM jenkins/jenkins:2.138.2 MAINTAINER Oleg Nenashev LABEL Description="This demo shows how to setup Jenkins Config-as-Code with Docker, Pipeline, and Groovy Hook Scripts" Vendor="Oleg Nenashev" Version="0.2" @@ -29,4 +29,6 @@ VOLUME /var/jenkins_home/pipeline-libs EXPOSE 5005 COPY jenkins2.sh /usr/local/bin/jenkins2.sh +ENV CASC_JENKINS_CONFIG=/var/jenkins_home/jenkins.yaml +COPY jenkins.yaml /var/jenkins_home/jenkins.yaml ENTRYPOINT ["tini", "--", "/usr/local/bin/jenkins2.sh"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..14d8e7d --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +build: + docker build -t onenashev/demo-jenkins-config-as-code . + +run: + docker run --rm --name ci-jenkins-io-dev -v maven-repo:/root/.m2 -v ${MY_PIPELINE_LIBRARY_DIR}:/var/jenkins_home/pipeline-library -v ${MY_OTHER_PIPELINE_LIBS_DIRS}:/var/jenkins_home/pipeline-libs -e DEV_HOST=${CURRENT_HOST} -p 8080:8080 -p 50000:50000 onenashev/demo-jenkins-config-as-code + +debug: + docker run --rm --name ci-jenkins-io-dev -e DEBUG=true -p 5005:5005 -v maven-repo:/root/.m2 -v ${MY_PIPELINE_LIBRARY_DIR}:/var/jenkins_home/pipeline-library -v ${MY_OTHER_PIPELINE_LIBS_DIRS}:/var/jenkins_home/pipeline-libs -e DEV_HOST=${CURRENT_HOST} -p 8080:8080 -p 50000:50000 onenashev/demo-jenkins-config-as-code diff --git a/init_scripts/src/main/groovy/io/jenkins/systemgroovy/plugins/OwnershipBasedSecurityHelper.groovy b/init_scripts/src/main/groovy/io/jenkins/systemgroovy/plugins/OwnershipBasedSecurityHelper.groovy deleted file mode 100644 index 0f5727f..0000000 --- a/init_scripts/src/main/groovy/io/jenkins/systemgroovy/plugins/OwnershipBasedSecurityHelper.groovy +++ /dev/null @@ -1,95 +0,0 @@ -package io.jenkins.systemgroovy.plugins - -import com.michelin.cio.hudson.plugins.rolestrategy.Role -import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap -import hudson.model.Computer -import hudson.model.Item -import hudson.model.Run -import hudson.security.Permission -import jenkins.model.Jenkins - - -// https://github.com/jenkinsci/ownership-plugin/blob/master/doc/OwnershipBasedSecurity.md#role-based-strategy-integration - -/** - * @author Oleg Nenashev. - * @since TODO - */ -class OwnershipBasedSecurityHelper { - - static RoleMap getGlobalAdminAndAnonymousRoles() { - Set adminPermissions = new HashSet() - adminPermissions.add(Jenkins.ADMINISTER) - Role adminRole = createRole("administrator", ".*", adminPermissions) - - Set anonymousPermissions = new HashSet([Jenkins.READ, Item.READ, Item.DISCOVER]) - Role anonymousRole = createRole("anonymous", ".*", anonymousPermissions) - - //TODO: This is a weird hack, which allows running WorkflowRun instances any node - //TODO: Jenkins.getACL() returns RootACL for node, hence we cannot use Node-specific security settings - // We need it to Run Pipeline Jobs. Ideally a RoleStrategy macro should be created. - // If "-Dio.jenkins.dev.security.allowRunsOnMaster" is "false", the Master node will be protected by - // Job Restrictions settings. Nodes have to be protected by Job Restrictions as well. - // Otherwise any user will be able to run whatever stuff on that nodes... - Set masterBuildPermission = new HashSet([Computer.BUILD]) - Role nodeBuildKillSwitch = createRole("BuildAnythingOnNode", ".*", masterBuildPermission) - - final SortedMap> grantedRoles = new TreeMap>() - grantedRoles.put(adminRole, singleSid("admin")) - grantedRoles.put(anonymousRole, singleSid("anonymous")) - grantedRoles.put(nodeBuildKillSwitch, singleSid("authenticated")) - - return new RoleMap(grantedRoles) - } - - static RoleMap getProjectRoleMap() { - Set ownerPermissions = new HashSet() - // Disabled: Ownership settings come from the directory, and we do not want the user to work with them - // ownerPermissions.add(com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin.MANAGE_ITEMS_OWNERSHIP); - ownerPermissions.addAll(Item.PERMISSIONS.permissions) - ownerPermissions.addAll(Run.PERMISSIONS.permissions) - Role ownerRole = createRole("@OwnerNoSid", ".*", ownerPermissions) - - Set coownerPermissions = new HashSet() - coownerPermissions.addAll(Item.PERMISSIONS.permissions) - coownerPermissions.addAll(Run.PERMISSIONS.permissions) - coownerPermissions.removeAll([Item.DELETE, Run.DELETE]) - Role coOwnerRole = createRole("@CoOwnerNoSid", ".*", coownerPermissions) - - return createRoleMapForSid("authenticated", ownerRole, coOwnerRole) - } - - static RoleMap getComputerRoleMap() { - Set ownerPermissions = new HashSet() - // Disabled: Ownership settings for agents are managed by Config-as-Code - // ownerPermissions.add(com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin.MANAGE_SLAVES_OWNERSHIP); - ownerPermissions.addAll(Computer.PERMISSIONS.getPermissions()) - Role ownerRole = createRole("@OwnerNoSid", ".*", ownerPermissions) - - Set coownerPermissions = new HashSet() - coownerPermissions.addAll(Computer.PERMISSIONS.getPermissions()) - coownerPermissions.removeAll([Computer.DELETE, Computer.CONFIGURE]) - Role coOwnerRole = createRole("@CoOwnerNoSid", ".*", coownerPermissions) - - return createRoleMapForSid("authenticated", ownerRole, coOwnerRole) - } - - // TODO: Should be replaced by RoleStrategy API - private static Role createRole(String name, String pattern, Set permissions) { - return new Role(name, pattern, permissions) - } - - private static RoleMap createRoleMapForSid(String sid, Role... roles) { - final SortedMap> grantedRoles = new TreeMap>() - for (Role role : roles) { - grantedRoles.put(role, singleSid(sid)) - } - return new RoleMap(grantedRoles) - } - - private static Set singleSid(String sid) { - return new TreeSet([sid]) - } - - -} diff --git a/init_scripts/src/main/groovy/scripts/Auth.groovy b/init_scripts/src/main/groovy/scripts/Auth.groovy index a2d6389..82b5bf6 100644 --- a/init_scripts/src/main/groovy/scripts/Auth.groovy +++ b/init_scripts/src/main/groovy/scripts/Auth.groovy @@ -1,8 +1,3 @@ -import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy -import com.michelin.cio.hudson.plugins.rolestrategy.RoleMap -import com.synopsys.arc.jenkins.plugins.rolestrategy.RoleType -import hudson.security.HudsonPrivateSecurityRealm -import io.jenkins.systemgroovy.plugins.OwnershipBasedSecurityHelper import jenkins.model.Jenkins import jenkins.security.QueueItemAuthenticatorConfiguration import hudson.model.* @@ -12,25 +7,14 @@ import org.jenkinsci.plugins.authorizeproject.strategy.TriggeringUsersAuthorizat boolean createAdmin = Boolean.getBoolean("io.jenkins.dev.security.createAdmin") -println("=== Installing the Security Realm") -def securityRealm = new HudsonPrivateSecurityRealm(false) +println("=== Configuring users") +def securityRealm = Jenkins.instance.getSecurityRealm() User user = securityRealm.createAccount("user", "user") user.setFullName("User") if (createAdmin) { User admin = securityRealm.createAccount("admin", "admin") admin.setFullName("Admin") } -Jenkins.instance.setSecurityRealm(securityRealm) - -println("=== Installing the Role-Based Authorization strategy") -RoleBasedAuthorizationStrategy strategy = new RoleBasedAuthorizationStrategy() -def grantedRoles = new HashMap() -grantedRoles.put(RoleType.Project.stringType, OwnershipBasedSecurityHelper.projectRoleMap) -grantedRoles.put(RoleType.Slave.stringType, OwnershipBasedSecurityHelper.computerRoleMap) -grantedRoles.put(RoleType.Global.stringType, OwnershipBasedSecurityHelper.globalAdminAndAnonymousRoles) - -strategy.@grantedRoles.putAll(grantedRoles) -Jenkins.instance.authorizationStrategy = strategy println("=== Configure Authorize Project") GlobalQueueItemAuthenticator auth = new GlobalQueueItemAuthenticator( diff --git a/init_scripts/src/main/groovy/scripts/MasterComputer.groovy b/init_scripts/src/main/groovy/scripts/MasterComputer.groovy index d3a9662..816e353 100644 --- a/init_scripts/src/main/groovy/scripts/MasterComputer.groovy +++ b/init_scripts/src/main/groovy/scripts/MasterComputer.groovy @@ -16,27 +16,3 @@ println("== Configuring Master computer") // Admin owns the node NodeOwnerHelper.setOwnership(Jenkins.instance, new OwnershipDescription(true, "admin")) -// Job restrictions -boolean allowRunsOnMaster = Boolean.getBoolean("io.jenkins.dev.security.allowRunsOnMaster") -if (allowRunsOnMaster) { - // TODO: Due to the BuildAnythingOnNode hack, there is actually no protection - println("Runs on Master are enabled. It is a bad idea from the security standpoint") - return -} - -// We allow running jobs in the SystemFolder owned by admin + whitelisted job types -// TODO: Job Restrictions API polishing would be really useful -OwnersListJobRestriction ownedByAdmin = new OwnersListJobRestriction([ new UserSelector("admin") ],false) -RegexNameRestriction inSystemFolder = new RegexNameRestriction("^System/.+", false) - -ClassSelector workflowJob = new ClassSelector(WorkflowJob.class.name) -JobClassNameRestriction whitelistedClasses = new JobClassNameRestriction([workflowJob]) - -Jenkins.instance.getNodeProperties().add( - new JobRestrictionProperty( - new OrJobRestriction( - new MultipleAndJobRestriction([ownedByAdmin, inSystemFolder]), - whitelistedClasses - ) - ) -) diff --git a/init_scripts/src/main/groovy/scripts/System.groovy b/init_scripts/src/main/groovy/scripts/System.groovy index 4bf6c08..74a5a45 100644 --- a/init_scripts/src/main/groovy/scripts/System.groovy +++ b/init_scripts/src/main/groovy/scripts/System.groovy @@ -12,17 +12,10 @@ println("-- System configuration") // TODO: Configure Job Restrictions, Script Security, Authorize Project, etc., etc. println("--- Configuring Remoting (JNLP4 only, no Remoting CLI)") CLI.get().enabled = false -Jenkins.instance.agentProtocols = new HashSet(["JNLP4-connect"]) Jenkins.instance.getExtensionList(StaplerProxy.class) .get(AdminWhitelistRule.class) .masterKillSwitch = false -println("--- Checking the CSRF protection") -if (Jenkins.instance.crumbIssuer == null) { - println "CSRF protection is disabled, Enabling the default Crumb Issuer" - Jenkins.instance.crumbIssuer = new DefaultCrumbIssuer(true) -} - println("--- Configuring Quiet Period") // We do not wait for anything Jenkins.instance.quietPeriod = 0 diff --git a/jenkins.yaml b/jenkins.yaml new file mode 100644 index 0000000..484db23 --- /dev/null +++ b/jenkins.yaml @@ -0,0 +1,101 @@ +jenkins: + mode: NORMAL + numExecutors: 2 + agentProtocols: + - "JNLP4-connect" + - "Ping" + nodeProperties: + - jobRestrictionProperty: + jobRestriction: + or: + first: + multipleAnd: + restrictions: + - ownersList: + usersList: + - selectedUserId: "admin" + acceptsCoOwners: false + - regexNameRestriction: + checkShortName: false + regexExpression: "^System/.+" + second: + jobClassNameRestriction: + jobClasses: + - selectedClass: "org.jenkinsci.plugins.workflow.job.WorkflowJob" + crumbIssuer: + standard: + excludeClientIPFromCrumb: true + disableRememberMe: false + scmCheckoutRetryCount: 0 + projectNamingStrategy: "standard" + markupFormatter: "plainText" + slaveAgentPort: 50000 + myViewsTabBar: "standard" + viewsTabBar: "standard" + + securityRealm: + local: + allowsSignup: false + enableCaptcha: false + authorizationStrategy: + roleBased: + roles: + global: + - name: "admin" + description: "Jenkins administrators" + permissions: + - "Overall/Administer" + assignments: + - "admin" + - name: "readonly" + description: "Read-only users" + permissions: + - "Overall/Read" + - "Job/Read" + - "Agent/Build" + assignments: + - "authenticated" + items: + - name: "@OwnerNoSid" + description: "Primary Owners" + pattern: ".*" + permissions: + - "Job/Configure" + - "Job/Build" + - "Job/Delete" + - "Run/Delete" + assignments: + - "authenticated" + - name: "@CoOwnerNoSid" + description: "Secondary Owners" + pattern: ".*" + permissions: + - "Job/Configure" + - "Job/Build" + assignments: + - "authenticated" + agents: + - name: "@OwnerNoSid" + description: "Primary Owners" + pattern: ".*" + permissions: + - "Agent/Configure" + - "Agent/Build" + - "Agent/Delete" + - "Agent/Build" + assignments: + - "authenticated" + - name: "@CoOwnerNoSid" + description: "Secondary Owners" + pattern: ".*" + permissions: + - "Agent/Connect" + - "Agent/Build" + assignments: + - "authenticated" +tool: + git: + installations: + - home: "git" + name: "Default" + diff --git a/plugins.txt b/plugins.txt index c10ca44..b734887 100644 --- a/plugins.txt +++ b/plugins.txt @@ -1,24 +1,27 @@ -matrix-auth:2.2 -cloudbees-folder:6.4 -workflow-aggregator:2.5 -workflow-cps:2.53 -git:3.9.0 +matrix-auth:2.3 +cloudbees-folder:6.6 +workflow-aggregator:2.6 +workflow-cps:2.59 +git:3.9.1 timestamper:1.8.10 -yet-another-docker-plugin:0.1.0-rc47 +yet-another-docker-plugin:0.1.0-rc48 ownership:0.12.1 -job-restrictions:0.7 -role-strategy:2.8.1 +job-restrictions:0.8 +role-strategy:2.9.0 mailer:1.21 authorize-project:1.3.0 -security-inspector:0.4 -monitoring:1.72.0 -locale:1.2 -blueocean:1.5.0 +security-inspector:0.5 +monitoring:1.74.0 +locale:1.3 +blueocean:1.9.0 filesystem_scm:2.1 -junit:1.24 +junit:1.26.1 checkstyle:3.50 findbugs:4.72 -parallel-test-executor:1.10 -email-ext:2.62 -jacoco:2.2.1 -cobertura:1.12.1 \ No newline at end of file +parallel-test-executor:1.11 +email-ext:2.63 +jacoco:3.0.3 +cobertura:1.13 +configuration-as-code:1.1 +configuration-as-code-support:1.1 +jdk-tool:1.1