Jenkins master setup explained

March 27, 2018 ยท View on GitHub

Jenkins configured on startup using Groovy scripts from the dockerizeit/master directory. All scripts read configuration parameters from properties files. So if you would like to adjust your instance parameters then only change them in the corresponding properties file - no Groovy hacking needed. If you are missing something, then you are very welcome to contribute support for whatever you need.

Dockerfile

TBD

Upgrading plugins, removing/installing plugins

Plugins configuration managed through the master/plugins.txt. To update its content first go to Manage Jenkins -> Manage Plugins and install necessary updates, uninstall plugins and etc. When ready run the following in Manage Jenkins -> Script console and then copy output to plugins.txt

plugins = [:]
jenkins.model.Jenkins.instance.getPluginManager().getPlugins().each {plugins << ["${it.getShortName()}":"${it.getVersion()}"]}
plugins.sort().each() { println "${it.key}:${it.value}"}

Another option is to use dockerizeit/scripts/update_plugins.sh script that will attach to master container, run groovy script above and save its output to plugins.txt. Note that update_plugins.sh doesn't use any type of authentication so you have to modify script if login required

Installation of custom plugin

If you have a custom .hpi / .jpi plugin that the master needs to run with copy the file into the dockerizeit/master/ and add the following line to the end of dockerizeit/master/Dockerfile:

COPY *.hpi /usr/share/jenkins/ref/plugins/

or if you plugin is available in Nexus/Artifactory/JitPack then you could download it to corresponding directory. See example below

RUN wget https://jitpack.io/com/github/praqma/tracey-jenkins-trigger-plugin/1.0.0/tracey-jenkins-trigger-plugin-1.0.0.hpi \
         -O /usr/share/jenkins/ref/plugins/tracey.hpi

Configuration through the Groovy files

Credentials

Credentials created by the credentials.groovy The script will read the following section from the jenkins.properties and create corresponding data structures

credentials {
  // Default credentials used to download Jenkins repo
  base {
    type = "ssh"
    userId = "jenkins"
    credentialsId = "jenkins"
    description = ""
    passphrase = ""
    path = "/var/jenknis_home/.ssh/id_rsa" // by default docker-compose.yml mounts ~/.ssh to /var/jenkins_home/.ssh Make sure it contains correct ssh key
  }
}

It is possible to create password-based credentials

credentials {
  base {
    type = "ssh"
    userId = "jenkins"
    credentialsId = "jenkins"
    description = ""
    passphrase = ""
    path = "/var/jenknis_home/.ssh/id_rsa"
  }
  pass {
    type = "password"
    userId = "test"
    credentialsId = "test"
    description = ""
    // Store password in the file and specify the path. Suggestion is to use .password inside .ssh directory.
    // If this level of security is enough for ssh keys then it will do for the password.
    // Later on we might implement HashiCorp Vault support
    path = "/var/jenkins_home/.ssh/.password"
  }
}

Global section

Some global properties in jenkins.properties are set through globalconfig.

global {
  // How many executors you want on master
  numExecutorsOnMaster = 0
  // Which address is used to axxess your Jenkins instance
  // http://jenkins.my.company:8080
  // left empty: http://$ip:8080
  jenkinsRootUrl = ""
  // Specify the administrator email
  / used as return address in email notofications
  jenkinsAdminEmail = "Jenkins <no-reply@yourcompany.com>"
  scmQuietPeriod = 3
  scmCheckoutRetryCount = 3
  git {
    // Jenkins in git
    name = "Jenkins Jenkinsson"
    email = "no-reply@yourcompany.com"
  }
  // Some default variables to use in job configurations.
  variables {
    default_credentials = "${credentials.base.credentialsId}"
    default_repo = "git@github.com:Praqma/JenkinsAsCodeReference.git"
    default_branch = "master"
    utility_slave = "utility-slave"
    master_image_name = "${images.masterImageName}"
    slave_image_name = "${images.slaveImageName}"
  }
}

Configuration of Groovy Shared Library plugin implemented in globalPipelineLibraries.groovy. Use the following block in jenkins.properties to define your libraries

libraries{
  library1 {
      enabled = true
      name = "name-of-library"
      version = "master"
      implicitly = false
      allow_overridden = false
      scm_path = "https://github.com/Andrey9kin/shared-groovy-lib-test.git"
      credentialsId = ""
  }
}

Slaves

Slaves created by the slaves.groovy The script will read slaves.properties and create corresponding slaves. Consider using generate_slaves_config.groovy to generate configuration for your existing slaves. Simply run the script in Jenkins Script Console (Manage Jenkins -> Script Console). Please note that there are limitations (see comments inside script).

SSH slaves configuration

slaves {
  // Long version
  sshTest {
   type = "ssh"
   name = "ssh-test"
   description = ""
   remoteFS = "/var/jenkins/slave"
   executors = "5" // Have to be string because of constructor
   mode = hudson.model.Node.Mode.NORMAL
   labels = "ssh ssh-test"
   retention = hudson.slaves.RetentionStrategy.INSTANCE
   // ssh slave specific values
   host = "localhost"
   port = 22
   credentialsId = "jenkins"
   jvmOptions = ""
   javaPath = ""
   prefixStartSlaveCmd = ""
   suffixStartSlaveCmd = ""
   launchTimeoutSeconds = 5
   maxNumRetries = 3
   retryWaitTime = 3
   env {
     key = "value"
   }
  }
  // short version
  sshTest2 {
   type = "ssh"
   name	= "ssh-test2"
   description = ""
   remoteFS = "/var/jenkins/slave"
   executors = "5"
   mode = hudson.model.Node.Mode.NORMAL
   labels = "ssh ssh-test2"
   retention = hudson.slaves.RetentionStrategy.INSTANCE
   // ssh slave specific values
   host = "localhost"
   credentialsId = "jenkins"
   env {
     key = "value"
   }
  }
}

JNLP slaves configuration example

slaves {
  jnlpTest {
   type = "jnlp"
   name	= "jnlp-test"
   description = ""
   remoteFS = "/var/jenkins/slave"
   executors = "5"
   mode = hudson.model.Node.Mode.NORMAL
   labels = "jnlp jnlp-test"
   retention = hudson.slaves.RetentionStrategy.INSTANCE
  }
}

Going forward we are planning to move slaves definition to the user repository and read them in the same way as we do with the JobDSL files for the seedjobs.

Seed jobs

Seed jobs created on the startup by the initialjobs.groovy The script will read the following section from the jenkins.properties and create corresponding jobs

seedjobs {
  jenkins {
    // Name of the job
    name = "jenkins_as_a_code-seedjob"
    // Repo to clone
    repo = "git@github.com:Praqma/JenkinsAsCodeReference.git"
    // Branch to checkout
    branch = "master"
    // Credentials to use when cloning
    credentials = "jenkins"
    // Where to find job dsl specification in the repository
    path = "jobdsl-gradle/src/jobs/groovy/*.groovy"
    // Additional classes if any
    classpath = "jobdsl-gradle/src/main/groovy"
    // Job parameters if needed
    parameters {
    }
  }
}

To plugin your repo just add one more section like the one above - script will iterate over all sections, create jobs, and trigger them. See example below

seedjobs {
  jenkins {
    name = "jenkins_as_a_code-seedjob"
    repo = "git@github.com:Praqma/JenkinsAsCodeReference.git"
    branch = "master"
    credentials = "jenkins"
    path = "jobdsl-gradle/src/jobs/groovy/*.groovy"
    classpath = "jobdsl-gradle/src/main/groovy"
    parameters {
    }
  }
  roadshow {
    name = "praqma-training.roadshow.seed"
    repo = "git@github.com:praqma-training/roadshow-dsl.git"
    branch = "master"
    credentials = "jenkins"
    path = "*.groovy"
    classpath = ""
    parameters {
      GITHUB_USER = "praqma-training"
    }
  }
}

Artifactory

Configured by the artifactory.groovy The script will read the following section from the jenkins.properties and create corresponding data structure. Current implementation relies on the credentials, i.e. you have to create credentials instead of specifying login and password in Artifactory config. In this way, we are trying to avoid having plain passwords in the configuration files. You have a possibility to configure multiple servers by adding more closures.

artifactory {
  server1 {
    enabled = true
    deployerCredentialsId = "artifactory"
    resolverCredentialsId = "artifactory"
    overridingCredentials = false
    connectionTimeOut = 300
    serverName = "my-lovely-artifactory"
    serverUrl = "http://1.1.1.1:8081/artifactory"
    bypassProxy = false
    connectionRetry = 5
  }
}

Gerrit

Configured by the gerrit.groovy The script will read the following section from the jenkins.properties and create corresponding data structure.

gerrit {
  example {
    enabled = false
    hostName = "gerrit.example.com"
    sshPort = 29418
    frontendURL = "http://gerrit.example.com:8081/"
    proxy = "http://proxy:8080"
    userName = "user"
    sshKeyFile = "/var/jenkins_home/.ssh/id_rsa"
    email = "user@example.com"
  }
}

Proxy

Configured by the proxy.groovy The script will read http_proxy, https_proxy, no_proxy, JAVA_PROXY environment variables (set during the container build. See Dockerfile for more details) and define then as Jenkins global variables.

Security configuration

Security configuration stored in the separate file - security.properties

Script security for JobDSL

JobDSL plugin supports script security starting from 1.60. You can disable sandbox mode by using config below

scriptSecurity {
  jobDSL = false
}

LDAP

LDAP configuration example. Fields name match LDAPSecurityRealm constructor.

ldap {
  enabled = true
  server = "ldap://1.2.3.4"
  rootDN = "dc=foo,dc=com"
  userSearchBase = "cn=users,cn=accounts"
  userSearch =
  groupSearchBase =
  managerDN = "uid=serviceaccount,cn=users,cn=accounts,dc=foo,dc=com"
  managerPassword = "password"
  inhibitInferRootDN = false
}

AD configuration example. Fields name match ActiveDirectorySecurityRealm constructor.

ad {
  enabled = true
  domain = "domain.com"
  site = null
  bindName = null
  bindPassword = null
  server = null
  // See [GroupLookupStrategy.java](https://github.com/jenkinsci/active-directory-plugin/blob/8817f035e15d33e390367aa7c664e0cfbb04d7cf/src/main/java/hudson/plugins/active_directory/GroupLookupStrategy.java) for the list of available options.
  // We can't use direct class resolution like hudson.plugins.active_directory.GroupLookupStrategy.RECURSIVE
  // because GroupLookupStrategy is a private enum
  groupLookupStrategy = RECURSIVE
}

Jenkins own database

Add the following snippet to security.properties in order to tell Jenkins to use its own database for the user management. There is no support for sign up in UI because Jenkins configuration will be wiped out during redeployment, i.e. user database is not preserved. Preferably avoid this type of security realm and use 3rd party service for that - think LDAP, Unix users database, GitHub, Google etc. Also, consider a need for those users - do you really need them if you anyway control configuration through the git repo. Well, unless this is a test instance facing public network

owndb {
  enabled = true
  users {
    user1 {
      userId = "jenkins"
      path = "/var/jenkins_home/.ssh/.password"
    }
    user2 {
      userId = "hudson"
      path = "/var/jenkins_home/.ssh/.password"
    }
  }
}

Google Login

Google Login plugin allows to login using Google domain credentials. Make sure to create file with the secret before enabling corresponding section

googlelogin {
  enabled = false
  clientId = "someId"
  clientSecret = "/var/jenkins_home/.ssh/.googlesecret"
  domain = "domain.com"
}

Matrix-based security

Add the following snippet to security.properties to appoint users access rights for matrix-based security model. For example, to allow anonymous users create a slave connection Jenkins master, or give authenticated user administrators rights.

matrixbasedsecurity {
  enabled = true
  users {
    anonymous {
      userId = hudson.security.ACL.ANONYMOUS_USERNAME
      permissions = [
                      hudson.model.Computer.CREATE,
                      hudson.model.Computer.CONNECT,
                      hudson.model.Hudson.READ,
                      hudson.model.Job.READ]
    }
    authenticated {
      userId = "authenticated"
      permissions = [
                       hudson.model.Hudson.ADMINISTER]
    }
  }
}