Jenkins can be a pretty important piece of infrastructure to a company. This is especially true if you’re using it as an continuous delivery platform to various environments. One of the important aspects of securing Jenkins is to enable SSL/HTTPS support so that the traffic to and from Jenkins is encrypted. In this guide we’re going to use a Jenkins instance that runs inside a Docker container. We’re going to use the official Jenkins Docker image available at DockerHub.
Creating a Keystore
If you haven’t bought a certificate already this would be a good place to start. If you don’t want to buy a certificate you can simply generate a self-signed keystore for testing purposes like this:
$ keytool -genkey -keyalg RSA -alias selfsigned -keystore jenkins_keystore.jks -storepass mypassword -keysize 2048
If you have already bought a certificate things will be a bit more complicated. It’s common that the purchase comes with various files such as the ones below (in this case we have a wildcard certificate but it doesn’t really matter):
|The signed certificate (public key)
|This is the private key
|An intermediate certificate*
* An intermediate certificate can sign certificates on behalf of the root CA. The root CA signs the intermediate certificate, forming a chain of trust. There may be many intermediate certificates.
So let’s begin by creating an encrypted PKCS#12 file that contains a combination of the signed certificate (public key) and our private key (you’ll be prompted to choose a password, write this down somewhere safe):
$ openssl pkcs12 -export -in
.crt -inkey .key -out jenkins.p12
This will generate an encrypted PKCS#12 file called `jenkins.p12`. Next create a Java keystore from this file (and write down the password somewhere safe):
$ keytool -importkeystore -srckeystore jenkins.p12 -srcstoretype PKCS12 -destkeystore jenkins_keystore.jks -deststoretype JKS
Now we have our keystore file but it’s not yet complete. We must also import all of our intermediate certificates and the root CA certificate to our keystore. First assemble all the intermediate certificates and the root certificate into one file, let’s call it `intermediaries.crt`. To do this simply open each intermediary certificate in a text editor and copy its content into the `intermediaries.crt` file (and don’t forget to do include the root certificate as well into this file). Ok now we’re ready to import them to our keystore:
$ keytool -importcert -keystore jenkins_keystore.jks -trustcacerts -alias intermediateCA -file intermediaries.crt
Voila! The keystore should now be complete and ready to be used by Jenkins. You can check that everything looks ok by doing:
$ keytool -list -v -keystore jenkins_keystore.jks | egrep "Alias|Valid"
It should say that the keystore is valid (among other things).
Using the Keystore in Jenkins
We’re going to use the official Jenkins Docker image to start up our Jenkins instance. Luckily the Dockerfile used to generate the image contains an EntryPoint pointing to the jenkins.sh script which allows passing command-line arguments to Jenkins when starting the container.
So we need to somehow expose our `jenkins_keystore.jks` file to Jenkins. One way to do this is to mount a volume from the host filesystem to Jenkins. For example we want Jenkins to store its data in a folder on the host called `/home/ubuntu/johndoe/jenkins` then we should copy the `jenkins_keystore.jks` into this folder. Next let’s start Jenkins:
$ docker run -v /home/ubuntu/johndoe/jenkins:/var/jenkins_home -p 443:8443 jenkins --httpPort=-1 --httpsPort=8443 --httpsKeyStore=/var/jenkins_home/jenkins_keystore.jks --httpsKeyStorePassword=
And that’s it! Jenkins should now start (inside Docker) and expose port 443 on the host which is forwarded to port 8443 in the container. We specify `–httpPort=-1` to disable HTTP traffic to Jenkins altogether and force the use of HTTPS. You should replace the
<keystore password> with the password you chose earlier when creating the `jenkins_keystore.jks` file.
Going all the way from the crt files to a Jenkins instance running with SSL support inside Docker requires a bit of work so hopefully this guide can make the process a bit easier.