Revisiting SSL Termination at Apache HTTPd

So I have a blog I created a long time ago dealing w/ Liferay and SSL. The foundation of that blog post was my Fronting Liferay Tomcat with Apache HTTPd post and added terminating SSL at HTTPd and configuring the Liferay instance running under Tomcat to use HTTPS for all of the communication.

If you tear into the second post, you'll find that I was using the AJP connector to join HTTPd and Tomcat together.

This is actually a key aspect for a working setup for SSL + HTTPd + Liferay/Tomcat.

Today I was actually working on a similar setup that used the HTTP connector for SSL + HTTPd + Liferay/Tomcat. Unauthenticated traffic worked just fine, but as soon as you would try to access a secured resource that required authentication, a redirect loop resulted with HTTPd finally terminating the loop.

The only info I had was the redirect URL, https://example.com/c/portal/login?null. There was no log messages in Liferay/Tomcat and repeated 302 messages in the HTTPd logs.

My good friend and coworker Nathan Shaw told me of a case he was aware of that was similar but was from Nginx; although different web servers, the 302 redirect loop on /c/portal/login?null was an exact match.

The crux of the issue is the setting of the company.security.auth.requires.https property in portal-ext.properties.

Basically when you set this property to true, you are saying that when a user logs in, you want to force them into the secure https side. Seems pretty simple, right?

So in this configuration, when a user on http:// wants to or needs to log in, they basically end up hitting http://.../c/portal/login. This is where a check for HTTPS is done and, since the connection is not yet HTTPS will issue a redirect back to https://.../c/portal/login to complete the login.

And this, in conjunction with the HTTP connector between HTTPd and Liferay/Tomcat, is what causes the redirect loop.

Liferay responds with the 302 to try and force you to https, you submit again but SSL terminates at HTTPd and the request is sent via the HTTP connector to Liferay/Tomcat.  Well, Liferay/Tomcat sees the request came in on http:// and again issues the 302 redirect. You're now in redirect loop hell.

Fortunately, this is absolutely fixable.

Liferay has a set of portal-ext.properties settings to mitigate the SSL issue. They are:

#
# Set this to true to use the property "web.server.forward.protocol.header"
# to get the protocol. The property "web.server.protocol" must not have been
# overriden.
#
web.server.forwarded.protocol.enabled=false

#
# Set the HTTP header to use to get the protocol. The property
# "web.server.forwarded.protocol.enabled" must be set to true.
#
web.server.forwarded.protocol.header=X-Forwarded-Proto

The important property is the first one.  When that property is true, Liferay will ignore the protocol (http vs https) of the incoming request and will instead use a request header to see what the original protocol for the request actually was.

The header name can be specified using the second property, but the default one works just fine. It's also how you google for an answer for your particular web server.

I'll save you the trouble for Apache HTTPd; you just need to add a couple of lines to your <VirtualHost /> elements:

<VirtualHost *:80>
    RequestHeader set X-Forwarded-Proto "http"
    ...
</VirtualHost>

<VirtualHost *:443>
    RequestHeader set X-Forwarded-Proto "https"
    ...
</VirtualHost>

That's it.

For every incoming request getting to HTTPd, a header is added with the request protocol.  When the ProxyPass configuration forwards the requests to Liferay/Tomcat, Liferay will use the header for the check on https:// rather than the actual connection from HTTPd.

Some of you are going to be asking

Why are you using the HTTP connector to joing HTTPd to Liferay/Tomcat anyway? The AJP connector is the best connector to use in this configuration because it is better performing than the HTTP connector and avoids this and other issues that can happen by using the HTTP connector.

You would be, of course, absolutely right about that. For a simple configuration like this where you only have HTTPd <-> Liferay/Tomcat, using the HTTP connector is frowned upon.

That said, I've got another exciting blog post in the pipeline that will force moving to this configuration... I'm not getting into any details at this point, but suffice it to say that when you see the results that I've been gathering, you too will be looking at this configuration too.

Blogs
Thanks David! I thought I was the only one considering these SSL offloading issues when I implemented these configuration properties in https://issues.liferay.com/browse/LPS-45199 so everything can be configured in the proxy layer through some "de facto standard" HTTP headers. Good to know I am not the only one :-).
Yeah, I got tripped up on a blog post I'm working on where I needed to use the HTTP proxy in Apache and couldn't log into Liferay any more because of the loop. I didn't see your ticket, Juan, but it wouldn't have helped in my immediate need to get things moving again. After finding the solution, though, I wanted to share!
There is another way to configure this in tomcat itself. The connector(in tomcat server.xml) has a scheme and a secure parameter that should/can be used for this. Set them and set SSLEnabled to false.

<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" SSLEnabled="false" scheme="https" secure="true"
redirectPort="8443" URIEncoding="UTF-8" />

With these settings tomcat considers himself "secure/https" even though it isn't actually secure.
More details about secure="true" or using ajp on the communication layer on this ancient article: https://web.liferay.com/web/olaf.kock/blog/-/blogs/securing-liferay-chapter-3-port-issues-and-http-https
There is another way to configure this in tomcat itself. The connector(in tomcat server.xml) has a scheme and a secure parameter that should/can be used for this. Set them and set SSLEnabled to false.

<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" SSLEnabled="false" scheme="https" secure="true"
redirectPort="8443" URIEncoding="UTF-8" />

With these settings tomcat considers himself "secure/https" even though it isn't actually secure.