Showing posts with label Tomcat. Show all posts
Showing posts with label Tomcat. Show all posts

Thursday, February 16, 2012

Configuring two-way SSL authentication on Tomcat Configuring two-way SSL authentication on Tomcat


Using two-way SSL authentication it is not only the client verifying the server's identity, but the server also authenticates the client. In this article we will configure Tomcat to use two-way authentication by executing the following steps:
  • Generate a self-signed server-certificate on the server
    • Download the server-certificate to the client
    • Import the server-certificate to the client's Java-keystore
    • Generate a self-signed client-certificate on the client
    • Upload it to the server
      • Import the client-certificate to the server's Java-keystore
      • Configure Tomcat to use this keystore, and configure to require two-way authentication.
      Both the server and the client will use self-signed certificates in our example; the purpose is only the mutual authentication and the encryption over the wire. This way we can secure Tomcat's Manager application, and we can even configure Maven to deploy to this server at the end of the build process automatically.
      Let's get started!

      On the server: generate the self-signed server-certificate

      Let's summarize the steps we will take:
      •   Delete the possibly existing key-store from previous runs (reproducibility is important ;-))
      •   Generate a certificate
      •   Export it
      •   Move it to an appropriate location where it can be downloaded
      The script below accomplishes all the tasks above. Don't forget to update the SERVER and PASSWORD environment variables at the beginning.
      cd ~
      SERVER=ec2-79-125-40-221.eu-west-1.compute.amazonaws.com
      PASSWORD=password
      
      sudo rm -f /usr/share/tomcat6/server.keystore
      sudo rm -f /usr/share/tomcat6/client.cer
      sudo rm -f /home/ec2-user/client.cer
       
      sudo su -c "keytool -genkeypair -alias serverkey -keyalg RSA -dname \"CN=$SERVER,OU=Techsoft Centre,O=Sony,L=Zaventem,ST=Vlaams-Brabant,C=BE\" -keystore /usr/share/tomcat6/server.keystore -keypass $PASSWORD -storepass $PASSWORD" tomcat
      
      sudo su -c "keytool -exportcert -alias serverkey -file /usr/share/tomcat6/server.cer -keystore /usr/share/tomcat6/server.keystore -storepass $PASSWORD" tomcat
      
      sudo mv /usr/share/tomcat6/server.cer /home/ec2-user
      
      To check the contents of the keystore, you can use
      sudo su -c "keytool -list -keystore /usr/share/tomcat6/server.keystore -storepass $PASSWORD" tomcat
      The output should be similar to this:
      Keystore type: JKS
      Keystore provider: SUN
      
      Your keystore contains 1 entry
      
      serverkey, 19-Dec-2010, PrivateKeyEntry, 
      Certificate fingerprint (MD5): B5:B5:B1:BA:B3:B0:BE:B2:B7:BF:B9:B9:B3:B6:BB:B2

      On Windows client: generate a certificate for the server

      Let's continue our journey on the client. The steps to take:
      1.   Download the server’s public key generated in the preceding step
      2.   Install it
      3.   Generate a key-pair (this will authenticate the client) for the server
      4.   Export the public key to a certificate
      5.   Upload it
      6.   Export the client's private key to a file
      7.   Upload to the server since we have OpenSSL there to process the key
      Steps 6 and 7 may require some more explanation. Since the certificate generated by keytool will be used by browsers to connect to Tomcat, it has to be exported in a way browsers can understand it. The OpenSSL tool can help us to accomplish this goal (it can generate .P12 files), but unfortunately it is not installed on Windows by default. But we have it on the Amazon Linux image; to avoid the hassle of downloading and installing OpenSSL on Windows, we can upload the private key to the server to process it with OpenSSL. But before uploading, we have to acquire it somehow from the java keystore - an official procedure only exists to export the public key part, but not the private key.
      To export the private key, we'll need a small java application, DumpPrivateKey.java:
      public class DumpPrivateKey {
        public static void main(String[] args) throws Exception {
        final String keystoreName = args[0];
          final String keystorePassword = args[1];
          final String alias = args[2];
          java.security.KeyStore ks = java.security.KeyStore.getInstance("jks");
          ks.load(new java.io.FileInputStream(keystoreName), keystorePassword.toCharArray());
          System.out.println("-----BEGIN PRIVATE KEY-----");
          System.out.println(new sun.misc.BASE64Encoder().encode(ks.getKey(alias, keystorePassword.toCharArray()).getEncoded()));
          System.out.println("-----END PRIVATE KEY-----");
        }
      }
      
      A compilation step will be also be needed with javac so that we can execute the code. Then we'll upload the exported private-key to the server for processing.
      The script to execute all the steps (including generating and compiling the small java-code above) above:
      set SERVER=ec2-79-125-40-221.eu-west-1.compute.amazonaws.com
      set PASSWORD=password
      
      if exist client.keystore ( del client.keystore )
      if exist server.cer ( del server.cer )
      if exist client.cer ( del client.cer )
      
      "C:\Program Files (x86)\PuTTY\pscp.exe" ec2-user@%SERVER%:server.cer .
      
      keytool -importcert -alias serverkey -noprompt -file server.cer -keystore client.keystore -storepass %PASSWORD%
      
      keytool -genkeypair -alias clientkey -keyalg RSA -dname "CN=philatelistclient,OU=Techsoft Centre,O=Sony,L=Zaventem,ST=Vlaams-Brabant,C=BE" -keypass %PASSWORD% -keystore client.keystore -storepass %PASSWORD%
      
      keytool -list -keystore client.keystore -storepass %PASSWORD%
      
      keytool -rfc -exportcert -alias clientkey -file client.cer -keypass %PASSWORD% -keystore client.keystore -storepass %PASSWORD%
      
      "C:\Program Files (x86)\PuTTY\pscp.exe" client.cer ec2-user@%SERVER%:
      
      echo public class DumpPrivateKey { > DumpPrivateKey.java
      echo   public static void main(String[] args) throws Exception { >> DumpPrivateKey.java
      echo   final String keystoreName = args[0]; >> DumpPrivateKey.java
      echo     final String keystorePassword = args[1]; >> DumpPrivateKey.java
      echo     final String alias = args[2]; >> DumpPrivateKey.java
      echo     java.security.KeyStore ks = java.security.KeyStore.getInstance("jks"); >> DumpPrivateKey.java
      echo     ks.load(new java.io.FileInputStream(keystoreName), keystorePassword.toCharArray()); >> DumpPrivateKey.java
      echo     System.out.println("-----BEGIN PRIVATE KEY-----"); >> DumpPrivateKey.java
      echo     System.out.println(new sun.misc.BASE64Encoder().encode(ks.getKey(alias, keystorePassword.toCharArray()).getEncoded())); >> DumpPrivateKey.java
      echo     System.out.println("-----END PRIVATE KEY-----"); >> DumpPrivateKey.java
      echo   } >> DumpPrivateKey.java
      echo } >> DumpPrivateKey.java
      
      "%JAVA_HOME%\bin\javac" -classpath . DumpPrivateKey.java
      
      "%JAVA_HOME%\bin\java" -classpath . DumpPrivateKey client.keystore %PASSWORD% clientkey > clientkey.pkcs8
      
      "C:\Program Files (x86)\PuTTY\pscp.exe" clientkey.pkcs8 ec2-user@%SERVER%:
      
      del client.cer
      del clientkey.pkcs8
      del DumpPrivateKey.class
      del DumpPrivateKey.java
      del server.cer
      
      
      Windows does not have scp on its own, so we are using PuTTY's pscp here to do the secure copy. If cygwin is installed (and its bin folder is in the PATH), the "C:\Program Files (x86)\PuTTY\pscp.exe" can be replaced simply by scp.

      On OSX/Linux client:

      If you happen to use Mac OSX or Linux on the client, the following script is the bash-equivalent of the one above:
      SERVER=ec2-79-125-40-221.eu-west-1.compute.amazonaws.com
      PASSWORD=password
      rm -f client.keystore
      rm -f server.cer
      rm -f client.cer
      scp ec2-user@$SERVER:server.cer .
      keytool -importcert -alias serverkey -noprompt -file server.cer -keystore client.keystore -storepass $PASSWORD
      keytool -genkeypair -alias clientkey -keyalg RSA -dname "CN=client,OU=Java Notes,O=Java Notes,L=Zaventem,ST=Vlaams-Brabant,C=BE" -keypass $PASSWORD -keystore client.keystore -storepass $PASSWORD keytool -list -keystore client.keystore -storepass $PASSWORD
      keytool -exportcert -rfc -alias clientkey -file client.cer -keypass $PASSWORD -keystore client.keystore -storepass $PASSWORD
      scp client.cer ec2-user@$SERVER:
      rm -f DumpPrivateKey.java
      echo 'public class DumpPrivateKey {' >> DumpPrivateKey.java
      echo '  public static void main(String[] args) throws Exception {' >> DumpPrivateKey.java
      echo '  final String keystoreName = args[0];' >> DumpPrivateKey.java
      echo '    final String keystorePassword = args[1];' >> DumpPrivateKey.java
      echo '    final String alias = args[2];' >> DumpPrivateKey.java
      echo '    java.security.KeyStore ks = java.security.KeyStore.getInstance("jks");' >> DumpPrivateKey.java
      echo '    ks.load(new java.io.FileInputStream(keystoreName), keystorePassword.toCharArray());' >> DumpPrivateKey.java
      echo '    System.out.println("-----BEGIN PRIVATE KEY-----");' >> DumpPrivateKey.java
      echo '    System.out.println(new sun.misc.BASE64Encoder().encode(ks.getKey(alias, keystorePassword.toCharArray()).getEncoded()));' >> DumpPrivateKey.java
      echo '    System.out.println("-----END PRIVATE KEY-----");' >> DumpPrivateKey.java
      echo '  }' >> DumpPrivateKey.java
      echo '}' >> DumpPrivateKey.java
      javac DumpPrivateKey.java
      java DumpPrivateKey client.keystore $PASSWORD clientkey > clientkey.pkcs8
      scp clientkey.pkcs8 ec2-user@$SERVER:

      On the server: import the client's certificate

      Let's continue on the server with the following steps:
      • Generate the PKCS12 certificate for the browser from the private key uploaded by the client in the preceding step
      • Fix permissions & move the client’s certificate to a place where the tomcat user can reach it easily
      • Delete the possibly existing client-key from the server's keystore
      • Install the client’s certificate to the server's java keystore
      • Clean up: remove the temporary files used during the process.
      The script:
      openssl pkcs12 -export -in client.cer -inkey clientkey.pkcs8 -password pass:$PASSWORD -out client.p12
      sudo chown tomcat:tomcat ~/client.cer
      sudo mv ~/client.cer /usr/share/tomcat6
      sudo su -c "keytool -delete -alias clientkey -keystore /usr/share/tomcat6/server.keystore -storepass $PASSWORD -noprompt" tomcat
      sudo su -c "keytool -importcert -alias clientkey -file /usr/share/tomcat6/client.cer -keystore /usr/share/tomcat6/server.keystore -storepass $PASSWORD -noprompt" tomcat
      sudo rm /usr/share/tomcat6/client.cer
      
      Since we were using the tomcat user to execute, if we wouldn’t have specified a keystore path, it would have been generated in tomcat's home folder, /usr/share/tomcat.

      On the client: import the certificate to the browser

      Back to the client, we’ll have to import the PKCS12 file client.p12 into the browser so that it can authenticate itself to Tomcat. To download the file, you can use
      scp 
       ec2-user@java-notes.com:client.p12 .
      Start Firefox and import the generated PKCS12 file as a Personal key via Options->Advanced->Encryption->View Certificates->Your Certificates->Import... To use Chrome or Internet Explorer, just right click the .P12 file and install the certificate to Windows' own certificate store.
      Don't lose this .P12 file, and don't disclose it: this is your key to your server, after the next step Tomcat will not allow HTTPS connections from browsers where this key is not imported into the certificate store.

      On the server: configure Tomcat's HTTPS connector

      Now we have insert the following line in server.xml, inside the section :
      From a script, this can be done with the help of sed:
      sudo sed -i 's//\n    \n/' /usr/share/tomcat6/conf/server.xml
      Please note: since we didn't specify a path for the keystore-file, it will be loaded from tomcat user's home directory, /usr/share/tomcat6. If on your server tomcat complains at startup not being able to reach this file, please specify an absolute path.
      To require HTTPS (and thus our .P12 file to be installed) for clients, the web-applications' web.xml file has to be updated by including the following XML-snippet:
      
        CONFIDENTIAL
      
      In our next article, we'll update Tomcat's Manager application to mandate the client to have our key for administration/deployment.

      Wednesday, February 15, 2012

      Running Tomcat on port 80 on Linux


      By default Tomcat's HTTP connector listens on port 8080. Changing to port 80 in Linux environment can be quite a tricky issue, since by default listening on any port under 1024 require a privileged user, and for security considerations it is not recommended to run Tomcat with elevated permissions. This article discusses how to use authbind to achieve this; it also describes the way all this configuration can be automated for the sake of the creation of a script which can be used to initialize a freshly installed Linux instance. This is especially advantageous on Amazon EC2, where we can use this init-script to initialize a fresh instance just launched from an AMI; and indeed, for the sake of this article Amazon's "Amazon Linux Image 1.0" was used for testing. Please note that this is a CentOS 5-based linux distribution, for other distributions there are slight changes, like replacing "sudo yum install tomcat6" with "sudo apt-get install tomcat6" on Debian-based systems like Ubuntu.
      In the end of the article, all the commands are summarized to facilitate one-step configuration.

      Installing Tomcat

      We’ll need the tomcat6 package to run Tomcat’s core components, as well as the tomcat6-admin-webapps package since we’ll use Tomcat’s Manager Application for application deployments, either thru Maven’s Cargo component or thru the web-browser. Since we’ll compile the authbind application from its sources, we’ll also need gcc, the GNU C Compiler package which contains all components to build an application on Linux. To install all this, grab a terminal and execute:
      sudo yum -y install tomcat6 tomcat6-admin-webapps gcc
      Usually a web server is started automatically on system boot. This can be achieved by
      sudo /sbin/chkconfig --levels 235 tomcat6 on

      Listening on ports<1024 in Linux with an unprivileged user

      There are more options to achieve this:
      -    By using authbind which authorizes specific users to specific ports under 1024
      -    By using Jsvc, a set of libraries and applications for making Java applications run on UNIX more easily (Jsvc allows Tomcat application to perform some privileged operations as root (e.g. bind to a port < 1024), and then switch identity to a non-privileged user.)
      -    By configuring iptables to re-route the packets from port 80 to 8080
      This article describes the authbind approach. But first, let's tell Tomcat to listen on port 80 instead of 8080.

      Changing Tomcat's default HTTP port

      The default HTTP port is defined in /etc/tomcat6/server.xml:
      We need to change this default port to 80 in server.xml. Either replace by hand, or automatically: to replace the occurrences of port=”8080” to port=”80”, execute the following script:
      sudo sed -i 's/port\=\"8080\"/port\=\"80\"/' /etc/tomcat6/server.xml
      The same for port 8443, which will be replaced with port 443:
      sudo sed -i 's/port\=\"8443\"/port\=\"443\"/' /etc/tomcat6/server.xml
      We'll start Tomcat with authbind. This can be achieved by changing Tomcat's init-script in /etc/init.d, replacing the line
      TOMCAT_SCRIPT="/usr/sbin/tomcat6"
      with
      TOMCAT_SCRIPT="exec authbind --deep /usr/sbin/tomcat6"
      Again, it can be automated like this:
      sudo sed -i  's/TOMCAT_SCRIPT=\"\/usr\/sbin\/tomcat6\"/TOMCAT_SCRIPT=\"exec authbind  --deep \/usr\/sbin\/tomcat6\"/' /etc/init.d/tomcat6
      We have to tell Tomcat to use the IPv4 stack by default. This can be done by appending the line CATALINA_OPTS="-Djava.net.preferIPv4Stack=true" to /etc/tomcat6/tomcat6.conf:
      sudo sed -i '$ a\CATALINA_OPTS=\"-Djava\.net\.preferIPv4Stack=true\"\n' /etc/tomcat6/tomcat6.conf

      Installing and configuring authbind

      Authbind is installed the usual way, with the help of gcc and make. Please note: For this step to succeed, the gcc package is needed. It is already installed with the command sudo yum install gcc earlier, when tomcat was installed.
      cd ~
      wget http://www.chiark.greenend.org.uk/ucgi/~ijackson/cvsweb/authbind/authbind.tar.gz?tarball=1 -O authbind.tar
      tar xf authbind.tar
      cd authbind
      make
      sudo make install
      Authbind is configured with some special files, for which we can assign our arbitrary permissions for the users we want to give access to. Since Tomcat is running with the Tomcat user, we'll tell authbind to allow connections to the HTTP port 80 and the HTTPS port 443 for this account:
      sudo touch /etc/authbind/byport/80
      sudo chmod 500 /etc/authbind/byport/80
      sudo chown tomcat /etc/authbind/byport/80
      sudo touch /etc/authbind/byport/443
      sudo chmod 500 /etc/authbind/byport/443
      sudo chown tomcat /etc/authbind/byport/443
      For the changes to take effect, Tomcat has to be restarted:
      sudo /etc/init.d/tomcat6 restart
      To see if there is any error, the tomcat log can be consulted:
      less -S /var/log/tomcat6/catalina.out

      The whole script

      Here is the whole script which automates all this:
      sudo yum -y install tomcat6 tomcat6-admin-webapps gcc 
      sudo sed -i 's/port\=\"8080\"/port\=\"80\"/' /etc/tomcat6/server.xml
      sudo sed -i 's/port\=\"8443\"/port\=\"443\"/' /etc/tomcat6/server.xml
      sudo sed -i 's/TOMCAT_SCRIPT=\"\/usr\/sbin\/tomcat6\"/TOMCAT_SCRIPT=\"exec authbind --deep \/usr\/sbin\/tomcat6\"/' /etc/init.d/tomcat6
      sudo sed -i '$ a\CATALINA_OPTS=\"-Djava\.net\.preferIPv4Stack=true\"\n' /etc/tomcat6/tomcat6.conf
      cd ~  
      wget http://www.chiark.greenend.org.uk/ucgi/~ijackson/cvsweb/authbind/authbind.tar.gz?tarball=1 -O authbind.tar
      tar xf authbind.tar
      cd authbind
      make
      sudo make install 
      sudo touch /etc/authbind/byport/80
      sudo chmod 500 /etc/authbind/byport/80
      sudo chown tomcat /etc/authbind/byport/80
      sudo touch /etc/authbind/byport/443
      sudo chmod 500 /etc/authbind/byport/443
      sudo chown tomcat /etc/authbind/byport/443
      sudo /sbin/chkconfig --levels 235 tomcat6 on
      sudo /etc/init.d/tomcat6 restart
      cd ~

      Monday, February 13, 2012

      Installing Apache Tomcat on Linux


      Introduction

      This article discusses how to install Apache Tomcat 6.0 (6.0.18) on 64-bit Debian Linux 4.0. Additionally it shows how to setup multiple Tomcat JVM instances on a single Linux server. For each Tomcat JVM instance a web application and Java servlet example is configured. The Tomcat installation steps outlined in this article are also applicable to most other Linux distributions. 

      Note that this document comes without warranty of any kind. But every effort has been made to provide the information as accurate as possible. I welcome emails from any readers with comments, suggestions, and corrections at webmaster_at_puschitz.com. 

      Installing Java Runtime Environment

      To run Tomcat, you need Java Standard Edition (Java SE), also known as the JDK. 

      For the Tomcat installation I used SUN's latest Java SE JDK that was available at the time of this writing: Java SE Development Kit (JDK) 6 Update 10 (6u10). Regarding Java SE 6, Platform Name and Version Numbers, seehttp://java.sun.com/javase/6/webnotes/version-6.html. And for the whole Java version history I recommend the Wiki article http://en.wikipedia.org/wiki/Java_version_history

      You can download SUN's latest Java JDKs at: http://java.sun.com/javase/downloads/index.jsp

      For my 64-bit Debian system I selected the 64-bit JDK multiplatform binary for Linux: jdk-6u10-linux-x64.bin
      I downloaded the binary file to /tmp and installed it as follows as root:
      # mkdir -p /usr/java
      # cd /usr/java
      #
      # chmod 700 /tmp/jdk-6u10-linux-x64.bin
      # /tmp/jdk-6u10-linux-x64.bin
      ...
         creating: jdk1.6.0_10/
         creating: jdk1.6.0_10/db/
         creating: jdk1.6.0_10/db/bin/
        inflating: jdk1.6.0_10/db/bin/ij   
        inflating: jdk1.6.0_10/db/bin/NetworkServerControl  
        inflating: jdk1.6.0_10/db/bin/setNetworkClientCP.bat  
        inflating: jdk1.6.0_10/db/bin/derby_common.sh  
      ...
      Done.
      # export JAVA_HOME=/usr/java/jdk1.6.0_10
      # export PATH=$JAVA_HOME/bin:$PATH
      #
      # which java
      /usr/java/jdk1.6.0_10/bin/java
      # java -version
      java version "1.6.0_10"
      Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
      Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode)
      #

      Installing Tomcat Software

      Download the latest Tomcat 6.x version from http://tomcat.apache.org/download-60.cgi. For Debian I downloaded the Binary Core Distribution file apache-tomcat-6.0.18.tar.gz which was the latest version at the time of this writing. 

      Once you downloaded the tar file make sure the MD5 checksum matches the value posted on Tomcat's web site, see http://www.apache.org/dist/tomcat/tomcat-6/v6.0.18/bin/apache-tomcat-6.0.18.tar.gz.md5:
      # md5sum /tmp/apache-tomcat-6.0.18.tar.gz
      8354e156f097158f8d7b699078fd39c1  /tmp/apache-tomcat-6.0.18.tar.gz
      
      
      Installing Tomcat from a binary release (tar file) requires manual creation of the Tomcat user account. This is not necessary if you install the Tomcat RPM package on a Linux system that supports RPMs. 

      For security reasons I created a user account with no login shell for running the Tomcat server:
      # groupadd tomcat
      # useradd -g tomcat -s /usr/sbin/nologin -m -d /home/tomcat tomcat
      
      (It should be noted that other Linux systems have nologin under /sbin not /usr/sbin

      Next I extracted the tar file to /var/lib and changed the ownership of all files and directories to tomcat:
      # cd /var/lib
      # tar zxvf /tmp/apache-tomcat-6.0.18.tar.gz
      # chown -R tomcat.tomcat /var/lib/apache-tomcat-6.0.18
      
      
      The get the Tomcat version of the newly installed Tomcat, run:
      # /var/lib/apache-tomcat-6.0.18/bin/version.sh
      Using CATALINA_BASE:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_HOME:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_TMPDIR: /var/lib/apache-tomcat-6.0.18/temp
      Using JRE_HOME:       /usr
      Server version: Apache Tomcat/6.0.18
      Server built:   Jul 22 2008 02:00:36
      Server number:  6.0.18.0
      OS Name:        Linux
      OS Version:     2.6.18-6-amd64
      Architecture:   x86_64
      JVM Version:    1.4.2
      JVM Vendor:     Free Software Foundation, Inc.
      #
      
      
      Starting/Stopping Tomcat

      Now try to startup the Tomcat server to see whether the default Tomcat home page is being displayed. 

      For security reasons I don't run the Tomcat server as user root but as tomcat which was created with no login shell. Therefore, to run Tomcat use the su command with the -p option to preserves all the environment variables when switching to tomcat (more on the Tomcat environment variables later). And since the tomcat account has no login shell, it needs to be specified with the -s option. (You may want to use this su command if you plan on writing and implementing a system startup and shutdown script for system reboots.)
      # export JAVA_HOME=/usr/java/jdk1.6.0_10
      # export PATH=$JAVA_HOME/bin:$PATH
      # export CATALINA_HOME=/var/lib/apache-tomcat-6.0.18
      # export CATALINA_BASE=/var/lib/apache-tomcat-6.0.18
      #
      
      # su -p -s /bin/sh tomcat $CATALINA_HOME/bin/startup.sh
      Using CATALINA_BASE:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_HOME:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_TMPDIR: /var/lib/apache-tomcat-6.0.18/temp
      Using JRE_HOME:       /usr/java/jdk1.6.0_10
      #
      
      
      Now verify that Tomcat was started successfully by opening the URL http://localhost:8080 (Port number 8080 is the default port used by Tomcat). Note that you should also be able to use the name of your server instead oflocalhost. Once you opened the URL in your browser you should see Tomcat's Congratulation page. If you don't see the page, check the log files under $CATALINA_HOME/logs (/var/lib/apache-tomcat-6.0.18/logs). 

      Before you continue with the next steps, make sure to shut down Tomcat since we want to run the Tomcat server out of a separate application directory which is covered in the next chapter.
      # su -p -s /bin/sh tomcat $CATALINA_HOME/bin/shutdown.sh
      Using CATALINA_BASE:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_HOME:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_TMPDIR: /var/lib/apache-tomcat-6.0.18/temp
      Using JRE_HOME:       /usr/java/jdk1.6.0_10
      #
      
      
      Switching to Tomcat User Account

      Most of the next steps in this article assume that you switched to the tomcat user account. If you see a '$' prompt, then the steps in this article are executed as the tomcat user. If you see a '#' prompt, then the steps are executed asroot

      Since for security reasons the tomcat user has no login shell, it needs to be specified with the -s option when switching from root to tomcat:
      # su - -s /bin/sh tomcat
      $ id
      uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
      $
      
      Note that non-root users cannot switch to the tomcat account. 

      Setting Up First Tomcat JVM Instance

      It is recommended not to store the web applications's files in Tomcat's distribution directory tree. For example, having a separate directory makes Tomcat upgrades easier since it won't overwrite configuration files like server.xml. And since this tutorial shows how to run two Tomcat instances concurrently on a single Linux server, two separate directories are needed anyway. It should be noted here that it's also possible to run multiple web applications per Tomcat JVM instance. This HOWTO shows the creation and configuration of one web application for each Tomcat instance. 

      Setting up Directories and Files

      In the following example I setup the first Tomcat JVM instance under the base directory /opt/tomcat-instance/sales.example.com. It's a good practice to name the base directory after the site name, in this examplesales.example.com

      Creating a new base directory for a new instance requires the creation and copying of various directories and configuration files. Execute the following commands as root:
      # mkdir -p /opt/tomcat-instance/sales.example.com
      # cd /opt/tomcat-instance/sales.example.com
      #
      # cp -a /var/lib/apache-tomcat-6.0.18/conf .
      # mkdir common logs temp server shared webapps work
      #
      # chown -R tomcat.tomcat /opt/tomcat-instance
      
      
      Most of the remaining steps are executed as the tomcat user. So make sure you switch from root to tomcat:
      # su - -s /bin/sh tomcat
      $ id
      uid=1001(tomcat) gid=1001(tomcat) groups=1001(tomcat)
      $
      
      
      Next I created an environment file for the new Tomcat instance. This will be useful for easily setting the environment variables when starting/stopping the new Tomcat instance:
      $ cat > /opt/tomcat-instance/sales.env << EOF
      export JAVA_HOME=/usr/java/jdk1.6.0_10
      export PATH=\$JAVA_HOME/bin:\$PATH
      export CATALINA_HOME=/var/lib/apache-tomcat-6.0.18
      export CATALINA_BASE=/opt/tomcat-instance/sales.example.com
      EOF
      $
      $ cat /opt/tomcat-instance/sales.env
      export JAVA_HOME=/usr/java/jdk1.6.0_10
      export PATH=$JAVA_HOME/bin:$PATH
      export CATALINA_HOME=/var/lib/apache-tomcat-6.0.18
      export CATALINA_BASE=/opt/tomcat-instance/sales.example.com
      $
      
      CATALINA_HOME is the base directory of Tomcat that contains all the libraries, scripts etc. for Tomcat. This is the parent directory of the extracted Tomcat tar file.
      CATALINA_BASE is the base directory of the new Tomcat instance, which in this example points to /opt/tomcat-instance/sales.example.com


      Configuring Tomcat Network Ports

      Since this is the first Tomcat instance that's being created here, the default port numbers can be left unchanged in $CATALINA_BASE/conf/server.xml (/opt/tomcat-instance/sales.example.com/conf/server.xml):
      
      
          
      
          
      
      
      However, these port numbers will have to be changed for the second Tomcat instance though, 

      Starting First Tomcat Instance

      To start the newly created Tomcat JVM instance, ensure that the environment variables are set for the new instance and execute the startup script:
      $ source /opt/tomcat-instance/sales.env
      $ $CATALINA_HOME/bin/startup.sh
      Using CATALINA_BASE:   /opt/tomcat-instance/sales.example.com
      Using CATALINA_HOME:   /var/lib/apache-tomcat-6.0.18
      Using CATALINA_TMPDIR: /opt/tomcat-instance/sales.example.com/temp
      Using JRE_HOME:       /usr/java/jdk1.6.0_10
      $
      
      If everything has been configured correctly, you should now see an empty white page when opening the URL http://localhost:8080. Note that instead of localhost you should also be able to use the name of your server. 
      If you get an error in the browser instead of an empty page, check the log files under $CATALINA_BASE/logs (/opt/tomcat-instance/sales.example.com/logs). Note that since CATALINA_BASE has been changed for the new Tomcat instance, the logs are no longer written to /var/lib/apache-tomcat-6.0.18/logs

      Relaying HTTP Port 80 Connections to Tomcat Port 8080

      By default, Tomcat listens on port 8080. To have the Tomcat server itself listen on HTTP port 80, Tomcat would have to run as root since only root can listen on ports below 1024 on Linux. But for security reasons this is not recommended. The solution I prefer is to relay port 80 TCP connections to port 8080 using the Netfilter package that comes with Linux. An alternate solution would be to use a service wrapper like jsvc from the Jakarta Commons Daemon project. But this solution would require the installation and maintenance of another piece of software on my system that I want to avoid. 

      The Netfilter package that comes already with Linux is transparent to Tomcat. The following steps show how to relay port 80 TCP connections to Tomcat's port 8080 using the iptables command from the Netfilter package. Note that these steps must be executed as root:
      # iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
      # iptables -t nat -I OUTPUT -p tcp --dport 80 -j REDIRECT --to-ports 8080
      
      The first rule redirects incoming requests on port 80 generated from other computer nodes, and the second rule redirects incoming requests on port 80 generated from the local node where Tomcat is running. 

      To see the newly configured rules, run:
      # iptables -t nat -L
      Chain PREROUTING (policy ACCEPT)
      target     prot opt source               destination         
      REDIRECT   tcp  --  anywhere             anywhere            tcp dpt:www redir ports 8080 
      
      Chain POSTROUTING (policy ACCEPT)
      target     prot opt source               destination         
      
      Chain OUTPUT (policy ACCEPT)
      target     prot opt source               destination         
      REDIRECT   tcp  --  anywhere             anywhere            tcp dpt:www redir ports 8080 
      #
      
      To remove the NAT rules we just created, you can run the iptables -t nat -F command which flushes and deletes the rules. Note that this will also flush any other rules that may have been configured on your system! For more information on iptables, see netfilter/iptables documentation

      To make the rules permanent for reboots, you can use the following option outlined here for Debian (other Linux distributions have other methods). First save the newly created rules in a file:
      # iptables-save > /etc/iptables.conf
      
      Then edit the /etc/network/interfaces file and add the line highlighted in blue for the public network interface. For example:
      iface eth0 inet static
              address 192.168.1.23
              netmask 255.255.255.0
              network 192.168.1.0
              broadcast 192.168.1.255
              gateway 192.168.1.1
              pre-up iptables-restore < /etc/iptables.conf
      
      The pre-up configuration in this example activates the iptables rules on my system before the public interface eth0 comes up. So the rules can be seen with iptables -t nat -L after each reboot. Note that for security reasons it's important that firewall rules are established before the network interfaces come up. Even though this is not an issue for relaying Tomcat connections, as a matter of good practice, the iptables rules should always be established before the network comes up. 

      It should be noted here that there is one Tomcat configuration parameter that you may or may not want to change, the proxyPort parameter in the server.xml file. Since Tomcat still receives requests on port 8080 as they are relayed by the Linux Netfilter system from port 80, Tomcat may display port 8080 in the URL depending on the application's content. So if you want to change it to port 80, the proxyPort parameter would need to be added in the$CATALINA_BASE/conf/server.xml (/opt/tomcat-instance/sales.example.com/conf/server.xml). file for port 8080:
      proxyPort="80"
                     connectionTimeout="20000"
                     redirectPort="8443" />
      
      After that you need to restart Tomcat to make this change effective. 

      Connecting to First Tomcat Instance Using Default HTTP Port

      If iptables have been configured correctly, you should now be able to open the URL http://localhost and see an empty white page. You could also use the URL http://localhost:80 (port 80 is the default port used by browsers) or the name of your server. If you get an error in the browser instead of an empty page, check the iptables configuration and check the log files under $CATALINA_BASE/logs (/opt/tomcat-instance/sales.example.com/logs). Note that since CATALINA_BASE was changed for the new Tomcat instance, the logs are no longer written to /var/lib/apache-tomcat-6.0.18/logs

      Setting Up a Web Application for First Tomcat JVM Instance

      You can setup multiple web applications for each Tomcat JVM instance. In this guide we are setting up one web application for each Tomcat JVM instance. 

      First make sure to switch to the tomcat user account and source in the environment variables for the remaining steps:
      # su - -s /bin/sh tomcat
      $ source /opt/tomcat-instance/sales.env
      
      
      Setting up Web Application Layout

      In the previous chapter the first Tomcat JVM instance was setup under the base directory $CATALINA_BASE (/opt/tomcat-instance/sales.example.com). In the following example I create a new directory called "sales" under$CATALINA_BASE/webapps which will become the root directory for the first web application, that is $CATALINA_BASE/webapps/sales. In Tomcat web application root directories are created under $CATALINA_BASE/webapps by default.
      $ mkdir $CATALINA_BASE/webapps/sales
      
      
      Configuring Web Application

      To configure Tomcat to recognize the new web application under $CATALINA_BASE/webapps/sales (/opt/tomcat-instance/sales.example.com/webapps/sales), the $CATALINA_BASE/conf/server.xml file needs to be edited. This is done by adding a new Context element with the path and docBase attributes. Note that Tomcat refers to webapps as "context". So Context here represents the configuration of a web application. The path attribute is the application name used within the URL, and the docBase attribute is the absolute path name of the new web application root under $CATALINA_BASE/webapps:
      
      
              
      
      In this example you can see that appBase already points to webapps by default, that is $CATALINA_BASE/webapps. The newly added path attribute points to the sales directory under $CATALINA_BASE/webapps which is the location for the new application. And the docBase attribute is set to mysales which stands for the application name within the URL, i.e. "http://localhost/mysales" or "http://localhost:8080/mysales". Make sure to add this newContext element inside the Host container element for 'localhost' which is the default host name. 

      Home Page for Web Application

      To have a starting page for the new web application, you can simply create and add a index.html file under the web application's root directory $CATALINA_BASE/webapps/sales (/opt/tomcat-instance/sales.example.com/webapps/sales). You could also create your own JSP page here. For testing purposes here is a simple index.html example for the new application:
      $ cat > $CATALINA_BASE/webapps/sales/index.html << EOF
      
      
      
      
      

      Apache Tomcat Sales Home Page

      EOF
      $

      Restarting First Tomcat Instance

      Now check whether the new web application has been configured correctly. To do that, run the following commands to restart the new Tomcat JVM instance:
      $ source /opt/tomcat-instance/sales.env
      $ $CATALINA_HOME/bin/shutdown.sh
      $ $CATALINA_HOME/bin/startup.sh
      
      If everything was configured correctly, you should now see the default home page for the new web application when opening the URL http://localhost/mysales or http://localhost/mysales:8080. Instead of localhost you should also be able to use the name of your server. If you get the error 'java.net.ConnectException: Connection refused' when you shutdown Tomcat, then Tomcat was probably not running. If you don't see the home page, check the log files under $CATALINA_BASE/logs

      Deploying Java Servlet for Web Application in First Tomcat JVM Instance

      Setting up Java Servlet Layout

      To follow the Java Servlet Specification for the new "sales" web application, I created the class directory for the Java class files under the new directory $CATALINA_BASE/webapps/sales/WEB-INF, see also Packaging Web Components. The WEB-INF directory is protected from access by browsers, meaning they are unbrowsable and safe from client views. The classes directory under WEB-INF is where web components and server-side utility classes should go. To create the WEB-INF and classes directories, run the following command:
      $ mkdir -p $CATALINA_BASE/webapps/sales/WEB-INF/classes
      
      
      JAR Files

      Most Java servlets also need JAR (Java ARchive) files which should be put under the lib directory. Since it's a good practice to keep the application separate from the Tomcat distribution directory tree, I created a new lib directory under $CATALINA_BASE/webapps/sales/WEB-INF which is consistent with WAR's hierarchical directory structure.
      $ mkdir $CATALINA_BASE/webapps/sales/WEB-INF/lib
      
      
      The Java servlet example below requires the servlet-api.jar JAR file. This JAR is already available in the Tomcat distribution directory tree $CATALINA_HOME/lib. You could copy this JAR file to the application's new lib directory $CATALINA_BASE/webapps/sales/WEB-INF/lib, but then you would get the following warning in the $CATALINA_BASE/logs/catalina.out log file when you startup Tomcat: 

      INFO: validateJarFile(/opt/tomcat-instance/sales.example.com/webapps/sales/WEB-INF/lib/servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class 

      Tomcat shows this warning since it tries now to load the JAR file twice, first from $CATALINA_HOME/lib and then from $CATALINA_BASE/webapps/sales/WEB-INF/lib. Even though it's not going to be a problem for Tomcat, it's better not to keep JARs in two places. Since the servlet-api.jar JAR file already exists in the Tomcat distribution directory, I did not copy it to the $CATALINA_BASE/webapps/sales/WEB-INF/lib directory. I use this directory for application specific JARs that don't come with the Tomcat distribution. You could also remove the JAR in $CATALINA_HOME/lib but remember that it will reappier the next time you upgrade the Tomcat software. 

      Creating a Java Servlet

      Since server-side classes are supposed to go to the WEB-INF/classes directory, I created the following class file example under $CATALINA_BASE/webapps/sales/WEB-INF/classes (/opt/tomcat-instance/sales.example.com/webapps/sales/WEB-INF/classes) and saved it as Sales.java:
      $ cat $CATALINA_BASE/webapps/sales/WEB-INF/classes/Sales.java
      import java.io.*;
      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class Sales extends HttpServlet {
      
          public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException
          {
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println("");
              out.println("");
              out.println("<b>Sales</b> Page");
              out.println("");
              out.println("");
              out.println("

      Executing Sales ...

      "); out.println(""); out.println(""); } }
      To compile the new Java servlet, the servlet-api.jar JAR file is needed which can be specified with either the -classpath option or the CLASSPATH environment variable. The -classpath option for SDK tools is preferred over the CLASSPATH environment variable since it can be set individually for each application without affecting others. In the following example I specify the path of the class directory with the basename '*' (if you are unfamiliar with basename, see 'man basename'). This is equivalent to specifying all files with the extensions .jar or .JAR files in the directory and therefore individual JAR files like servlet-api.jar don't need to be specified. 

      The following command should now compile the Java servlet without errors:
      $ cd $CATALINA_BASE/webapps/sales/WEB-INF/classes
      $ javac -classpath "$CATALINA_HOME/lib/*" Sales.java
      $ ls
      Sales.class  Sales.java
      $
      
      
      Configuring the Java Servlet

      To configure servlets and other components for an application, an XML file called web.xml needs to be configured. The format of this file is defined in the Java Servlet Specification. In Tomcat, this file exists in two place:
      $CATALINA_BASE/conf/web.xml
        $CATALINA_BASE/webapps/{your-appname}/WEB-INF/web.xml
      The first one is the default web.xml file which is the base for all web applications in a Tomcat JVM instance, and the latter one is for the web application where WEB-INF resides for overwriting application specific settings. 

      For the newly created Java servlet "Sales" I created a new web.xml file under $CATALINA_BASE/webapps/sales/WEB-INF:
      $ cat $CATALINA_BASE/webapps/sales/WEB-INF/web.xml
      
      
      
      
        
          servlet_sales
          Sales
        
      
        
          servlet_sales
          /execute
        
      
      
      
      
      For each servlet there is a  element. It identifies the servlet name () and the Java class name (). The servlet mapping () maps a URI to the servlet name (). In the above example "/execute" in "http://localhost:8080/mysales/execute" maps to "servlet_sales" which points to the "Sales" servlet class. Note that the order of these elements is important. So when you open the URL "http://localhost:8080/mysales/execute", the "Sales" Java servlet will be executed. 

      In the following example I updated the $CATALINA_BASE/webapps/sales/index.html file to provide an entry point to the new Java servlet:
      $ cat $CATALINA_BASE/webapps/sales/index.html
      
      
      
      
      

      Apache Tomcat Sales Home Page

      Execute Sales $

      Testing and Executing the Java Servlet

      Note that if you run javac with the -classpath option or the CLASSPATH environment variable in the same shell before you startup Tomcat, you will get java.lang.NoClassDefFoundError / java.lang.ClassNotFoundExceptionerrors in your browser when you execute a servlet. To avoid this, simply re-login as the tomcat user before you startup Tomcat:
      # su - -s /bin/sh tomcat
      $ source /opt/tomcat-instance/sales.env
      $ $CATALINA_HOME/bin/shutdown.sh
      $ $CATALINA_HOME/bin/startup.sh
      
      After Tomcat restarted, open the URL http://localhost/mysales (or use the server name instead of localhost) and you should see the "Execute Sales" link. Clicking on this link should invoke the Java servlet and display "Executing Sales" in your browser. If you are presented with an empty page instead, review the above steps and make sure you didn't miss a step. Check also the log files under $CATALINA_BASE/logs

      Setting Up Second Tomcat JVM Instance

      General

      If you've gone through all the previous steps in this HOWTO, then the following steps should be very easy to follow and to understand without much explanations. Therefore, I'll provide here just the steps for setting up a second Tomcat JVM instance and an application called "Order". 

      Steps for Second Tomcat JVM Instance and Application

      Login as root and execute the following steps to setup the second Tomcat JVM instance:
      # mkdir -p /opt/tomcat-instance/order.example.com
      # cd /opt/tomcat-instance/order.example.com
      #
      # cp -a /var/lib/apache-tomcat-6.0.18/conf .
      # mkdir common logs temp server shared webapps work
      #
      # chown -R tomcat.tomcat /opt/tomcat-instance/order.example.com
      #
      # su - -s /bin/sh tomcat
      $ cat > /opt/tomcat-instance/order.env << EOF
      export JAVA_HOME=/usr/java/jdk1.6.0_10
      export PATH=\$JAVA_HOME/bin:\$PATH
      export CATALINA_HOME=/var/lib/apache-tomcat-6.0.18
      export CATALINA_BASE=/opt/tomcat-instance/order.example.com
      EOF
      $
      $ source /opt/tomcat-instance/order.env
      $
      
      
      For the second Tomcat JVM instance the default port numbers need to be changed in $CATALINA_BASE/conf/server.xml (/opt/tomcat-instance/order.example.com/conf/server.xml). In the following example I increased the port numbers by one:
      8006" shutdown="SHUTDOWN">
      
          8081" protocol="HTTP/1.1"
                     connectionTimeout="20000"
                     redirectPort="8444" />
      
          8010" protocol="AJP/1.3" redirectPort="8444" />
      
      
      Create a new application root directory:
      $ mkdir $CATALINA_BASE/webapps/order
      
      
      To configure the new web application, edit $CATALINA_BASE/conf/server.xml (/opt/tomcat-instance/order.example.com/conf/server.xml) and add the following entry in blue:
      
      
              
      
      
      Create a new home page for the new "Order" application and include a link to the Java servlet that will be setup next:
      $ cat > $CATALINA_BASE/webapps/order/index.html << EOF
      
      
      
      
      

      Apache Tomcat Order Home Page

      Execute Order EOF
      $
      Now setup and create a new Java servlet:
      $ mkdir -p $CATALINA_BASE/webapps/order/WEB-INF/classes
      $ mkdir $CATALINA_BASE/webapps/order/WEB-INF/lib
      
      $ cat $CATALINA_BASE/webapps/order/WEB-INF/classes/Order.java
      import java.io.*;
      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class Order extends HttpServlet {
      
          public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws IOException, ServletException
          {
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println("");
              out.println("");
              out.println("<b>Order</b> Page");
              out.println("");
              out.println("");
              out.println("

      Executing Order ...

      "); out.println(""); out.println(""); } }
      Compile the new Java servlet:
      $ cd $CATALINA_BASE/webapps/order/WEB-INF/classes
      $ javac -classpath "$CATALINA_HOME/lib/*" Order.java
      $ ls
      Order.class  Order.java
      $
      
      
      Configure the Java servlet:
      $ cat $CATALINA_BASE/webapps/order/WEB-INF/web.xml
      
      
      
      
        
          servlet_order
          Order
        
      
        
          servlet_order
          /execute
        
      
      
      
      
      Now make sure to relogin as tomcat and start the second Tomcat JVM instance:
      # su - -s /bin/sh tomcat
      $ source /opt/tomcat-instance/order.env
      $ $CATALINA_HOME/bin/startup.sh
      
      
      After the second Tomcat JVM restarted, open the URL http://localhost:8081/myorder (or use the server name instead of localhost) and you should see the "Execute Order" link. Clicking on this link should invoke the Java servlet and display "Executing Order" in your browser. If you are presented with an empty page instead, review the above steps and make sure you didn't miss a step. Check also the log files under $CATALINA_BASE/logs