Thursday, March 10, 2011

Glassfish v2 behind Apache mod_proxy with SSL and dynamic virtual hosts

This post is about how to configure Glassfish behind an Apache 2 using mod_proxy while allowing SSL-connections. The glassfish server runs several applications within their own context paths. Besides that, it has no requirements for own virtual hosts or SSL or other magic you can think of, which keeps the glassfish setup plain and simple.

As for the apache part, in my case each application should be accessed via its own domain, so a (dynamic) virtual host setup is required. Also, the applications should each be reachable via https and plain old http.

My configuration is based on this post, so some of the following parts should be equal. Also, the requirements are nearly the same:
  • Apache 2.2.x
  • SSL
  • mod_proxy
  • mod_rewrite
  • Glassfish 1.x+, 2.x or 3.x (with adjustments)
  • A multi-domain certificate for all virtual hosts. This allows us to have multiple ssl-hosts on one single IP adress. Google has more information on this topic.
  • Know how to setup your apache for ssl, since this will not be handled explicitly here. If not, again, google is your friend.
Everything in place? Here we go!

1) I recommend using a dedicated directory for the two proxy-specific configuration files we will create. I used /etc/apache2/gf/ for this task and will reference it for this tutorial.

2) In this directory create two files: hosts.map, where we will store hostname-application mappings and proxy.conf, where... you can guess this by yourself.

The first file, hosts.map, is a simple key-value map like this:
gfadmin.mydomain.de 8048/
app1.myserver.de 8080/app1
app2domain.de 8080/app2

The left column defines all hostnames we proxy forward to the glassfish-contexts (read: applications) defined in the right column. The number before is simply the port your glassfish runs on.

The first pair is to access the glassfish administration console via its own fancy hostname. This is not required but I thought nice to have.

The second file contains the real magic:
# enable rewrite engine
RewriteEngine           on

# pass the Host: line from the incoming request to the proxied host
ProxyPreserveHost       on

# define the mapping file
RewriteMap vhost txt:/etc/apache2/gf/hosts.map

# store client ip in request header
RequestHeader Set Proxy-ip %{REMOTE_ADDR}e

# add missing / for empty urls:
RewriteRule ^$ / [R,L]

# This rule fixes some browsers (safari) adding the port to the host name:
RewriteCond %{HTTP_HOST} ^([^/:]*)(:(80|443))?

# proxy-redirect with map lookup based on HTTP_HOST variable with ports removed and localhost:8080/badhost.html as default
# replace 8080/badhost.html with anything you want to throw at people accessing hosts not defined in your hosts map.
# note, that other standard apache vhosts still work so you don't need to handle them here!
RewriteRule ^/(app1/|app2/)?(.*) http://localhost:${vhost:%1|8080/badhost.html?}/$2 [P,L]

Anoyingly, you have to exclude the context paths from being forwarded to the application server as the applications always try to add it to automatically created hyperlinks. This is what the (app1/|app2/)? at the beginning of the RewriteRule is about. You can use any other regexp matching your context paths here, in this example (app\d/)? should work equally. I did not yet find a better solution for this issue. Other options are to change the links in your applications to omit the context path or to use virtual hosts also for your glassfish and give each application a root-context (/), which is what I wanted to avoid in the first place.

3) Now we only need to create a default virtual host for this machine. A default virtual host serves as catch all for requests not handled via explicit <VirtualHost> sections containing a SeverName or ServerAlias directive. As for ssl, you can only have one virtual host since the Request-URL can only be determined *after* the certificate is defined. So basically, if you only have one single IP-address and don't want to use different ports, you can only use one certificate and ssl-vhost for your server. This is also why we need a multi domain certificate for this setup.

Back to topic - You can simply discard your default virtual host configuration (000-default) and replace it with this one:
<VirtualHost *:443>
        # ssl
        SSLEngine on

        # certificates
        SSLCertificateKeyFile /etc/apache2/cert/server.key
        SSLCertificateFile /etc/apache2/cert/certificate.crt
        SSLCertificateChainFile /etc/apache2/cert/chainFile.crt

        # this i just took over from the original post. I guess it has its reason standing there...
        RequestHeader Set Proxy-keysize 512

        Include /etc/apache2/gf/proxy.conf
</VirtualHost>
<VirtualHost *:80>
        Include /etc/apache2/gf/proxy.conf
</VirtualHost>

4) Now your apache configuration is done. The last thing to be done is to adjust your glassfish http-listeners. Add the following attributes to the default listener (default is http-listener-1) and the admin-listener if you proxy to this one too:
key: authPassthroughEnabled
value: true

key: proxyHandler
value: com.sun.enterprise.web.ProxyHandlerImpl

I (and the glassfish documentation) recommend setting the listeners to listen only to localhost because we assumingly created some kind of security risk with the above settings. Simply set the field "Network Address" to 127.0.0.1. You can also disable the https listener (http-listener-2) since the apache takes care of ssl.

5) Restart or reload glassfish and apache and everything should work.

2 comments: