Serve Jenkins over HTTPS with Apache as Proxy and Certbot/LetsEncrypt SSL

There are several ways to make Jenkins work over HTTPS, here I’m just providing the path we followed using Apache as a proxy that connects to Jenkins internally.

The goal then is to allow users to connect to a URL like https://jenkins.example.com, preventing connections over http (insecure) and direct one to the Jenkins instance (which by default runs on the port 8080), please notice that Jenkins was installed on an Ubuntu distro using the apt command, and also that in order to follow the instructions provided here you need root access to the server or at least a user with sudo permissions.

The first step is to configure the Jenkins service so that it only responds only to Apache local requests, we edited the file
/etc/default/jenkins, adding a variable

HTTP_HOST=127.0.0.1

And and argument for Jenkins start command:

--httpListenAddress=$HTTP_HOST

So, our file looks like this:

After these changes we restarted the Jenkins service and checked that accessing http://jenkins.example.com:8080 is not possible anymore

The second step is related to the Apache configuration:

We created a folder to be used as document root (so that Certbot can create some validation files there) e.g. /var/www/vhosts/jenkins
and a Vhost configuration file, let’s call it jenkins-cert-renewal.conf

<VirtualHost *:80>
    DocumentRoot "/var/www/vhosts/jenkins/"
    ServerName jenkins.example.com

    <Directory "/srv/apps/jenkins/">
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

Once this conf file is created, it’s required to enable the Vhost and reload/restart Apache:

sudo a2ensite jenkins-cert-renewal.conf; sudo service apache2 restart

By going to http://jenkins.example.com you must get a blank page now

OK, so the next step is to generate the SSL certificate for the first time, make sure Certbot is intalled, if not, follow instructions provided here: https://certbot.eff.org/lets-encrypt/ubuntuxenial-apache.html; to generate the certificate we executed the command:

sudo certbot --apache certonly -d jenkins.example.com -w /var/www/vhosts/jenkins

In the output of this command you can find the path to the certificates and with that proceed to create a second vhost configuration file, this will be used most of the time and will have the logic to redirect the connections from HTTP to HTTPS as well as keeping the HTTPS configuration, let’s call this file jenkins.conf

<VirtualHost *:80>
    DocumentRoot "/var/www/vhosts/jenkins/"
    ServerName jenkins.example.com

    # Redirects traffic to https
    RewriteEngine On
    RewriteRule ^.*$ https://jenkins.example.com/ [L,R=302]
</VirtualHost>

<VirtualHost *:443>
    ServerName jenkins.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/jenkins.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/jenkins.example.com/privkey.pem

    ProxyPreserveHost On
    ProxyRequests Off
    AllowEncodedSlashes NoDecode
    ProxyPass / http://127.0.0.1:8080/ nocanon
    ProxyPassReverse / http://127.0.0.1:8080/
    ProxyPassReverse / https://jenkins.example.com/

    <Proxy http://127.0.0.1:8080*>
        Order deny,allow
        Allow from all
    </Proxy>
</VirtualHost>

Certbox certificates expire after 3 months, since we don’t want to be renewing manually, we decided to create a simple Bash scriptĀ  and configure a cronjob to run it every 3 months

So, the content of the script is something like this:

#!/bin/bash
sudo a2dissite jenkins.conf
sudo a2ensite jenkins-cert-renewal.conf
sudo service apache2 reload
# renew command is pretty basic
sudo certbot renew
sudo a2dissite jenkins-cert-renewal.conf
sudo a2ensite jenkins.conf
sudo service apache2 reload

Then we edited the crontab for the user, adding a job to be executed on a specific day every 3 months (make sure the Bash script created has execution permissions):

# command to renew Jenkins SSL certificate
00 03 18 3,6,9,12 * /path/to/script/ssl-renew-script

We just configured it to run at 3AM every March, June, September and December 18th.

We will keep an eye out on it in 3 months to see how it goes.