Spring Boot can run as a standalone server, but putting it behind an Apache web server has several advantages, such as load balancing and cluster management. Now with LetsEncrypt it’s easier than ever (and free) to secure your site with SSL.
In this tutorial, we’ll secure an Apache server with SSL and forward requests to a Spring Boot app running on the same machine. (And once you’re done you can add Stormpath’s Spring Boot integration for robust, secure identity
management that sets up in minutes.)
Set Up Your Spring Boot Application
The most basic Spring Boot web application just shows a homepage. Using Maven, this has four files: pom.xml
, Application.java
, RequestController.java
, and home.html
.
The pom.xml
file (in the root folder) declares four things: application details, starter parent, starter web dependency, and the Maven plugin (for convenience in running from the console).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.stormpath.sample</groupId> <artifactId>basic-web</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
Application.java
(src/main/Java/com/stormpath/tutorial) simply declares the app part of Spring Boot.
1 2 3 4 5 6 7 |
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } |
RequestController.java
(src/main/java/com/stormpath/tutorial) maps all requests to the homepage.
1 2 3 4 5 6 7 8 9 |
@Controller public class RequestController { @RequestMapping("/") String home() { return "home.html"; } } |
Finally home.html
(src/main/resources/static) is just declares a title and message.
1 2 3 4 5 6 |
<!DOCTYPE html> <html> <head><title>My App</title></head> <body><h1>Hello there</h1></body> </html> |
Note: you can clone this basic project from the GitHub repo.
Next, run:
1 2 |
mvn spring-boot:run |
You should see the page when browsing to localhost:8080
.
Launch Apache
Next, we need to fire up Apache. I created an Ubuntu instance on EC2 (check out the AWS Documentation for a getting started guide). I then logged in and installed Apache with the following:
1 2 |
sudo apt-get install apache2 |
This should install and start an Apache server running on port 80
. After adding HTTP
to the instance inbound security group (again here, the AWS Documentation contains a guide) you should be able to browse to the public DNS.
Add LetsEncrypt
LetsEncrypt has policies against generating certificates for certain domains. amazonaws.com
is one of them (because they are normally transient). You need to add a CNAME to a personal domain that points to the instance you created. Here I’m using kewp.net.za
.
The following commands should install SSL certificates to your domain automatically.
1 2 3 4 |
sudo git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt cd /opt/letsencrypt ./letsencrypt-auto --apache -d kewp.net.za |
Browsing to your personal domain should now bring up the Apache homepage with SSL.
Note: Chrome didn’t like the security of my page (I didn’t get the green icon) because the standard Ubuntu front-end returns unencrypted (http
) contents.
Build a Connector for Spring Boot
We have to tell Spring Boot to make a connector using AJP, a proxy protocol that connects Apache to Tomcat. To do this dd the following to the bottom of the class in Application.java
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Bean public EmbeddedServletContainerFactory servletContainer() { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory(); Connector ajpConnector = new Connector("AJP/1.3"); ajpConnector.setProtocol("AJP/1.3"); ajpConnector.setPort(9090); ajpConnector.setSecure(false); ajpConnector.setAllowTrace(false); ajpConnector.setScheme("http"); tomcat.addAdditionalTomcatConnectors(ajpConnector); return tomcat; } |
We’re setting the AJP port to 9090
manually. You might want to add a variable to application.properties
and pull it in with @Value
to make it more configurable.
Restart the app as above and you should see messages that Tomcat is now listening on both port 8080
and 9090
. Note: the Github repository above has the connector code included so you can just use that from the start.
Run the Application on Your Instance
In the screenshots above I’ve been running the web app on my local Windows machine for testing. To get it to run on your instance just do the following.
1 2 3 4 |
git clone https://github.com/stormpath/apache-ssl-tutorial cd apache-ssl-tutorial mvn spring-boot:run |
Reroute Apache
Now we tell Apache to pass all traffic to our application. We can use the proxy
and proxy_ajp
modules for that. But first, we need to enable them.
1 2 3 |
sudo a2enmod proxy sudo a2enmod proxy_ajp |
Now we need to update the virtual host on port 443 to use the connector we created. For me the relevant file was in /etc/apache2/sites-available/000-default-le-ssl.conf
. Add the follow to the bottom of the <VirtualHost *.443>
element.
1 2 3 |
ProxyPass / ajp://localhost:9090/ ProxyPassReverse / ajp://localhost:9090/ |
And at last, restart the server.
1 2 |
sudo service apache2 restart |
Add Another Security Group
Now we need to ensure that EC2 is going to allow traffic to HTTPS. Add HTTPS
to the inbound security group as before.
Fire Up Your New (Secure) Spring Boot Application!
Now when you browse to your domain, you should see our Spring Boot web app, secured behind SSL!
Configure SSL Between Apache and Tomcat
One last thing. The traffic between Apache and Tomcat is currently unencrypted (HTTP). This can be a problem for some apps (like Stormpath – which requires a secure connection). To fix this, we use something called Tomcat’s RemoteIpValve
. Enable this by adding the following to your application.properties
.
1 2 3 |
server.tomcat.remote_ip_header=x-forwarded-for server.tomcat.protocol_header=x-forwarded-proto |
Apache will set these headers by default and then Tomcat (embedded in Spring Boot) will properly identify the incoming traffic as SSL.
Add Authentication
Application security is intrinsic to what we do here at Stormpath. Our team of Java security experts have just released the 1.0 version of our Java SDK, and with it massive updates to our Spring and Spring Boot integrations. You can add authentication for secure user management in this or any Spring Boot application in just 15 minutes! Check out our Spring Boot Quickstart to learn how!