I have seen many articles on utilizing Spring Boot with port forwarding and SSL certificates. None of these articles seem complete. Recently, I have had to deploy a Spring Boot app into production. I could not find good reference material for what I was trying to accomplish. I needed the app to be usable on port 80 with SSL enabled using LetsEncrypt as the certificate authority. In Linux, any port under 1024 has special privileges. There are two main options. The first is to use iptables to redirect port 80 traffic to the port that the Java web app is running on, usually port 8080. This option lacks SSL capabilities. The second option is to use a proxy, usually Apache or Nginx.
Iptables
If you don’t need SSL capabilities, iptables is the easiest and most simple option:
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 # Port 80 traffic will be directed to port 8080
The main drawback of this approach is that iptables cannot redirect to HTTPS only sites. If you need to remove this, then just enter these simple commands in your terminal:
iptables -t nat --line-numbers -L # List rules iptables -t nat -D PREROUTING {num} # Remove rule. Replace {num} with the number from the command above.
Apache
Using Apache or Nginx is the most flexible option. Configuration is much more onerous than using iptables. In this tutorial, we will be using Apache on Ubuntu.
- First install Apache and enable modules:
sudo apt-get install apache2 sudo a2enmod proxy sudo a2enmod proxy_http
2. Navigate to configuration directory:
cd /etc/apache2/sites-available
3. Now you will need to modify the 000-default.conf file adding these lines under the "VirtualHost"
brackets:
ProxyRequests Off ProxyPreserveHost On ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/
4. Restart Apache:
sudo service apache2 restart
Now port 80 should be serving traffic through port 8080. If the app doesn’t use SSL, feel free to stop reading here. Make sure everything is working before continuing. The next step is enabling SSL.
To create the LetsEncrypt SSL certificate, we must first ensure that we are inside the terminal on the production server. This will not work on your local machine. Here are the steps to produce the certificate:
wget https://dl.eff.org/certbot-auto # Download client to server chmod a+x certbot-auto # Make executable ./certbot-auto # Install dependencies to server ./cerbot-auto certonly -a standalone -d {domainName} -d {secondDomainName} # Create certificate (on Debian derivatives it will create the certificates at "/etc/letsencrypt/live/{domainName}"
Now we must navigate into the directory that LetsEncrypt generated:
sudo su # Need root to see files cd /etc/letsencrypt/live/{domainName}
Finally, we can see the contents that LetsEncrypt created:
cert.pem chain.pem fullchain.pem privkey.pem
Our next step is to create keys that are compatible with Spring Boot. To create a keystore for Spring Boot:
openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out keystore.p12 -name tomcat -CAfile chain.pem -caname root
Now move the key somewhere readable to your Spring Boot application and then append the following attributes to the application-properties:
security.require-ssl=true server.ssl.key-store={key_store_location} server.ssl.key-store-password={key_store_password} server.ssl.keyStoreType=PKCS12 server.ssl.keyAlias=tomcat
Now we need to modify Apache configuration file again. To make all HTTP requests redirect to HTTPS, append this to the top of the config file:
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
Next we need to ensure that we can proxy to port 8080 with SSL, we need to add the SSLCertificateKeyFile, SSLCertificateChainFile,
and SSLCertificateFile
. Your final config file should look something like this:
# Making all web requests HTTPS RewriteEngine On RewriteCond %{HTTPS} off RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} ServerAdmin webmaster@localhost # Replace this if you want. ServerName {domain} SSLEngine on SSLProxyEngine On SSLProtocol All -SSLv2 -SSLv3 # Disable SSL versions with POODLE vulnerability SSLCertificateKeyFile /etc/letsencrypt/live/{domain}/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/{domain}/chain.pem SSLCertificateFile /etc/letsencrypt/live/{domain}/fullchain.pem ProxyRequests Off ProxyPreserveHost On ProxyPass / https://localhost:8080/ ProxyPassReverse / https://localhost:8080/ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined
Now restart Apache:
sudo service apache2 restart
Finally, we have HTTPS traffic which proxies traffic from port 80 to 8080. I think this process is very onerous just to get SSL running for a Spring Boot app. Hopefully, Spring Boot will have a “no configuration” solution soon.
References:
Your web has proven useful to me.
Using configuration like the example above means the application will no longer support plain HTTP connector at port 8080.
Great solution, thank you so much. I tested the apache “version” in the past, but for now i need just iptable approach and that is great.