I have been trying to get SSL working on GlassFish, when I realised that unlike Apache (where the SSL configuration can be defined in the virtual host block), the SSL settings for GlassFish is attached to the network listener, not the virtual host.
In the case of a default GlassFish OSE 4.0 installation, the network listener http-listener-2 is the SSL listener, and only one certificate can be associated with the listener at any one time. The default certificate given to http-listener-2 is a self-signed certificate given the alias s1as. Changing this to a CA-signed certificate is a matter of modifying the keystore file using the Java keytool program, and changing the certificate alias used by the network listener (via the SSL tab on the network listener configuration page).
I wanted to host multiple virtual hosts, each using a different SSL certificate as they have different domains, on GlassFish. I wondered why one listener can only use one SSL certificate, because on Apache each virtual host block can house its own SSL settings and hosting multiple SSL sites is definitely possible (I tried).
So I began scouring the documentation and found this in the GlassFish 4.0 Security Guide:
Name-based Virtual Hosts Using name-based virtual hosts for a secure application can be problematic. This is a design limitation of the SSL protocol itself. The SSL handshake, where the client browser accepts the server certificate, must occur before the HTTP request is accessed. As a result, the request information containing the virtual host name cannot be determined prior to authentication, and it is therefore not possible to assign multiple certificates to a single IP address.
If all virtual hosts on a single IP address need to authenticate against the same certificate, the addition of multiple virtual hosts probably will not interfere with normal SSL operations on the server. Be aware, however, that most browsers will compare the server’s domain name against the domain name listed in the certificate, if any (applicable primarily to official, CA-signed certificates). If the domain names do not match, these browsers display a warning. In general, only address-based virtual hosts are commonly used with SSL in a production environment.
Okay, so it seems like GlassFish is blaming the SSL protocol for not being able to use different certificates for different virtual hosts. The explanation makes sense though; the server will need to know from the client the name of the virtual host the client is connecting to, but this exchange will also have to be encrypted using SSL. But which SSL certificate should the server use for this initial exchange? The problem is understandable.
However, if the problem is the SSL protocol, shouldn’t all web servers behave the same way? If it is impossible on GlassFish, how come it is possible on Apache?
I did some more digging, and stumbled on an Apache wiki page which stated:
As a rule, it is impossible to host more than one SSL virtual host on the same IP address and port. This is because Apache needs to know the name of the host in order to choose the correct certificate to setup the encryption layer. But the name of the host being requested is contained only in the HTTP request headers, which are part of the encrypted content. It is therefore not available until after the encryption is already negotiated. This means that the correct certificate cannot be selected, and clients will receive certificate mismatch warnings and be vulnerable to man-in-the-middle attacks.
I find it quite weird that the Apache documentation is agreeing in saying that it is impossible because I am sure it worked when I tried it. This very blog is running on a multiple-certificate SSL server!
Then I clicked on the link on that wiki page that leads to another wiki page, which has the answer: Server Name Indication (SNI).
Apparently, SNI is an extension to the SSL protocol that was introduced in 2006 which allowed virtual hosts to use different SSL certificates and the Apache version that I use has already included SNI inside its SSL implementation.
Before SNI, one IP address and port combination can only use one SSL certificate. The introduction of SNI lifted this limitation of the SSL protocol. So let’s get back to GlassFish. Does it support SNI? Based on the documentation, no. Why doesn’t it support SNI? Let’s find out.
Googling GlassFish SNI brought me to an interesting post by Ricardo Martín Camarero. His encounter with SNI is the opposite of mine; he knew it was not supposed to be possible until someone told him otherwise. He made me realise through his post that since GlassFish runs on Java, SNI support will be dependent on the Java VM.
As of the current version of Java, which is version 7, SNI implementation is only supported for clients i.e. Java clients will be able to connect to servers utilizing SNI with no certificate errors.
SNI support for servers will be added to Java 8. As such, we might have to wait until the next version of GlassFish to use SNI.
4 thoughts on “GlassFish 4 cannot do SSL for multiple virtual hosts”
This is quite an interesting topic and you summed it up quite well.
On GF3 each network listener can have one SSL cert, but if no SNI support that would requiere an additional IP per listener. GF4, if I am not mistaken, was released around September 2013 and may not have built-in support for SNI either then.
But, http://docs.oracle.com/javase/7/docs/technotes/guides/security/enhancements-7.html, JSSE supports SNI. If so, this rather raise the question of why would not it being used in GF. There is a discussion at http://stackoverflow.com/questions/12361090/server-name-indication-sni-on-java whith links to various possibilities for using SNI.
As for the JDK, http://openjdk.java.net/projects/jdk8/milestones page list SNI in M5. https://jdk8.java.net release candidate is Java 8 build 129 so I assume SNI is included by now.
SNI issue is not that new, so I guess either GF supports it already or one is suposed to add/use a Java extension/binaries. At least until a GF version that includes SNI then.
Something also is it may be possible to install a front Apache to handle the HTTPS and then forward to GF. This is just an idea, I would have to test and confirm.
You’ve got a nice blog, I will likely browse around.
Thank you for your comment and kind words.
You are right about the Apache workaround. That is what I am using right now, I let Apache handle the SSL (with SNI) and use mod_proxy to forward to GlassFish.
The SNI support in Java 7 as I understand it is only for client connections i.e. Java applications can connect to SSL servers using SNI with no errors. But for server support i.e. Java applications being the SNI servers, that will be in Java 8, and I assume will be included in the next version of GlassFish.
Thanks again for dropping by!
We are now May 2016 and GF V4.1.1 still does not seem to have a mechanism to support multiple SSL enabled virtual hosts. This despite GF web site recommending to use Java 8.
I just wrote a quick doc how to set up Apache in front of GF for this purpose, http://www.e2cserver.com/glassfish-apache-front.html , however I see you mention using mod_proxy while I use mod_jk.
Hope all is well for you.
There is no need to put a reverse proxy in front of Glassfish. I managed to get it work with GF5 Web Profile (not tested with GF4), JDK 8_151 and Letsencrypt.
Assume you have three apps aaa.war , bbb.war and ccc.war and want them to be called on same host IP with aaa.com , bbb.com and ccc.com.
– deploy the three war’s to the default ‘server’ Make aaa.war the default app for ‘server’ : use context path /aaa (for example)
– now you can call the aaa app with aaa.com , the bbb app with aaa.com/bbb and the ccc app with aaa.com/ccc
– create two new virtual servers. Let them use the default listeners listener-1 and listener-2
– set Host bbb.com to first virtual server and host ccc.com to the second
– set bbb.war as default app for the first virtual server and ccc.war for the second (do not deploy them to the virtual servers) Set context paths /bbb and /ccc respectively
– before using Letsencrypt , stop Glassfish and any server (if a frontend is installed) , which runs on port 80
– start Letsencrypt with “certbot certonly” (certbot must be already installed)
– choose standalone
– when asked for domain name, put in all three domains , space or comma separated (e.g aaa.com bbb.com ccc.com)
– at this point, if you already have a certificate for aaa.com , you will be asked if you wish to expand it with the new domains. Choose yes
– after creation, import the new certificate into Glassfish keystore (replace the old one, which covers aaa.com – only, if it exists) Check if listener-2 points to the new alias
Now you have a SSL certificate, which covers all your domains at once and can remove the frontend server (if any). After starting Glassfish (it must run on port 80 / 443 or they must be redirected to 8080 / 8181 wit IPTables ) , all domains have a https connection and resolve to aaa , bbb or ccc apps respectively