Monthly Archives: October 2013

GlassFish 4 cannot do SSL for multiple virtual hosts

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.

Move Oracle APEX on Oracle XE 11gR2 from EPG to APEX Listener

I have been playing with Oracle Application Express (APEX) to make a few applications, but I have never been bothered to switch from the default Embedded PL/SQL Gateway (EPG).  However, after becoming a little bit familiar with APEX and its different configurations, I became interested to explore other ways of deploying APEX.

The purpose of this post is to document the switch-over of my APEX deployment from EPG to APEX Listener.

The OS of the server is CentOS 6.4.  I installed Oracle Express Edition (XE) 11gR2, with which APEX 4.0 is included.  I then upgraded APEX to 4.2.3, and installed GlassFish 4 Open Source Edition on the server.

Next, configure the APEX_PUBLIC_USER user.

 SQL> alter user apex_public_user identified by password account unlock;

After that it is time to disable EPG.

SQL> exec dbms_xdb.sethttpport(0);

Next, go into the APEX unzipped directory and run the configure restful services script.

$ cd apex_install_directory
$ sqlplus / as sysdba
SQL> @apex_rest_config
Enter a password for the APEX_LISTENER user              []
Enter a password for the APEX_REST_PUBLIC_USER user              []

PL/SQL procedure successfully completed.

PL/SQL procedure successfully completed.

...create APEX_LISTENER user

PL/SQL procedure successfully completed.

User created.

...create APEX_REST_PUBLIC_USER user

User created.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Grant succeeded.

Session altered.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Synonym created.

Session altered.

PL/SQL procedure successfully completed.

SQL>

Then, grant connect privileges to the APEX_040200 user.

 DECLARE
  ACL_PATH  VARCHAR2(4000);
BEGIN
  -- Look for the ACL currently assigned to '*' and give APEX_040200
  -- the "connect" privilege if APEX_040200 does not have the privilege yet.

  SELECT ACL INTO ACL_PATH FROM DBA_NETWORK_ACLS
   WHERE HOST = '*' AND LOWER_PORT IS NULL AND UPPER_PORT IS NULL;

  IF DBMS_NETWORK_ACL_ADMIN.CHECK_PRIVILEGE(ACL_PATH, 'APEX_040200',
     'connect') IS NULL THEN
      DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(ACL_PATH,
     'APEX_040200', TRUE, 'connect');
  END IF;

EXCEPTION
  -- When no ACL has been assigned to '*'.
  WHEN NO_DATA_FOUND THEN
  DBMS_NETWORK_ACL_ADMIN.CREATE_ACL('power_users.xml',
    'ACL that lets power users to connect to everywhere',
    'APEX_040200', TRUE, 'connect');
  DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL('power_users.xml','*');
END;
/
COMMIT;

Download and unzip APEX Listener.

Configure APEX Listener.

# java -jar apex.war
This Listener instance has not yet been configured.
Please complete the following prompts

Enter the location to store configuration data:/root/apex_listener/
Oct 15, 2013 1:31:28 AM oracle.dbtools.common.config.cmds.ConfigDir execute
INFO: Set config.dir to /root/apex_listener/ in: /root/apex_listener/apex.war
Oct 15, 2013 1:31:29 AM oracle.dbtools.common.config.file.ConfigurationFolder logConfigFolder
INFO: Using configuration folder: /root/apex_listener/apex
Enter the name of the database server [localhost]:
Enter the database listen port [1521]:
Enter 1 to specify the database service name, or 2 to specify the database SID [1]:2
Enter the database SID [xe]:
Enter the database user name [APEX_PUBLIC_USER]:
Enter the database password for APEX_PUBLIC_USER:
Confirm password:
Enter 1 to enter passwords for the RESTful Services database users (APEX_LISTENER,APEX_REST_PUBLIC_USER), 2 to use the same password as used for APEX_PUBLIC_USER or, 3 to skip this step [1]:1
Enter the database password for APEX_LISTENER:
Confirm password:
Enter the database password for APEX_REST_PUBLIC_USER:
Confirm password:
Oct 15, 2013 1:32:38 AM oracle.dbtools.common.config.file.ConfigurationFiles update
INFO: Updated configurations: defaults, apex, apex_al, apex_rt
Enter 1 if you wish to start in standalone mode or 2 to exit [1]:2
#

Configure administrator for APEX Listener

# java -jar apex.war user adminlistener "Listener Administrator"
Oct 15, 2013 1:36:58 AM oracle.dbtools.common.config.file.ConfigurationFolder logConfigFolder
INFO: Using configuration folder: /root/apex_listener/apex
Enter a password for user adminlistener:
Confirm password for user adminlistener:
Oct 15, 2013 1:37:14 AM oracle.dbtools.standalone.ModifyUser execute
INFO: Created user: adminlistener in file: /root/apex_listener/apex/credentials

Create war for static images

# java -jar apex.war static /u01/app/oracle/apex/images
WAR Generation complete
 WAR location     : /root/apex_listener/i.war
 Context path     : /i
 Static resources : /u01/app/oracle/apex/images
Ensure the static resources are available at path: /u01/app/oracle/apex/images

Deploy on GlassFish.