pwnt.be

Access Control for Tomcat’s Manager App

On Apache Tomcat 7, deploying and undeploying Java EE webapps on the fly is straightforward thanks to its Manager app. You just head on over to /manager, log in with a manager-gui user, and click around. Alternatively, you use the Client Deployer with a manager-script user to connect to the Manager’s REST-like API. That’s all well and good, but there’s a huge catch …

For, you see, as access to the Manager is defined on a per-host basis, any user who can deploy to application context /foo on your host can also deploy to context /bar. Consequently, if you want to share a host between users, there is no waterproof way to keep them from messing with each other’s apps.

How to Fix It

One solution to this is to define a virtual host for each user or group of trusted users. However, in some cases, this is just not practical. For instance, personally, I’m dealing with teams of students, who will each be asked to deploy an app to a context path which has been allocated for them. Without fine-grained access control, there is no way to keep the team12 user from manipulating the app at /team07, for instance. This principle might still be extended to provide contexts for both an unstable and a stable version of the app, but the fact remains that access control is desirable. Moreover, the solution I am about to provide is easily extended.

So, yes, I am going to explain how to add access control to Tomcat’s Manager app. This tutorial requires a tiny bit of coding, but it’s not as bad as it sounds.

What we’re going to be doing is writing a filter, which is essentially a component that gets called before the actual app. Thus, we’re going to be intercepting certain calls to the Manager app to see if their actions are allowed, and if not, we won’t pass them to the Manager and throw an error instead.

Prerequisites

First off, if you don’t have a Java EE IDE installed yet, get one. I’m going to be describing the required steps for NetBeans IDE 7.1, but Eclipse and friends should work fine. Just make sure your environment supports Java EE.

Set Up the Project

Fire up NetBeans and create a new project. You might be tempted to create a Java EE project. That would probably work, but personally, with version 7.1 of NetBeans, I am unable to select my Tomcat instance as a server.

Luckily, you can just as easily start off with a Java Application, so let’s do that instead. Under File, select New Project, or just use the toolbar button. Give the project a meaningful name—say, TomcatManagerAuthenticator. Don’t create a main class; it’s no problem if you do, but it’s simply not necessary.

The only downside to a Java Application project is that it does not include the Java EE libraries. Rectify this by right-clicking on Libraries under your new project, and selecting Add Library. Choose Java EE 6 API Library and add it. You are now ready to start writing the filter code.

Write the Filter

A filter is actually just another Java class, which implements Java EE’s servlet Filter interface. Let’s create such a class. Right-click your project and, under New, select Java Class. Name the class MyManagerFilter or whatever you like. Using a package other than the default—i.e., empty—is not necessary.

You’ll then see the code for the empty MyManagerFilter class. Make it implement the Filter interface, as shown below this paragraph. At the end of this section, you’ll find a complete implementation of the class.

public class MyManagerFilter implements Filter {
  public void init(FilterConfig filterConfig)
      throws ServletException {
  }

  public void doFilter(ServletRequest request,
      ServletResponse response,
      FilterChain chain)
      throws IOException, ServletException {
  }

  public void destroy() {
  }
}

Now, let’s see how we can actually intercept those HTTP requests to the Manager app. Upon each of them, the doFilter method of our filter will get called. Consequently, we can examine the request parameters to find out who is the user who is currently logged in, and which app he is trying to manipulate. The user name is part of HTTP’s Authorization header and is easily obtained. The path to the application which is being managed is consistently passed via a GET parameter called path. Thus, placing the following code in doFilter tells us everything we need to know:

HttpServletRequest hreq = (HttpServletRequest) request;
String user = hreq.getRemoteUser();
String path = request.getParameter("path");

Note that all of this applies to both the Manager’s HTML-based GUI and its text-based REST interface, for use with the Client Deployer.

How the variables user and path are further used depends on your exact goal. Regardless, either the call is allowed or it isn’t, so let’s represent this using a boolean variable and work from that:

boolean allowed = isAllowed(user, path);
if (allowed) {
  // Forward the request.
  chain.doFilter(request, response);
} else {
  // Throw a HTTP 403 (Forbidden) error.
  HttpServletResponse hresp = (HttpServletResponse) response;
  hresp.sendError(HttpServletResponse.SC_FORBIDDEN);
}

That’s all there is to doFilter, but we have yet to implement our own function isAllowed. I’ve cooked up a sample implementation of MyManagerFilter, which should get you started. You could, for instance, retrieve the access control list—i.e., userToApps in the code—from an XML file. Remember that any user names you would like to use will also need to be defined in Tomcat’s tomcat-users.xml file, each with the appropriate role.

Compile the Project

Save MyManagerFilter.java. You are now ready to compile this source file to the equivalent MyManagerFilter.class. To do so, select Build Main Project from NetBeans’s Run menu, or just hit F11.

If you navigate to the project folder in Windows Explorer (or something equivalent), you’ll find a folder called build, containing a classes folder with the class file you’re looking for. If you’re having trouble finding the project folder, right-click on your NetBeans project and select Properties.

Keep your Explorer window open, since you’ll need that class file in a second.

Add the Filter to the Manager

There are two steps to installing the newly created filter class on your Tomcat instance. First, you need to copy the physical file MyManagerFilter.class to the Manager app’s directory structure. Second, the app’s configuration needs to be updated to include the filter in the invocation chain.

So, let’s first place the filter class on the server. Your Tomcat installation directory contains a webapps folder by default. Inside it, there is a folder called manager, which contains (part of) the Manager app. Inside that, there should be a folder called WEB-INF. In there, copy your NetBeans project’s classes folder, which only contains MyManagerFilter.class. If, for some reason, WEB-INF already contains a classes folder, just add your class file to it.

Also in WEB-INF is a file called web.xml. Open it in a text or XML editor. This file tells Tomcat how to run the Manager app. There should already be some <filter> and <filter-mapping> elements in there. In a very similar way, we’ll add our own filter to the chain. Below them, just add this code snippet:

<filter>
  <filter-name>MyManagerFilter</filter-name>
  <filter-class>MyManagerFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>MyManagerFilter</filter-name>
  <url-pattern>/text/*</url-pattern>
</filter-mapping>

The first part informs Tomcat of the filter’s existence. The second part tells it to apply the filter to any URL belonging to the Manager app that starts with /text/. This ties the filter to the REST API. If you’d like to use it on the HTML GUI instead, just change text to html.

Test Things Out

And that’s pretty much it! In the mean time, Tomcat will have detected the change in configuration and the Manager app will have been reloaded. However, if you’ve turned this feature off for whatever reason, or if you’re simply unsure, just restart Tomcat to be safe. If you entered text above, try to deploy some apps using the Client Deployer; if you entered html, point your browser to the Manager, try some different logins, and poke around.

If you’re stuck at any point, feel free to leave a comment below. I’ll probably be extending this tutorial with some nice screenshots in the future.

Final Notes

A lot of this stuff is part of Java EE rather than Apache Tomcat, so it could also be applicable to other application servers, such as GlassFish. If you have any pointers for users of those, be sure to drop a line.

Finally, credit goes to Tim Funk, who coined the basic idea for this approach on the Tomcat user mailing list way back in 2003. Since it took me a while to dig that up and I couldn’t find a complete example, I figured I’d write this tutorial. I hope it’ll be of use to anyone trying to secure their Tomcat instance.

Post a Comment

This contraption supports Gravatar, as well as Markdown with SmartyPants. If none of that made sense to you, feel free to ignore it and start typing.

  • :)
  • :D
  • ;)
  • :-O
  • :P
  • :@
  • :$
  • :S
  • :(
  • :'(
  • :|
  • :-#
  • 8-|
  • ^o)
  • :-*
  • +o(
  • :^)
  • *-)
  • 8-)
  • |-)
30 euro gratis!
Disorientation
Continuity
Tangentiality
Retributions
Simple Linear Regression with JFreeChart
Dan Moore, Nicolas Machado, Sascha, Tim, Sascha, Tim, Sascha
Bizar Hairdressing & Beyond
Ramona, jul, Hanne, Hanne, Ruxi, Wim, Tim, Sarina, Lies, Lynn, erwin, Ano, Frederick, Jacqueline, Wazaaa, Tim, Rebecca, Charlie
Open brief aan Belgacom Proximus
Tim, pvc, ben, Tim, Simon Allais, Tim, Hans Similon, Simon Allais, Lander Foquet, Shane Vandewalle
Proximus, Universiteit Gent, Kafka: schrappen wat niet past
WouterH, Tim, Bart Coppens, Tim, Steven, Tim, Femke
Redeprognostication
Cédric Verstraeten, Tim, Joyce, Menti
Colophonics