Support HTTPS in Azure Marketplace image for Jenkins

Create a Jenkins server on an Azure Linux VM from the Azure portal has introduced how to configure Jenkins server in azure based on Azure Marketplace image for Jenkins. This image is published by Microsoft, as it has configured Nginx and Jenkins environment well and also make sure they are started automatically,it is very fast and easy to setup the Jenkins based on this image. Based on current implementation, the Jenkins console is inaccessible through unsecured HTTP, there is instruction shown below when accessing the HTTP endpoint. That means in order to secure the conversion, ssh is needed to setup the tunnel.

Recently, I got some questions regarding to how to access Jenkins portal via HTTPS, and this article is about this requirement.

Generate Let's Encrypt Certificate

In order to setup HTTPS, we need either create self-signed certificate or require certificate from certificate authority. We will require certificate from Let's Encrypt. Before moving forward, make sure we have already had our own domain name and also make sure a CNAME entry is created for the target VM which should have been provisioned in Azure. Then ssh into the VM and execute the below commands. The first git command is used to clone the letsencrypt GibHub repository into the VM locally, as the image has installed git already, there is no need to install git anymore. When executing letsencrypt-auto command, there are three options nginx, standalone or Apache to choose, I have tried option 2 (standalone) to generate the certificate successfully, just please make sure to "service nginx stop" firstly, as nginx has been configured to bind to 80 port be default, let's encrypt standalone server will fail to bind to 80 port because of that.

 git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto certonly

After filling the target domain name and also email address, finally the certificate will created into /etc/letsencrypt/live/customdomain/, we will use the fullchain.pem and privkey.pem in next step.

Setup SSL Offloading in Nginx

Though there are quite a lot of web servers in Linux to support SSL offloading, nginx has been installed in the VM and we will go ahead to achieve our requirement just based on a little bit configuration change. The following is the default configuration in /etc/nginx/sites-available/default. As we can see the url rewrite to Jenkins-on-azure, that's the reason why it is showing the ssh instruction page when accessing the portal via HTTP endpoint.

 
server {
    listen 80;
    server_name jacjenkins.centralus.cloudapp.azure.com;
    error_page 403 /jenkins-on-azure;
    location / {
        proxy_set_header        Host $host:$server_port;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;


        # Fix the “It appears that your reverse proxy set up is broken" error.
        proxy_pass          https://localhost:8080;
        proxy_redirect      https://localhost:8080 https://customname.centralus.cloudapp.azure.com;
        proxy_read_timeout  90;
    }
    location /cli {
        rewrite ^ /jenkins-on-azure permanent;
    }

    location ~ /login* {
        rewrite ^ /jenkins-on-azure permanent;
    }
    location /jenkins-on-azure {
      alias /usr/share/nginx/azure;
    }
}

Let's modify the above file to the following, it will let nginx listen on 443 port and use the Let's Encrypt certificate to setup the TLS conversation. Save the modified file, execute "service nginx restart", also make sure 443 port is opened in the NSG inbound rule, we should be able to access the Jenkins portal by HTTPS endpoint now. For more detail about nginx SSL setup, please check NGINX SSL Termination.

 
server {
    listen 443 ssl;
    server_name customdomain;
    ssl_certificate /etc/letsencrypt/live/customdomain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/customdomain/privkey.pem;
    location / {
        proxy_set_header        Host $host:$server_port;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;


        # Fix the “It appears that your reverse proxy set up is broken" error.
        proxy_pass          https://localhost:8080;
        proxy_read_timeout  90;
    }
}