Zero cost verified https using letsencrypt and nginx for tomcat 8

We have got our application running in the cloud and wanted to implement SSL ( https ) for the requests. We had previously thought it as an expensive affair as the certificate authority like verisign charges decent amount per year. That’s when we came across letsencrypt.org which is a opensource and free certificate authority sponsored by tech giants like cisco.

They provide easy and intuitive methods to generate free certificates for any number for websites and sub domains. In this post, I am going to explain implementing https for a site running tomcat 8.

The Setup

We are running a spring based application in tomcat 8 and is going to make all the requests https.

  • Apache Tomcat 8  Webserver running in port 81
  • Nginx webservice for reverse proxy listening to port 80 and port 443 SSL
  • RHEL Centos 7 running in AWS
  • letsencryt as certificate authority
  • certbot as the client tool for letsencryt

 

Nginx

We need to start with nginx as it will be the primary interface for port 80 and also the SSL port 443.

Installing nginx

We are currently using Centos 7  for the server and nginx can be installed from epel release packages. Run the following commands in the terminal.

Run this if the epel release packages are not installed

$ sudo yum install epel-release
$ sudo yum install nginx

Once the installation is completed, we need to make sure that the port in the nginx is configured to 80. You can check it in the nginx.conf file. The default location for nginx is /etc/nginx/nginx.conf

Look for the line  starting with listen  and make sure that its 80. If you want to use a different port for nginx, you can change in the marked places.

selection_155

Configuring host in nginx

Once nginx is installed, we need to configure the host for which we are going to do the https redirection. In the nginx.conf file, there is a line that says

include /etc/nginx/conf.d/*.conf;

It means that all the .conf ending files in the /etc/nginx/conf.d directory would be  recognized by nginx and configured during startup. So we need to create a file for the domain/hostname ending with .conf in this directory.
For eg: If the domain / host is secure.example.com, we need to create a file like secure.example.com.conf

We need to put the following lines in the file intially and save

server {

listen 80;
server_name secure.example.com;

location /.well-known {

alias /var/www/secure.example.com/.well-known;

}

}

In the above statments, we are telling nginx to listen to port 80 for requests coming on the hostname as secure.example.com.

The location /.well-known  is required for the letsencrypt cert client to allow hidden folders to be served for completing the acme-challenge.

After this is done, we need to create a folder for each domain / host . You can place the folders in any location , just make sure that its properly pointed in the conf file. I am using the root as /var/www  and created a dir secure.example.com  folder as specified in the conf file.

Starting nginx

Now its time to start nginx server. You can start nginx in centos by issuing the following command:

service nginx start

In case the service is already running and just want to reload the conf, you can issue the command:

service nginx reload

I am only explaining nginx aspect from the point of generating and servicing https requests. You can use nginx to serve the entire site and that would require some configuration changes to conf file for the respective sites.

SSL using letsencrypt

As specified in the beginning, we will be using letencrypt as the certificate authority. They are free, automated , new and fantastic !!. You don’t need to go behind the expensive verisign certificates to provide secure content over https. Learn more about them in the official website letsencrypt.org

Letsencrypt client

For using the letsencrypt, we need to use a client for obtaining and renewing the certificates. There are so many clients specified in the website, but we are going to use the one called certbot

Certbot is simple and straightforward and can be used with minimum commands. We need to start by installing certbot. You can view the entire document for nginx in centos 7 here.

$ sudo yum install epel-release
$ sudo yum install certbot

Generating a certificate

Once certbot is installed, we need to generte SSL certificate for our site using letsencrypt. Its simple as issuing a command from the terminal:

 $ certbot certonly –webroot -w /var/www/secure.example.com -d secure.example.com

When you are running this for the first time , you will be asked to enter the email address of the webadmin in case you need to recover certificates or letsencrypt need to communicate with you.

le-email

Next, agree to the terms and conditions

agreement

This will create certificates for secure.example.com  with the root of web for these as /var/www/secure.example.com

When we execute the above command, certbot will create .well-known  folder under the /var/www/secure.example.com folder and will try to complete an access challenge. This is to verify the authenticity of the site and to make sure that you are the web admin. By default, nginx will not allow access to hidden folders ( folders starting with .(dot) ). To override it , we have provided the entry for location /.well-known   in the conf file for the host.

Once the command executes successfully, you will receive a message saying that the ssl certificates are created successfully and are placed under the following location . The location for the generated certificates is usally /etc/letsencrypt/live/secure.example.com .  We will be using these files in the conf file for the ssl validation in next step.

lets-encrypt-nginx-certbot-create-certificate

Configuring certificate in nginx for https

Now that we have the certificates generated, lets put it to use. We need to update the conf file of the domain for which the certificates are generated. Put the following content in the secure.example.com.conf overwriting the current content.

# configuration for the SSL
server {

# Set the ssl listener
listen 443 ssl;
server_name secure.example.com ,localhost;

# set ssl to on
ssl on;

# Specify the ssl certificates
ssl_certificate /etc/letsencrypt/live/secure.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/secure.example.com/privkey.pem;

# This is required for renew of  the certificate
location /.well-known {

alias /var/www/secure.example.com/.well-known;

}

# Setting the redirect for the certificate
location / {

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_pass http://127.0.0.1:81;
proxy_redirect http://127.0.0.1:81 https://secure.example.com;

}

}

# Configuration to redirect normal http calls to https
server {

listen 80;
server_name secure.example.com;
return 301 https://$host$request_uri;

}

You need to replace the secure.example.com with whatever is your domain or sub-domain is.

In the file we can see that there are two server {} configurations. The first server configuration does the following

  1. Set the listening port to 443 for https
  2. Set the hostname
  3. Set ssl to on
  4. Specify the ssl_certificate and and the private key file ( generated by certbot )
  5. .well-known folder location. This will be needed for renewing of certificate.
  6. Proxy configuration

The second server configuration does the following:

  1. Listen to port 80 ( for normal http requests )
  2. send it to https with a 301 ( resource permanently moved ) status code so that the browser will always try https for the same url next time.

At this point, we need to restart the nginx server once. So issue the following command in the terminal.

service nginx start

Configuring Apache tomcat

We need to do some configuration changes in the apache tomcat for the SSL proxy to work. Lets look into that now.

server.xml  changes

The configuration for apache tomcat resides in the server.xml file under apache-root/conf folder. Keep a backup copy of the file incase we need to revert the settings.

First we need to update the port of the apache tomcat to 81 ( or whatever port you want to use in the backend. Please make appropriate changes in the conf file also ). To do this, find the section in the below image:

Selection_156.jpg

Next we need to add a configuration that allow apache to identify and configure the forwarded details by the proxy. Add the following lines to the server.xml file

<Valve className=”org.apache.catalina.valves.RemoteIpValve”
remoteIpHeader=”x-forwarded-for”
remoteIpProxiesHeader=”x-forwarded-by”
protocolHeader=”x-forwarded-proto”
/>

These need to be added under the existing <Valve> configuration in the server.xml file.

selection_157

Once these changes are done, we are good with tomcat and you can restart the tomcat instance for getting the change into effect.

Allow the port 443 in firewall

One major mistake I did during setup was that the 443 ( https) port was not allowed in my server firewall as well as in the inbound configuration of AWS. We need to do this for allowing the traffic or else you will only see a rotating progress bar in the browser and final “Unable to connect” message.

Allow the port in firewall

Since I am using Centos  7, I can use the firewall-cmd command to allow the traffic from https port ( 443) . Issue the following commands

$ firewall-cmd –permanent –add-port=443/tcp
$ firewall-cmd –reload

Allow the port in the Amazon AWS inbound rules

We need to make sure that the port is open in the amazon AWS console also. This step is required only if you are using AWS for hosting the server.

Goto the security group for your server and click on the edit button. You will need to add the new rule under inbound and allow 443 for everywhere.

Selection_158.jpg

At this point, when you put the secure.example.com, you should be redirected to https://secure.example.com

Renewal of certificate

One important thing to be noticed is that , any certificate generated by letsencrypt is valid only for 90 days. You need to have the certificate renewed after that. This can be done by certbot client itself. To check if certbot can renew, we can do a dry run using the following command

$ certbot renew –dry-run

If you get a successful response, then we can configure the certbot to run twice a day for renewing.

Renewal script

We actually created a script to have the certbot run and then schedule it in cron . We can do it as below:

  1. Create a file renew_ssl.sh in /opt/scripts folder ( or any folder of your choice )
  2. Put the content as below
    #!/bin/sh
    echo “Renewing certificate….”
    certbot renew –quiet
    echo “Certificate renewal completed.. “
  3. Save the file
  4. Execute the following to make it executable.
    chmod +x renew_ssl.sh

Setting up cron job for renewal

We are running the script twice a day to make sure that we are checking the expiry. The script will only update when there is certificates to be renewed.

  1. Execute the following to open the crontab
    crontab -e
  2. Enter the following lines of text
    0 7 * * * /opt/scripts/renew_ssl.sh > /opt/scripts/renew_ssl.log 2>&1
    0 21 * * * /opt/scripts/renew_ssl.sh > /opt/scripts/renew_ssl.log 2>&1
  3. Exit the cron editor

Now this job will be run at morning 7 and night 9 to renew the certificates. You may choose a different time for the cron.

 

 

So thats it for the setup of nginx as a proxy for https to Tomcat running in a Centos server. Please comment below if you have any queries or need more clarity on any sections described.

regards
S

Advertisements

2 thoughts on “Zero cost verified https using letsencrypt and nginx for tomcat 8

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s