Bringing military-grade cybersecurity solutions to the enterprise and critical systems.
Telephone: 310.356.7869

Virtual hosts in JBoss

Category: JBoss

6:24 PM, Sun, Dec 2 2007

Java web application developers should be familiar with setting up hosts in Tomcat. In Tomcat, the steps are to edit conf/server.xml. In the Connector, add an attribute like: address="192.168.1.1" to make the process bind to one particular address, if desired. Of course change the port number from 8080 to 80 (more about port numbers later). Then add your host entries, like this:

<Host name="virtual-host-example.com"
   appBase="/srv/tomcat/virtual-host-example">
  <Alias>www.virtual-host-example.com</Alias>
  <Context path="" docBase="ROOT"/>
        <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
                 directory="logs"  prefix="virtual-host-example.com_log."
                 suffix=".txt" pattern="combined" resolveHosts="false"/>
</Host>

Create the necessary docroot directory, restart the server, and you're done. You have Tomcat bound to one particular interface, and you have configured a virtual host (virtual-host-example.com) with an alias (www.virtual-host-example.com) so that users can get to the site with or without the "www". Be sure that there is a DNS A record or CNAME pointing the domain name to the address.

JBoss Application Server (AS) uses an embedded Tomcat as its web server, so virtual host configuration is similar, but not the same. All examples here will be with JBoss 4.2.2, the latest released version as of this writing.

First, changing the bind address, if necessary: JBoss looks for a system property of jboss.bind.address. This property is bound in to XML files throughout JBoss as the ${jboss.bind.address} value. See for example server/default/conf/jboss-service.xml to see how it is used. The easiest way to set the system property is to add the -b parameter when running it:

bin/run.sh -b 192.168.1.1

This is not convenient for production use.

The JBoss manual suggests the following: Edit the bin/run.conf file and add -Djboss.bind.address=192.168.1.1 to the JAVA_OPTS section, like this:

if [ "x$JAVA_OPTS" = "x" ]; then
   JAVA_OPTS="-Xms128m -Xmx512m -Dsun.rmi.dgc.client.gcInterval=3600000
-Dsun.rmi.dgc.server.gcInterval=3600000 -Djboss.bind.address=192.168.1.1"
fi
                

The JAVA_OPTS assignment should be all on one line. This file is also a good place to set the JAVA_HOME and other parameters.

However, -Djboss.bind.address=192.168.1.1 doesn't do anything in JBoss AS 4.2.2. A look at the org.jboss.Main source code shows that the processCommandLine() method always sets the value of the jboss.bind.address System property to 127.0.0.1. If -Djboss.bind.address=192.168.1.1 were put as an argument to the Java class, rather than in JAVA_OPTS for the JVM, it would work, but there is no way to add arguments to the Java class in run.conf. This is unfortunate.

Therefore, the only way to set the bind address is to modify bin/run.sh. Simply add the -b 192.168.1.1 argument where org.jboss.Main is being called.

As before, change the server port from 8080 to 80. This is done in the server/default/deploy/jboss-web.deployer/server.xml file, which is of course the Apache Tomcat server configuration file.

We also add the virtual hosts to server/default/deploy/jboss-web.deployer/server.xml, but in a way that is slightly different. This time, we will create abstract names for the hosts, in addition to their domain names. Let's give the name virthost to www.virtual-host-example.com. The server/default/deploy/jboss-web.deployer/server.xml will have the following Host entry added, on a production machine:

<Host name="virthost" autoDeploy="false"
  deployOnStartup="false" deployXML="false">
  <Alias>virtual-host-example.com</Alias>
  <Alias>www.virtual-host-example.com</Alias>

  <Valve className="org.apache.catalina.valves.AccessLogValve"
    prefix="vhost2" suffix=".log" pattern="combined"
    directory="${jboss.server.home.dir}/log"/>

  <DefaultContext cookies="true" crossContext="true" override="true"/>
</Host>
                

Note that the Alias entries could be different on a development machine. You could add entries like testapp1 to the /etc/hosts file to create a test environment. This is why we're using the abstract name virthost instead of a real DNS name for the host entry.

Now this virtual host must be bound in the web application. There's nowhere to do that in the standard web.xml. JBoss has its own file, web.xml, which goes in WEB-INF just like web.xml. This what our app's jboss-web.xml looks like:

<jboss-web>
    <context-root>/</context-root>
    <virtual-host>virthost</virtual-host>
</jboss-web>

We are using the symbolic name virthost. That way, the development JBoss and the production JBoss can bind the application to different DNS names. Very practical.

Also, in the Host block in the JBoss server.xml file, I changed the log format from common to combined. Combined log format records the user agent string, something which is very valuable, especially for servers with mobile clients.

Now we have seen the differences between virtual host configuration in Tomcat vs. in JBoss:

  • In both cases, a Host block is added to server.xml.
  • In Tomcat, the bind address is specified in the server.xml file.
  • In JBoss, the bind address is specified on the command line, or by editing run.sh.
  • In Tomcat, no symbolic name for the host is needed; just use the DNS name.
  • In JBoss, the Host section has a symbolic name, and the DNS names are stored as aliases.
  • In a JBoss application, the vendor-specific WEB-INF/jboss-web.xml file is used to bind an application to a virtual host name, and context path, just like it can be used to bind other resources, such as JNDI resources.

That's a guide to setting up hosts in JBoss. In a future guide, we'll look at setting up SSL certificates in JBoss and Tomcat.

Now we come to the question of port numbers. A very old security flaw in Unix, and then in Linux, is the restriction that only root processes can bind to ports less than 1024, so-called "privileged" ports. This single design flaw has resulted in more security problems in Unix and Linux than any other flaw in those systems. Unfortunately, unthinking system administrators cling to the belief that if ordinary users were able to bind to "privileged" ports, something bad would happen. This is naive, and exactly opposite of reality. We should have processes with the least access binding to externally-accessible ports on the machine. In fact, root processes shouldn't be able to bind to these ports at all. Unfortunately, it is unlikely that this flaw will be fixed until Linux moves away from its obsolete user permissions model of security, and moves to a fine-grained capabilities model.

What this means is, to bind JBoss to port 80, JBoss must be run as root. This is usually ok. Unlike with network servers written in C, such as Apache HTTPD, there is practically no way to do a buffer overflow followed by remote code execution in JBoss. The JVM will never execute instructions in a user array, and nothing running in the JVM can access memory.

However, it is still not desirable to have these processes running as root. What if you need to host an application that you don't know too much about? That application will run with permission to access any file on your server. That's not desirable.

The solution is to use Linux' advanced firewall capabilities to map port 80 to a higher (non-privileged) port. I'll show how to do that in the next blog entry.