SSL Termination, Load Balancers & Java

So you got this killer Java web app you are developing, or you are deploying some super cool off the shelf application, and you decide to use some sort of SSL Accelerator to offload your HTTPS traffic, and it looks something like this:

Only things aren’t going so well. Maybe all the generated URLs for HTTPS connections are coming out as plain HTTP. Or your web app is attempting something clever and ntify when specific requests are plain HTTP and redirect them over to HTTPS, however even after redirection the app still tries to redirect again.

What Gives?

As you have probably guessed, the introduction of the SSL Accelerator has caused a problem because now all the incoming requests to your web app are coming in as plain HTTP, because HTTPS was terminated upstream somewhere and so your application server never knows about it. In Java web applications, this generally manifests itself as HttpServletRequest.isSecure() always returning false, wreaking havoc in its wake. The Servlet machinery makes lots of decisions based on the value of isSecure(). All the way down to when you are defining security-constraint elements in web.xml and setting transport-guarantee to CONFIDENTIAL.

So What’s the Fix?

I’ve encountered this quite often, with various SSL Accelerators, Load Balancers and App & Web Servers. In general, the idea is to maintain separate paths for the requests all the way through to the app servers, like so:

In doing so, you can typically configure the back end web or app server to correctly identify the originating scheme of the request (HTTP or HTTPS).

Any Examples?

The Easy – Plain Ol’ Tomcat Backend.

By far the easiest container to work with is Apache Tomcat. The options available in a Connector clearly had this type of configuration in mind. Specifically these are

scheme – this tells Tomcat which protocol to use to create links, either http or https.

proxyPort – this tells Tomcat what to use as the real originating port, instead of the port the connector is listening on.

proxyHost – similarly, this tells Tomcat what to use as the real host name, instead of the host Tomcat is running on.

secure – this tells Tomcat whether the Connector is secure or not.

SSLEnabled – whether to configure the connector for SSL/TLS or simply Plain HTTP.

So, in the case where you are only using Apache Tomcat in your platform,

The correct configuration can be accomplished by creating two Connectors, one for port 8080 and one for port 8081. These would look like

Introducing Apache Httpd into the mix really all that much harder than using simply Tomcat. Here’s the desired traffic pattern.

You may notice that HTTP protocol stops at the Httpd and then from Httpd to Tomcat the protocol switches to AJP. And then there’s the two streams coming into Httpd, but lets tackle Tomcat first. Its actually not very different at all from straight Tomcat. The only difference is switching to AJP/1.3 for the protocol.

Now, for Httpd. This is solved by setting up a separate virtual servers for each port, and passing them off to the appropriate Tomcat Connector using mod_jk and separate worker pools for each Tomcat Connector.

And that’s it. The one caveat here, is that although originating from HTTPS and plain-HTTP follow different paths even through Httpd, Httpd doesn’t really know that. So if you are doing anything interesting other than serving static content from Httpd, you may have an issue, for example if you are running PHP or something else that may need to care, you may be out of luck.

The Difficult – Less Configurable Application Servers

The Tomcat team as really taken a lot of time to provide a highly configurable platform, but what if this is not your target deployment platform, and you are faced with attempting to do SSL Termination on a platform that isn’t so helpful. One example that I’ve got a bunch of experience with is what was called Sun Java System Web Server and now Oracle iPlanet Web Server. Although I did enjoy working with it, the platform did not provide such an easy method of integrating an external SSL Accelerator.

But all is not lost. Fortunately Java Servlets version 2.3 and newer provide some facilities that can help. Servlets 2.3 provide two interesting features, Filters and Wrappers.

Filters provide a method for you to do some pre-processing of requests before your Servlets, JSP and anything else is invoked (they also can do post-processing but we don’t need that here.)

Wrappers provide a method for you enclose a Request or Response in your own object.

The two, together can help you fake the Java machinery into setting HttpServletRequest.isSecure() and HttpServletRequest.getScheme() into returning the correct values depending on the original protocol.

Now just plumb this up in your Web App’s web.xml as the first filter in the chain, and you are there. Well, most of the way there. there’s the opposite problem now. Every request, no matter what the originating protocol, appear to be HTTPS. In a mixed protocol environment, that is still a problem.

No worries, we can do it! Yes, with a little more tweaking, it should be possible to support a mixed protocol environment.

And its with a little trick from the load balancer or SSL Accelerator. Most of them that I have run into allow you to modify the request as its passing through the appliance. To the idea is to insert a Header into the HTTP request for request that have gone through the SSL Accelerator. A typical flow would look something like this.

I won’t go into the configuration on the SSL Accelerator/Load Balancer end, but lets look at enhancing the Filter to handle mixed protocols. In this example, we’ll assume the Accelerator/LB sets the header X-Original-Protocol to https for requests coming through it. The doFilter() method becomes

Voila. Now the Java Servlet machinery should be handling a mixed protocol environment just fine. This technique should work with just about any web container, be it WebLogic, JBoss, SJSWS/IWS or GlassFish.

Its arguable whether this method is more straight forward than the other previous methods, but it does have it down sides, which for me, make it less desirable. It does require a little programming, and needs actively integrated into each web app you are deploying and I don’t like having to muck with 3rd party web.xml files if I don’t need to, and for that I leave this as a last resort option.

I’m sure there are other techniques for working with SSL Accelerators, these are just the methods that worked for me so far. Hope others find them useful.

There is another big downside to the request wrapper approach — I’m pretty sure it will NOT correct the behavior of HttpServletResponse.sendRedirect. In order for the sendRedirect to determine the appropriate scheme for the absolute url when a relative url is passed in, the HttpServletResponse implementations generally have a direct reference to the original HttpServletRequest and it will have no knowledge of your request wrapper or the correction it makes to the scheme.

Wow, great article. I was thinking on a solution like this really is the way I wanted to go. Nice that you paved it for me :)

I will be using the Zen Loadbalancer for off-loading and I don’t think it will support the adding of a header. But I also use Apache HTTPD in the middle so configuration for this can be done at that tier.