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.
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.
Next, agree to the terms and conditions
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.
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
- Set the listening port to 443 for https
- Set the hostname
- Set ssl to on
- Specify the ssl_certificate and and the private key file ( generated by certbot )
- .well-known folder location. This will be needed for renewing of certificate.
- Proxy configuration
The second server configuration does the following:
- Listen to port 80 ( for normal http requests )
- 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:
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.
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.
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:
- Create a file renew_ssl.sh in /opt/scripts folder ( or any folder of your choice )
- Put the content as below
#!/bin/sh
echo “Renewing certificate….”
certbot renew –quiet
echo “Certificate renewal completed.. “ - Save the file
- 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.
- Execute the following to open the crontab
crontab -e - 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 - 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
Hi, thanks for the tutorial. I’ve followed it and have letsencrypt with nginx in front of tomcat instance. Even though I have removed port 8081 via firewall cmd, the tomcat instance can still be reached using 8081.
https://edu.aldontutoring.com.au/Aldon or edu.aldontutoring.com.au:8081/Aldon
I only want to have access via https.
John
You can change your nginx to start on 80 and then have the below configuration
If your nginx is starting on 8081, you can change listen 80 to listen 8081
# Configuration to redirect normal http calls to https
server {
listen 80;
server_name secure.example.com;
return 301 https://$host$request_uri;
}