Introduction 

Tomcat is one of the Industry’s most popular application servers, both because of its performance and because of its price (free).  Included with Tomcat is an application referred to as “The Manager App” by the documentation, which provides basic monitoring and application deployment functions for a Tomcat instance. It includes the ability to start or stop applications that have already been deployed, deploy new applications and unload or underlay existing applications.  It also provides statistics on sessions, server version and memory usage.  Needless to say, these capabilities and information should not be made available to just anyone.  Tomcat provides some tools that can be employed to secure the Manager App, and I’ll show you how to use them to control access and isolate the Manager App from those who should not have access to it.  In my example, I’ll be using Tomcat 7.0.64 on Solaris 10, but the steps will also work in a Linux environment. 

Step 1: Isolate Access To The Manager App 

The first step in securing the Manager App is to isolate access to it from access to the other applications served by Tomcat.  One of the best ways of doing this is to create a distinct interface in the Solaris OS that is used only for Management network traffic.  This is a common security practice in most Enterprise environments, and chances are, you have already done this.  Now you have to make sure that the Manager App can only be accessed via the management network interface, and not via the public interface on your server, where your other applications deployed to Tomcat are accessed. 

If you look in $CATALINA_BASE/webapps, you will see that the Manager App has been deployed in its own context.  The context definition is in $CATALINA_BASE/webapps/manager/META-INF/context.xml.  If you look at the bottom of this file, you will see the following statement: 

<Valve className=“org.apache.catalina.valves.RemoteAddrValve 
allow=“127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1” /> 

This statement uses the Tomcat Remote Address Valve to limit access to anything in the context to the localhost IP address.  By default this statement is commented out.  Remove the comment characters (the <!— and —> lines).  Since most of us (hopefully) don’t have a web browser installed on our server, we need to configure the RemoteAddrValve to allow access from a particular IP address (or addresses) from which it is safe to access the Manager App.  Let’s say that we want to allow access from 10.10.5.6 and 10.10.5.7.  Change the regular expression in the allow statement so that it reads: 

<Valve className=“org.apache.catalina.valves.RemoteAddrValve 
allow=“10\.10\.5\.[67]” /> 

Don’t forget to restart Tomcat to make this change effective. 

Now we must provide a path for network traffic to traverse the separate management interface and reach the Manager App.  To do this, we must set up a Tomcat HTTP connector that listens only on the management interface that we defined in Solaris.  HTTP connections are defined in $CATALINA_BASE/conf/server.xml.  Assuming that our management network interface has an IP address of 10.10.5.15, we would add a new HTTP connection like this: 

<Connector port=“8080” address=“10.10.5.15” server=“none” protocol=“HTTP/1.1” /> 

The connector should be placed inside of the Service element in the server.xml.  After saving your changes, restart Tomcat and verify that you now see Tomcat listening on the management IP address by checking the output of netstat. 

The connector and the context work together to restrict access only to the management interface of your server.  The Connector provides access to the Tomcat instance, but is restricted by the “address” setting in the Connector definition.  In the Manager App context, the RemoteAddrValve only allows access from selected IP addresses.  If someone tries to access the Manager App via the public network interface and Tomcat Connector, the RemoteAddrValve will prevent access. 

Step 2: Authentication of Manager App Users 

As we mentioned in the Introduction, we should not allow the Manager App to be used by just anyone.  To prevent this, the Manager App is configured by default to allow access only to users who have certain permissions.  The Manager App is configured to allow access via a web browser to any user who has been assigned the “manager-gui” role.  By default, no users exist, so therefore no one has access.  In its default configuration, the Manager App uses an XML file in $CATALINA_BASE/conf called tomcat-users to create users and assign them roles.  While this is better than nothing, there are several security problems with this approach: 

  1.  User and password information is stored in an unencrypted file that is accessible by others 
  2. Users are never forced to change their passwords, nor do they have the ability to do so without ssh access to the server, or help from an administrator 
  3. The complexity of user passwords cannot be enforced 
  4. In a multi-server environment, users must have a separate ID and password for each server, stored locally on that server 

An easy way to solve all of these problems is to use a centralized authentication provider, such as LDAP.  A properly configured LDAP environment not only solves the security concerns above, it also keeps track of additional information about users, such as what roles they hold.  In LDAP, we can create objects which represent the roles that the Tomcat Manager App recognizes, and assign those roles to various users in the LDAP directory. 

Tomcat accesses LDAP via the JNDI (Java Naming Directory Interface) API.  JNDI can access almost any kind of provider, including LDAP.  For our example, we will assume that our Tomcat Server is using Oracle’s Directory Services Enterprise Edition LDAP Directory. 

 

  1.  First we have to create an LDAP object for the manager-gut role by adding the entry below to a file called add_role.ldif (the file name does not matter: 
     
    dn: cn=manager-gui,dc=example,dc=com 
    objectClass: top 
    objectClass: LDAPsubentry 
    objectClass: nsRoleDefinition 
    objectClass: nsSimpleRoleDefinition 
    objectClass: nsManagedRoleDefinition 
    cn: manager-gui 
    description managed role for Tomcat Deployers using the Manager GUI 
  2. Now run the command below to add the entry to the directory.  You will need elevated privileges to add the entry, so if you don’t have rights, contact someone who does and have the execute this for you 

    ldapmodify -p <ldap port> -h <ldap hostname> -D “uid=admin,ou=people,dc=example,dc=com -f ./file.txt 

  3. Now let’s modify a user and assign them the manager-gui role.  Place the following text into a file: 
    dn: uid=someuser,ou=people,dc=example,dc=com 
    change type: modify 
    add: nsroleDN 
    nsroleDN: manager-gui  
  4. Again, let’s use ldapmodify to make the change to your user’s LDAP entry.  Running this command will require LDAP admin privileges: 

    ldapmodify -p <ldap port> -h <ldap hostname> -D “uid=admin,ou=people,dc=example,dc=com -f ./file.txt 

  5. Now let’s use JNDI to define a security realm for that context that the Manager App is running in.  A Realm is a Tomcat security boundary - everything inside the Realm that we are defining here will require authentication via LDAP.  Since we only want to use LDAP to authenticate the Manager App, we will define the Realm inside of the Manager App context.  Add the following to the top of the  Manager App context.xml file,  under the <Context> element.  This file is located at %CATALINA_BASE/webapps/manager/META-INF/context.xml 

     

    <Realm clasName=“org.apache.catalina.realm.JNDIRealm 
              connectionName=“uid=bindaccount,ou=people,dc=example,dc=com” 
              connectionPassword=“connectionpassword 
              connectionURL=“ldaps://ldap.host.name:<secure port>” 
              useStartTLs=“true” 
              userPattern=“uid={0},ou=people,dc=example,dc=com” 
              userRoleName=“nsroledn 
    /> 

    Here is some explanation of the JNDI parameters: 

    connectionName - This the DN of the ID that is used to bind to LDAP 

    connectionPassword - This is the password of the ID that is used to bind to LDAP 

    connectionURL - This is the hostname and port of the LDAP server.  In our example, we are using an SSL connection to bind to LDAP securely over TCP port 3636 

    userPattern - This is the search criteria that LDAP will use when trying to locate the entry of the user who is requesting access to the Manager App.  The {0} in the parameter substitutes the userid supplied to Tomcat when it displays the browser login dialogue box 

    userRoleName - This is the name of the attribute in the user’s LDAP directory entry that contains the name of a role held by that user.  There may be multiple occurrences of the nsRoleDN attribute. 

    After restarting Tomcat, if you open your browser to http://<management IP>/manager, the browser will now prompt you for your LDAP username and password.  Once correct values have been supplied, Tomcat displays the Manager App page, since you added the role to your LDAP entry by virtue of the nsRoleDN attribute, Tomcat recognizes that you hold the manager-gut role, and allows you in to the application 

Step 3 - Add SSL To The Management Connector 

Although you are now supplying a userid and password for access to the Manager App, and those credentials are being verified by LDAP, the connection between your browser and the Tomcat server is not encrypted, so your credentials are being passed over the network in plain text.  Its time to fix that by configuring SSL on the Management IP connector in Tomcat. 

 

In order to configure SSL, we must have an SSL certificate and private key.  In our example, I’m going to use a certificate generated by a self-signed Certificate Authority.  For Tomcat to use the certificate and key to secure the connection, they will need to be placed into a Java Key Store (JKS) database.  But they must first be converted into a format where they can be imported into the database.  Once they are in the JKS, Tomcat can use them to secure the connection.  Here is what we need to do to set that up: 

  1. First use OpenSSL to convert your self-signed CA, your certificate and your key into the pk12 format, so that they can be imported into a JKS 

    openssl pkcs12 -export -in examplecert.crt -inkey examplekey.key -out examplecert.p12 -name tomcat -CAfile exampleCA.crt -caname example root -chain 

    You will be prompted  for a export password.  Be sure an remember it or write it down, because you will need it in the next step. 

  2. Now we need to create a new JKS file and import our examplecert.p12 file into it: 

    keytool -importkeystore -srckeystore examplecert.p12 -srcstoretype PKCS12 -destkeystore tomcat.jks 

    You will be prompted for a source keystone password.  This is the export password that you entered above.  You will also be prompted to create a destination keystone password.  Record this as well - it will be needed in the next step.  Copy your new tomcat.jks file to the $CATALINA_BASE/conf directory, and make sure that the ID that tomcat runs as has read access to the file. 

  3. Next, we will alter the definition of our management side connector to add SSL to it: 
    <Connector port=“8080 
       address=“10.10.5.15” 
       server=“none” 
       protocol=“HTTP/1.1” 
       SSLEnabled=“true” 
       scheme=“https” 
       secure=“true” 
       useServerCipherSuitesOrder=“true” 
       ciphers=“TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA” 
       keyAlias=“tomcat” 
       keyPass=“<export key password from the PKCS12 file>” 
       keystoreFile=“conf/tomcat.jks” 
       keystorePass=“<keystore password>” 
       sslEnabledProtocols=“TLSv1.1, TLSv1.2” 
       /> 

    Here is a bit of explanation for the additional parameters: 

    SSLEnabled - Turns on SSL encryption for this connector 

    scheme - Allows Tomcat to respond accurately to scheme requests 

    secure - Allows Tomcat to respond accurately to requests on whether SSL is enabled 

    useServerCipherSuiteOrder - By default, when setting up a new SSL connection, Tomcat will use whatever Cipher the client specifies.  This setting forces the connection to use one of the SSL ciphers configured in Tomcat, which prevents weak clients from compromising security 

    ciphers - The list of ciphers that Tomcat will allow.  In our example the list contains the recommended ciphers if you are using a 1.8.x JRE 

    keyAlias - The alias for the key in the JKS database.  This comes from the “-name” parameter in step a above 

    keyPass - The export key password entered in step a above 

    keystoreFile - The path and name of the JKS file.  The path is relative to $CATALINA_BASE if a leading “/“ is missing 

    keystorePass - The password for the JKS keystone 

    sslEnabledProtocols - The list of protocols that will be used for the SSL connection 

     

    Restart Tomcat after making these changes to the server.xml file.  Now the connection to the Manager App is protected by SSL, so your ID and password are encrypted. 

Conclusion 

By making the changes above, we’ve take a useful application, and made it accessible in a safe manner by isolating from the general public, and securing all connection between the app and its users, and the application and LDAP.  The steps presented here can be used to protect any application, not just the manager app, although the application must be written to work with JNDI for it to support roles.  Configuring JNDI as we have can be used to implement basic authentication security for any context.  Implementing SSL is a standard security practice, although its not usually done on Tomcat, as most architectures put a reverse proxy or web server that hosts SSL in front of Tomcat.  There are many management applications available for Tomcat, and this article has given you the tools to implement good security for them in your Tomcat hosting environment. 

For More Information

Click the links below to get more insightful articles related to this subject: