How do you set up HTTPS with CloudBees CI on traditional platforms?

Article ID:219001547
4 minute readKnowledge base

Issue

  • You want to enable HTTPS in your CloudBees CI cluster using your TLS certificate(s).

  • You want the operations center and controllers to handle the HTTPS.

Resolution

On modern cloud platforms, HTTPS is enabled at the Kubernetes Ingress level, so please refer to the CloudBees CI installation guide corresponding to your Kubernetes provider. On traditional platforms, HTTPS is often similarly handled by the reverse proxy or load balancer. However, if you need an operations center and controllers to handle HTTPS themself, please read on.

Jenkins, and hence CloudBees CI, is built with an internal web container called Jetty via the Jenkins Winstone project. Jetty can serve HTTPS, which Winstone exposes via the command line. Hence, we need to prepare the keystore and update the command line options to enable HTTPS.

If you have intermediate certificates, you will need to concatenate them into a single file. The order of the certificates is important. The server certificate should be first, followed by the intermediate certificate(s), and finally the root certificate. More detailed steps can be found in How To Setup Https Within Jetty With Intermediate Certificates.

Prepare the Keystore

At the time of writing this article, the jetty container currently does not support PKCS#8 format. If you are using another format, like PKCS#8, you are required to convert the format for Jetty to understand. If you already have the certificate in a different format, then it is safe to skip this step.

If you run a supported release on Java 11 or newer, you can use the PKCS #12 archive (.p12 or .pfx file extensions). If the key and certificate are in separate files, any online guide showing how to combine them into the PKCS #12 archive should work. If you are still using Java 8, you can do the following steps to create a Java KeyStore.

  1. The first step is to convert the certificate file (crt) to a pem file:

     openssl x509 -in jenkins-cert.crt -out jenkins-cert.pem -outform PEM
  2. Once the certificate is converted to PEM, then the certificate should be converted to PKCS#12. Note Please make sure to use a password. Having a certificate without a password throws exceptions later on.

     openssl pkcs12 -inkey certificate.key -in jenkins-cert.pem -export -out jenkins-cert.p12
  3. Create a Java Keystore. In this part, we will be creating an empty keystore. The values used for the keystore can be anything as we will be deleting the certificate, leaving it a blank keystore: Note Please make sure to set a password for the keystore.

     keytool -genkey -alias jenkins -keystore jenkins.jks
  4. Delete the certificate which was generated from the above step:

     keytool -delete -alias jenkins -keystore jenkins.jks
  5. Take the certificate and import it into the Java keystore. This command will look different depending on the format of the certificate:

     keytool -v -importkeystore -srckeystore <SRC-CERTIFICATE.CRT> -srcstoretype <SRC-CERT-FORMAT> -destkeystore jenkins.jks -deststoretype JKS

    An example using PKCS#12 keystore:

     keytool -v -importkeystore -srckeystore jenkins-cert.p12 -srcstoretype PKCS12 -destkeystore jenkins.jks -deststoretype JKS

    If you are running Jenkins in a Docker container, it is recommended that you use a Docker volume or bind mount for the directory that the keystore is located in. This way, the certificate won’t have to be copied into each container and all containers can share the same certificate.

Update the command line arguments

Add the certificate to the list of Jenkins arguments. This will tell the jetty container which certificate to use. See How to add Jenkins command line options to CloudBees CI (CloudBees Core)? on installation-specific ways of adding arguments. The JENKINS_ARGS variable is often used to achieve this. For example:

 JENKINS_ARGS="--httpPort=-1 --httpsKeyStore=/path/to/keystore/jenkins.pfx --httpsKeyStorePassword=changeit --httpsPort=8443"
  • The httpPort=-1 argument will tell the jetty container to disable http, so that only https traffic is served via the port you choose using --httpsPort. If you are switching from an already existing instance currently using http, it can make the migration easier to leave the httpPort enabled while you are testing that the --httpsPort is working correctly. Once the instance is serving https traffic without issues, we recommend always setting httpPort=-1.

  • The httpsKeyStore and httpsKeyStorePassword are variables that were set when creating the empty keystore.

  • The httpsPort will tell the jetty container to use https on any port (no default value).

  • You can also optionally disable specific protocols or cipher suites using the ` --excludeProtocols` or --excludeCipherSuites arguments, as documented in https://github.com/jenkinsci/winstone#command-line-options.

If you run Jenkins in a Docker container, you will want to add the JENKINS_ARGS to the docker run command. For example:

 docker run -p 8443:8443 --env JENKINS_ARGS="--httpPort=-1 --httpsKeyStore=/path/to/keystore/jenkins.pfx --httpsKeyStorePassword=changeit --httpsPort=8443" <JENKINS_IMAGE>

The -p 8443:8443 option will publish the container’s 8443 port to the host’s 8443 port. The --env option will set environment variables inside the docker container. In this case, that would be the JENKINS_ARGS environment variable.

Since applications running as a normal user can’t bind to port 443, you can run the controller on port 8443, then redirect all traffic incoming to port 443 to 8443 using iptables: Reverse proxy - iptables

References