Running Jenkins with SSL on default HTTPS port

Note: Since Jenkins is a java application running with limited system permission, it’s socket listeners can’t listen on ports below 1024. So if you want to run Jenkins with HTTPS enabled on the default HTTPS-port 443, you need some work around.

Basic solution (complete):

Here are the complete steps I took when switching one of our Jenkins server from running on it’s default port 8080 with HTTP to HTTPS on port 443:

  • You need a “Java keystore” of the SSL-certificate you want to use. It does not matter it it’s self-signed or an official certificate, the steps needed are identical:
  • Copy the certificate, private key and (if present) intermediate CAs to your Jenkins host. The files need to be in PEM-format. If not, convert them respectivly like described here.
  • Convert the certificate-files to one single-filed PKCS12 container. Note that you have to set a password here, otherwise the import in the keystore will fail later. If you don’t have intermediate CA, ommit the “-CAfile” option.
    openssl pkcs12 -export -inkey <private key> -in <certificate> -CAfile <intermediate> -out <container>.p12 -name <some name> -caname root
    For example:
    openssl pkcs12 -export -inkey example-org.key -in example-org.pem -CAfile example-org-ca.pem -out /root/jenkins.p12 -name example -caname root
  • Make sure, that the Java “keystore”-command is present. This should be the case if you installed Java via your distributor’s repository. If not, install “keystore” manually.
  • Run the keystore tool with following parameters:
    keytool -importkeystore -srckeystore <container>.p12 -srcstoretype pkcs12 -destkeystore <keystore>.jks
    For exmaple:
    keytool -importkeystore -srckeystore /root/jenkins.p12 -srcstoretype pkcs12 -destkeystore /var/lib/jenkins/keystore/jenkins_keystore.jks
    In this case the keystore is created at “/var/lib/jenkins/keystore/jenkins_keystore.jks”. You should create it in a subfolder located in the home-dir of your Jenkins installation. In my case the home-dir is “/var/lib/jenkins”. If needed, create the subfolder-structure first. You will be asked for two passwords. The first is the new password that Jenkins will use to access the keystore, the second is the one you used when creating the PKCS12-container. Of course, you can use identical passwords. However, the new password need a minimum length on 6 characters.
  • Make sure that the user that runs Jenkins is the owner of the created keystore. In my case the user and group are both “jenkins”:
    chown -R jenkins:jenkins /var/lib/jenkins/keystore/
  • I’m note sure if this step is really needed for Jenkins, but it’s always a good idea to have the system’s CA trust store up to date. More details can be found here. To do so, follow the steps below. If your certificate does not have any intermediate CA, you can ommit these steps:
    Copy the intermediate CA file to the “source/anchors/” subfolder of your system’s “pki/ca-trust”-structure. The exact location differs, so check the documentation on your distribution if you’re unsure. For CentOS it is located at “/etc/pki/ca-trust”:
    cp example-org-ca.pem /etc/pki/ca-trust/source/anchors/
    Update the CA trust store by running:
    update-ca-trust
  • In order to be able to access Jenkins via the default HTTPS-port 443, we need to enable IP-forwarding and add some rule to our iptables/netfilter ruleset. IP-forwarding can the enabled by sysctl. I did this by adding some lines to “/etc/sysctl.conf”:
    echo "# For Jenkins 8443 -> 443 IP forward" >> /etc/sysctl.conf
    echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
    echo "net.ipv4.conf.eth0.route_localnet = 1" >> /etc/sysctl.conf
  • Important: In the global security settings section of your Jenkins administration panel you should set the port for incoming JNLP-connections to a fixed port. Otherwise it’s problematic to set decent iptable-rules. In my case I use port 43623.
  • Tell Jenkins to use HTTPS instead of HTTP by default. This can be done in Jenkins’s “default” config, normally located at “/etc/default/jenkins” or “/etc/sysconfig/jenkins” – depending on your distribution. Open the file in an editor of your choice and change the following lines:
    JENKINS_HTTPS_PORT="8443"
    JENKINS_HTTPS_KEYSTORE="<filepath to the keystore>"
    JENKINS_HTTPS_KEYSTORE_PASSWORD="<password you set>"
    JENKINS_HTTPS_LISTEN_ADDRESS="<listen IP address>"

    For example:
    JENKINS_HTTPS_PORT="8443"
    JENKINS_HTTPS_KEYSTORE="/var/lib/jenkins/keystore/jenkins_keystore.jks"
    JENKINS_HTTPS_KEYSTORE_PASSWORD="changeit"
    JENKINS_HTTPS_LISTEN_ADDRESS="0.0.0.0"
  • Next step is to add some rules to iptables to establish an internal port-forwarding for requests coming from outside on port 443 to localhost port 8443. You can find additional information here.
    iptables -A INPUT -p tcp --dport 43623 -j ACCEPT
    iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
    iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
    iptables -A INPUT -p tcp --dport 443 -j ACCEPT
    iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
    iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443

    Note the port 43623 here. This is the JNLP-port I was talking about. It is needed if you have Jenkins-slaves connected using the JNLP-connector.
    Save the iptables rules by:
    iptables-save > /etc/sysconfig/iptables(RHEL, CentOS, …)
    iptables-save > /etc/iptables/rules.v4"(Debian, Ubuntu, …)
  • Reboot the machine. After the reboot, Jenkins should the reachable via https:// without the need to add a port number.

Advanced solution:

If you want to redirect your users still accessing Jenkins via HTTP and ports like 8080 and have them redirected to 443 automatically, you can follow the additional steps below. I use an apache 2.2.x for the redirection here.

  • If not yet installed, install Apache 2.2.x or 2.4.x on your system.
  • Since we want the Apache to process resquest on port 8080 coming from outside, we need to tweak the defaults for Jenkins ( “/etc/default/jenkins” or “/etc/sysconfig/jenkins”) a bit. Change the following lines:
    JENKINS_PORT="-1"
    JENKINS_HTTPS_LISTEN_ADDRESS="127.0.0.1"

    by doing so, you disable the old HTTP-port 8080 completely and allow access to the HTTPS-port 8443 only by localhost.
  • Now adjust your Apache config (“/etc/httpd/conf/httpd.conf” or “/etc/apache2/ports.conf”) that it listens on both ports 80 and 8080:
    Listen 80
    Listen 8080

    Note: Do not enable HTTPS (port 443) in the Apache config since this port is occupied by Jenkins.
  • Enable “NameVirtualHost” for both, 80 and 8080 (not needed for Apache 2.4 and higher):
    NameVirtualHost *:80
    NameVirtualHost *:8080
  • Create a new site-config for the redirection. (“/etc/httpd/conf.d/jenkins.conf” or “/etc/apache2/sites-available” respectively):
    <VirtualHost *:80>
    ServerAdmin root@localhost.de
    ServerName jenkins.example.org
    ServerAlias jenkins

    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^(.*)$ [NC]
    RewriteRule ^(.*)$ https://jenkins.example.org$1 [R=301,L]
    </VirtualHost>

    <VirtualHost *:8080>
    ServerAdmin root@localhost.de
    ServerName jenkins.example.org
    ServerAlias jenkins

    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^(.*):8080$ [NC]
    RewriteRule ^(.*)$ https://jenkins.example.org$1 [R=301,L]
    </VirtualHost>
  • If you’re running a Debian-based system, enable the new site by:
    a2ensite jenkins
  • We need to adjust the iptable-rules to reflect the changes in routing:
    iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:8443
    iptables -A FORWARD -i eth0 -m state --state NEW -m tcp -p tcp -d 127.0.0.1 --dport 8443 -j ACCEPT
    iptables -t nat -A OUTPUT -p tcp --dport 443 -d 127.0.0.1 -j DNAT --to-destination 127.0.0.1:8443

    And save the modified rules again:
    iptables-save > /etc/sysconfig/iptables(RHEL, CentOS, …)
    iptables-save > /etc/iptables/rules.v4"(Debian, Ubuntu, …)
  • Now reboot your system to check every service comes up automatically. After the reboot requests to http://<your sitename>:8080 and http://<your sitename> should be redirected to https and thus to your newly secured Jenkins.

References:

3 comments

  1. Oh man, this is the definitive guide. I might also add that you can get the CA cert from Let’s Encrypt.

Leave a comment

Your email address will not be published. Required fields are marked *