In my previous post on Different JVM settings for different Tomcat instances, I briefly touched on using tomcat-users.xml to allow access to the the Server Status page. This page, along with the Tomcat Manager application (amongst others), are installed by default when you deploy Tomcat. Access to them is controlled via the tomcat-users.xml file.
As we saw in the previous article, we can enable access by defining a role, and subsequently a user who is part of that role.
Why doesn't Tomcat ship with a default account?: Simple. It's a security issue. Instead of providing that default account, Tomcat is locked down completely, and it's up to us to enable access.
Let's try accessing the Tomcat Manager without having any users defined in tomcat-users.xml.
As you can see, access is restricted. However, what it does do is tell us (some of) the types of roles available to us.
Let's look at our tomcat-users.xml file again. Now, based on what you see in the file, and what we added in the previous article, I'm hoping most (if not all) of you are a little concerned by the fact passwords are stored in plain text.
Thankfully this file isn't directly accessible from a web browser, however it can be read by anyone who has access to the server.
Tomcat Manager roles
Tomcat 7 introduced granular control over the roles available to us for accessing the Tomcat Manager, Host Manager and Server status pages. (Technically it was already there in Tomcat 6, but you had to do a bunch of manual work).
Previously, there was a "manager' role that you would add users to, to give them access to the Server Status page. The downside of this approach was that by assigning the user to the "manager" role, you also gave them access to the Tomcat manager, which allows tasks such as deploying/undeploying apps, expiring sessions, etc.
Not really ideal.
Let me, or should I say, Tomcat 7 introduce four delegation roles:
Full access to the Tomcat Manager.
Provides the same access as manager-gui, but from a text interface. Command-line savvy administrators can utilise this role to do everything that manager-gui does, but they can programatically control actions via scripts written using perl, python, etc.
Provides access to the jmxproxy, which exposes information for monitoring purposes.
Access to the Server Status page.
All manager-* roles can access the Server Status page.
As an aside, the "admin" role has also been split into admin-gui and admin-script. These do exactly what they say, and provide access to the Host Manager application.
Tomcat Realms are simply a way of "storing" usernames, passwords and roles. The default out of the box realm used is "UserDatabaseRealm". This particular realm reads clear text passwords out of tomcat-users.xml
We're going to look at a couple of alternative realms, namely: MemoryRealm and JDBCRealm. There are other realms available to you, and if your interested, you can read up on them at: http://tomcat.apache.org/tomcat-7.0-doc/realm-howto.html
What we're going to do is change the realm to "MemoryRealm", which whilst still reading the user details out of tomcat-users.xml, will now do so using a specified encrypted format: SHA, MD2 or MD5.
The algorithm must be supported by the java.security.MessageDigest class
Open up CATALINA_HOME/instance/conf/server.xml
Find "UserDatabaseRealm", as below, and comment it out.
Next, add the following Realm inside the Host block, which is just below the section you commented out.
Save the file, and exit.
We now need to create a SHA encrypted version of the password you want to use. Tomcat is useful here as it provides a script to do just that. Inside CATALINA_HOME/bin, run the following:
digest.sh -a SHA <yourpassword>
As a proper example:
digest.sh -a -SHA admin
You now want to replace your password (in tomcat-users.xml) with the encrypted version.
<user username="admin" password="d033e22ae348aeb5660fc2140aec35850c4da997" roles="manager-gui"/>
Now restart Tomcat and you should be able to login.
As another little aside, let's briefly mention Realms and how they apply.
- This Realm is shared across all web applications, on all virtual hosts, unless overridden by a or element.
- This Realm is shared across all web applications for this virtual host, unless overridden by a element.
- This Realm will only be used for this application.
To configure the JDBCRealm, we need to do a couple of things first:
- Create tables and columns in your database of choice (we're going to use MySQL in this scenario)
- Configure a 'tomcat' user to allow Tomcat to talk to MySQL
- Drop the MySQL connector JAR file into place
- Set up our Realm
Use the following scripts to create your tables:
(I've created a dedicated database called TomcatAuthentication)
create table users (
user_name varchar(15) not null primary key,
user_pass varchar(50) not null
create table user_roles (
user_name varchar(15) not null,
role_name varchar(15) not null,
primary key (user_name, role_name) );
We need to populate our tables with data, so now run the following:
insert into users values('admin','d033e22ae348aeb5660fc2140aec35850c4da997');
insert into user_roles values('admin','manager-gui');
We now need to grant a 'tomcat' user read access to these tables, so run the following from your favourite MySQL client tool:
grant select on TomcatAuthentication.* to 'tomcat'@'localhost' identified by 'acRs7mLLbA';
The next thing we need to do is get a hold of the MySQL JDBC driver, which we can download from:
Download the file, extract the contents, and then copy the JAR file (in my case mysql-connector-java-5.1.20-bin.jar) to CATALINA_HOME/lib
Now we want to add a Realm to our servers.xml file to support JDBC.
Add the following to our definition (replacing the MemoryRealm):
userTable="users" userNameCol="user_name" userCredCol="user_pass"
I've already replace the appropriate entries above to those we're using in this article.
Now you can restart Tomcat, and when you try to access the Tomcat Manager or Service Status page, authentication will now take place against MySQL.
You can obviously harden your password further by using SSL, and locking down access to internal IPs or even just localhost, and we'll look at this in a future article.