The installation guide for Jenkins can be found at . We deployed Jenkins on a VM running CentOS 6 x64. This wiki is aimed to describe customizations made to the default setup including OpenStack integration and Shibboleth authentication.
OpenStack can be used to provision Jenkins slaves on demand. A Jenkins slave is an executor node that runs a Jenkins agent and gets jobs from the Jenkins master.
To set up this integration a Jenkins plugin was used, called JClouds. Installing Jenkins plugins is a fairly straight forward job that can be done from the web interface through Manage Jenkins -> Manage Plugins. The JClouds plugin is designed to work with multiple cloud platforms, so it has to be configured for OpenStack specifically. The plugin configurations can be found under Manage Jenkins -> Configure System, or alternatively can also be tweaked from /var/lib/jenkins/config.xml. The parameters to configure are:
Profile : <profile name> Provider Name : openstack-nova End Point URL : http://<ip>:5000/v2.0/ Identity : tenant:user Credentials : <password> RSA Private Key : <private key> Public Key : <public key>
The endpoint should point to the machine where you have the openstack-nova-api set up. In case of our all-in-one type of setup, there is not much to choose from. The identity and credentials are needed for authentication at OpenStack. If you want to keep Jenkins slave separate from other projects running on OpenStack you can create a new tenant/user pair in OpenStack as such:
source keystonerc_admin keystone user-create --name=jenkins --pass=<pass> keystone tenant-create --name=jenkins-testbed --description="Jenkins Testbed" keystone user-role-add --user=jenkins --role=_member_ --tenant=jenkins-testbed
Next you should create a key pair that Jenkins will use to allow ssh access into the slaves.
Note! The public key of the created key pair should be uploaded into OpenStack, so that it can be injected in the VMs authorized_hosts list. Do this using:
nova keypair-add --pub-key <pub key> jenkins-key
You can test your setup with a Test Connection button on the bottom of the page.
In order to be able to start slaves, you first need to define Cloud Instance Templates. The parameters to configure are:
Name : <name> Labels : <lable1> <label2> Hardware Options Specify Hardware ID : RegionOne/2 (m1.small) Image/OS Options Specify Image ID : RegionOne/<image ID> General Options Admin Username : <admin username> Networks : <network ID> Security Groups : <secgroup> Open Stack Options Key Pair Name : jenkins-key
After naming the template you should also give it a couple of labels. Labels work in Jenkins like tags. It's useful to group more templates of the same kind, for example with 'slave' for generic templates, and with more specialized keywords for other templates. These labels can later be used in job configurations, and it's the main way of restricting the type of the node where a they are allowed to run.
Next a collection of openstack specific options continue. You should choose one of the provided Hardware type (m1.tiny, m1.small...) and image IDs. You can find image IDs by executing glance image-list in your openstack node. The 'Admin Username' expects the username with sudo capabilities on the image that you are about to boot. This varies from image to image (for example, the centOS 6 cloud image has 'root', while the centOS 7 image has 'centos'). This is required, because Jenkins will attempt to ssh into a newly created VM to set up its environment. The network ID has to be the ID of an already existing network within openstack, and can be viewd with nova network-list. (Note! This network should be associated with the same tenant that you created for the jenkins user) You can provide specific, already existing security groups to use, but we observed that Jenkins tends to create its own security group in OpenStack with a single rule that opens up port 22. Last, you have to provide the name of the keypair that you installed in the previous step, while setting up the connection. These options should be sufficient to start Jenkins slaves.
Other useful options that can be tweaked are:
Init Script : executes a series of bash commands after the VM is created Allow Sudo : Jenkins creates a UNIX user (named 'jenkins' by default) to run its agent. This enables sudo on it. Useful when you want to install something through the Init Script. Install Private Key : copy the private key provided in the setup, such that you can ssh from one slave into the other.
Using the plugin
After you install the plugin two separate options will appear in every job configuration under Build Environment: JClouds Instance Creation and JClouds Single-Use Slave.
JClouds Instance Creation allows you to create a specified number of VMs from a template. Note that this will not create a VM on which to run the job, rather it creates the VM as part of the job (this means that you should have at least one matching executor where this job can run). The IPs of the newly created VMs are then listed in the environmental variable called JCLOUDS_IPS. You can use this to establish connections to the VMs from this job.
Note! By default these VMs will be terminated automatically once the job finishes. There is an option, however, called 'Stop on Terminate' which just stops them instead of terminating. This option is implemented such that it issues a 'suspend' command to OpenStack, which currently fails ('VM_MISSING_PV_DRIVERS'). As a result the VM is not terminated, and not suspended either, so it keeps running.
JClouds Single-Use Slave creates a single-use VM matching the node restrictions of the job, runs the job on the VM and terminates it.
Note! This option seems to hang the job occasionally. It seems that the VM is created and the slave is installed as expected on it (logs confirm), but the state is not refreshed, so the job tends to stay in the queue until a manual status refresh of slaves. Moreover, after job completion, the single-use slave is marked for deletion, but it's actually never removed until manual intervention (delete node directly from OpenStack).
Authentication through the Nikhef SSO
The motivation behind this setup was to have Jenkins set up as a Nikhef services, only available to known members. Jenkins can be set up to run in a container, or can be run on its own as deamon . In our setup Jenkins is running as a deamon with apache in front of it. Apache servers as a reverse proxy which only lets requests through if they have been authenticated by the Nikhef SSO. The authentication relies on Shibboleth.
Apache reverse proxy setup
The OS that was running Jenkins came with apache 2.2, but the Jenkins reverse proxy required an option (namely AllowEncodedSlashes NoDecode) which was only available in later releases of apache. You can read about this phenomenon and the requirements of Jenkins here and here. We decided to switch to apache 2.4:
wget -O /etc/yum.repos.d/epel-httpd24.repo http://repos.fedorapeople.org/repos/jkaluza/httpd24/epel-httpd24.repo yum install httpd24 httpd24-mod_ssl httpd24-httpd-devel
Note! This will install an additional apache server (2.4) next to the already existing one in CentOS 6 (2.2). Make sure the shut down the previous one, before firing up this one. The newly installed apache (2.4) will be rooted in /opt/rh/httpd/root with all of it's configuration, so the apache config files will also be in there, not in /etc/httpd. The development package is needed to build shibboleth RPM from source, as described below.
Next you need to configure apache to act as a reverse proxy. Set up virtual host on port 8008 with ProxyPass and ProxyPassReverse to the ajp enpoints of jenkins listening on port 8009. Create a new configuration file /opt/rh/httpd24/root/etc/httpd/conf.d/jenkinsproxy.conf containing:
<VirtualHost *:8008> ServerName <hostname> UseCanonicalName On UseCanonicalPhysicalPort On SSLEngine On SSLCertificateFile <ssl.crt> SSLCertificateKeyFile <ssl.key> #only if applicable SSLCertificateChainFile <ca.crt> ProxyRequests Off ProxyPass / ajp://127.0.0.1:8009/ nocanon ProxyPassReverse / ajp://127.0.0.1:8009/ AllowEncodedSlashes NoDecode </VirtualHost>
You should fill the server hostname and the location of the key and certificate file you are planning to use for HTTPS. In our setup we run jenkins without a prefix, so the server can be accessed in /. If you run Jenkins with a prefix, make sure to include it after the / in both ProxyPass and ProxyPassReverse.
Make sure that apache is listenning on port 8008 by editing /opt/rh/httpd24/root/etc/httpd/conf/httpd.conf and make a rule for port 8008 in your iptables. After starting apache you should be able to access the Jenkins Dashboard via the apache reverse proxy.
sudo service httpd24-httpd start
Note! In this setup the communication between apache and Jenkins is unencrypted using ajp, which is fine until Jenkins only listens on localhost. Additional steps can be taken to make this connection use ssl.
Note! Make sure to close all listening ports (HTTP and HTTPS) on Jenkins, and only leave the AJP port (8009) opened for incoming connections on localhost. If you fail to do so, the reverse proxy can get bypassed! Change the following parameters in /etc/sysconfig/jenkins:
JENKINS_PORT="-1" JENKINS_LISTEN_ADDRESS="" #JENKINS_HTTPS_PORT="" #JENKINS_HTTPS_LISTEN_ADDRESS="0.0.0.0" JENKINS_AJP_PORT="8009" JENKINS_AJP_LISTEN_ADDRESS="127.0.0.1"
So far we configured HTTPS access to the Jenkins server through an apache reverse proxy, but so far everybody can use jenkins uncontrolled. To set up authentication using the Nikhef SSO we used Shibboleth. The Shibboleth SP (Service Provider) component is made up a deamon called shibd and an apache module that helps configure apache to send authentication requests. Unfortunately, the prepackaged RPMs are deciding which version of apache module to install based on the native apache of the system, which in our case is apache 2.2, wrongly. To install the right apache module we had to build our own RPM from a SRPM as outlined here. Since the Shibboleth package also comes with some dependencies, what we ended up doing was to install Shibboleth with yum (this takes care of the right dependencies) then remove the shibboleth package, build it from source and install it. This avoids building the rest of the dependencies from source.
wget -O /etc/yum.repos.d/shib.repo http://download.opensuse.org/repositories/security://shibboleth/CentOS_CentOS-6/security:shibboleth.repo yum install shibboleth.x86_64 rpm -e --nodeps shibboleth rpmbuild --rebuild --clean --without builtinapache -D 'shib_options \ --enable-apache-24 \ --with-apxs24=/opt/rh/httpd24/root/usr/bin/apxs \ --with-apr=/opt/rh/httpd24/root/usr/bin/apr-1-config \ --with-apu=/opt/rh/httpd24/root/usr/bin/apu-1-config' SRPMS/shibboleth-2.5.3-1.1.src.rpm rpm -iv RPMS/x86_64/shibboleth-2.5.3-1.1.x86_64.rpm
Note! In order for the rpmbuild to succeed you might have to install the development packages of the shibboleth dependencies.
Note! The rpmbuild also failed multiple times complaining about rpaths and about a deprecated dependency. The rpath warnings got fixed with 'export QA_RPATHS=$[ 0x0001|0x0010 ]', while the dependency error turned out to be a wrongly configured library dependency in /opt/rh/httpd24/root/usr/bin/apxs. A nasty hack made apxs look into /opt/rh/httpd24/root/usr/lib64 for its dependencies rather then in /usr/lib64. The diff between the old and new apxs:
35c35 < my $libdir = `pkg-config --variable=libdir apr-1`; --- > my $libdir = "/opt/rh/httpd24/root/usr/lib64/";
You can verify if you have the right apache module installed with:
rpm -q -l shibboleth-2.5.3-1.1.el6.x86_64 | grep mod_shib_24
Shibboleth with Apache
The guides at  and  helped in configuring apache and shibboleth to work together. Shibboleth came installed with a sample apache configuration file which loads the module. You should move this module into the right place in httpd24, or otherwise you have to load the module manually somewhere else.
cp /etc/shibboleth/apache24.config /opt/rh/httpd24/root/etc/httpd/conf.d/
The Shibboleth configuration file can be found under /etc/shibboleth2.xml. You have to configure this to your own setup.
... <ApplicationDefaults entityID=<entityID> REMOTE_USER="eppn persistent-id targeted-id"> <Sessions lifetime="28800" timeout="3600" relayState="ss:mem" checkAddress="true" handlerSSL="true" cookieProps="https"> <SSO entityID="https://sso.nikhef.nl/sso/saml2/idp/metadata.php"> SAML2 SAML1 </SSO> <Logout>SAML2 Local</Logout> <Handler type="MetadataGenerator" Location="/Metadata" signing="false"/> <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/> <Handler type="Session" Location="/Session" showAttributeValues="false"/> <Handler type="DiscoveryFeed" Location="/DiscoFeed"/> </Sessions> <MetadataProvider type="XML" uri="https://sso.nikhef.nl/sso/saml2/idp/metadata.php" backingFilePath="sso.nikhef.nl-metadata.xml" reloadInterval="7200"> </MetadataProvider> ... </ApplicationDefaults> ...
Make sure to fill the 'entityID' in the ApplicationDefaults to a unique id, in our case it's simply the hostname of the service that we're setting up. The 'entityID' in the SSO has to match the id provided by the IdP metadata. Last, a MetadataProvider has to be registered, pointing to the right endpoint of the Nikhef SSO in our case.
Note! We disabled the signature verification on the metadata that shibboleth is downloading from the IdP. The reason why we did this was because the metadata seemed to be signed with the certificate that is provided through the metadata itself.
Before you can use the Nikhef SSO as your IdP you have to make sure to register the metadata provided by your service with the IdP. You can generate this metadata by looking at the /Shibboleth.sso/Metadata endpoint of your service.
Next you can configure apache to restrict access to a subtree of your choice only to privileged users by redirecting them to the Nikhef SSO. In our case this will be the whole tree starting from /. Add the following section into /opt/rh/httpd24/root/etc/httpd/conf.d/jenkinsproxy.conf:
<Location /> SSLRequireSSL AuthType shibboleth ShibRequestSetting requireSession 1 Require valid-user </Location>
After restarting httpd24-httpd and shibd you should that when accessing jenkins, you get redirected to the SSO page to log in.