URL: http://www.switch.ch/aai/docs/shibboleth/SWITCH/1.3/idp/run-tomcat-unprivileged.html
Author: Lukas Haemmerle - SWITCH
$Date: 2006/05/19 12:56:48 $
$Revision: 1.6 $ 

Run Tomcat as unprivileged user on Debian 3.1 (sarge)

Table of Contents

Introduction
Overview
Prerequisites
Set permissions
Tomcat Configuration
Links

Introduction

In order to use Tomcat for the Shibboleth Identity Provider it must be able to listen on port 443 (https). Applications that want to bind to a port <1024 under Linux must be run as root user. Running server applications as user root should be prevented due to security considerations. Therefore, applications like Apache are started as root user but then create threads running as unprivileged users.
Unfortunately this cannot be done for Tomcat because it is 100% Java and OS-dependant tasks like changing to lesser than root permissions after startup is not possible. However, there are at least two good possibilities to solve this:

  1. Use Apache as front end and mod_jk2/AJP for communication between Apache and Tomcat
  2. Use iptables to forward traffic between 443 and the actual port Tomcat uses

Option 1 is described in . Option 2 is relatively easy and doesn't involve installing Apache and mod_jk. Therefore, it is described in the following.

Overview

The Shibboleth Identity Provider (IdP) 1.3 is implemented in Java and can be run as a Java web application in Tomcat. The IdP consists of the two servlets SSO (Single Sign On) and AA (Attribute Authority). The users should access the SSO servlet on the port 443 (https). The AA servlet is accessed by the Shibboleth Service Provider (SP) and usually is on port 443 or 8443.

The example values used in this guide are:

aai-login.example.edu
The DNS name of the Home Organization (Identity Provider) server, for real installations instead of "www", names like "aai-logon", "aai" or something similar are used.

Prerequisites

Set permissons

Since we want Tomcat to run not as root, we first create a new unprivileged user. You must be root user for the following tasks.

$ groupadd tomcat $ useradd -g tomcat -d /opt/tomcat tomcat

Next we have to make sure that the user tomcat can read and write to relevant directories. Make sure that Tomcat doesn't run anymore. Otherwise, stop tomcat with:

$ /etc/init.d/tomcat stop

Next we change permissions of the tomcat related directories with:

$ cd /opt/tomcat/ $ chown -R tomcat:tomcat * $ cd /var/log/shibboleth/ $ chown -R tomcat:tomcat * $ cd /etc/shibboleth/ $ chown -R tomcat:tomcat *

Some of the above directories probably are symlinks. So make sure that the owner of the content of these directories is tomcat

Tomcat Configuration

In the following it is assumed that your Tomcat setup uses a single IP and two different ports (443 and 8443) instead of two different IPs and a single port 443. However, it should be easy to adapt the following to the latter.

First we configure Tomcat to use ports greater than 1023, which can also be bound to by unprivileged users. For that we modify the file /etc/tomcat/server.xml and look for the (at least) two Connector elements where the IPs or ports are defined that should be listened on. Change all well-known ports to some other port above 1023. E.g. 443 could be forwarded to 1443.



To tell Tomcat to run as user tomcat we have to edit the file /etc/init.d/tomcat. Furthermore, we insert some iptable rules to forward well-known ports to the ports we configured in the Connector elements of Tomcat.
There should be a code fragment like below. Please add the lines in blue and change the lines in red to fit your environment.

#! /bin/sh -e # # /etc/init.d/tomcat -- startup script for the Tomcat 5.0 servlet engine # # Written by Miquel van Smoorenburg . # Modified for Debian GNU/Linux by Ian Murdock . # Modified for Tomcat by Stefan Gybas . PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin NAME=tomcat DESC="Tomcat 5.5 servlet engine" DAEMON=/usr/bin/$NAME CATALINA_HOME=/opt/$NAME # The following variables can be overwritten in /etc/default/tomcat5 # Run Tomcat 5.5 as this user ID (default: tomcat5) # Set this to an empty string to prevent Tomcat from starting automatically TOMCAT5_USER=tomcat # Flush iptables iptables -F -t nat # Forward ports in order to run Tomcat as non-privileged user iptables -t nat -A OUTPUT -d aai-login.example.edu -p tcp --dport 443 -j DNAT --to-destination :1443 iptables -t nat -I PREROUTING -d aai-login.example.edu -p tcp --dport 443 -j DNAT --to-destination :1443 # Additional rules for AA #iptables -t nat -A OUTPUT -d aai-aa.example.edu -p tcp --dport 443 -j DNAT --to-destination :1443 #iptables -t nat -I PREROUTING -d aai-aa.example.edu -p tcp --dport 443 -j DNAT --to-destination :1443 # The first existing directory is used for JAVA_HOME (if JAVA_HOME is not # defined in /etc/default/tomcat5) JDK_DIRS="/opt/java" # Arguments to pass to the Java virtual machine (JVM) CATALINA_OPTS="-Djavax.net.ssl.trustStore=/etc/tomcat/truststore.aaitest.jks" # Use the Java security manager? (yes/no) TOMCAT5_SECURITY="no" # End of variables that can be overwritten in /etc/default/tomcat5 # overwrite settings from default file if [ -f /etc/default/tomcat5 ]; then . /etc/default/tomcat5 fi test -f $DAEMON || exit 0 # Look for the right JVM to use for jdir in $JDK_DIRS; do if [ -d "$jdir" -a -z "${JAVA_HOME}" ]; then JAVA_HOME="$jdir" fi done export JAVA_HOME export CATALINA_OPTS # Define other required variables PIDFILE="/var/run/$NAME.pid" LOGDIR="$CATALINA_HOME/logs" WEBAPPDIR="$CATALINA_HOME/webapps" STARTUP_OPTS="" if [ "$TOMCAT5_SECURITY" = "yes" ]; then STARTUP_OPTS="-security" fi # CATALINA_PID for catalina.sh export CATALINA_PID="$PIDFILE" case "$1" in start) if [ -z "$TOMCAT5_USER" ]; then echo "Not starting $DESC as configured (TOMCAT5_USER is empty in" echo "/etc/default/tomcat5)." exit 0 fi if [ -z "$JAVA_HOME" ]; then echo "Could not start $DESC because no Java Development Kit" echo "(JDK) was found. Please download and install JDK 1.3 or higher and set" echo "JAVA_HOME in /etc/default/tomcat5 to the JDK's installation directory." exit 0 fi echo -n "Starting $DESC using Java from $JAVA_HOME: " # Remove dangling webapp symlinks for webapp in "$WEBAPPDIR"/*; do if [ "$webapp" != "$WEBAPPDIR/*" -a ! -e "$webapp" ]; then echo "Removing obsolete webapp $webapp" >>"$LOGDIR/catalina.out" rm "$webapp" >> "$LOGDIR/catalina.out" 2>&1 || true fi done # Symlink new webapps from /usr/share/java/webapps for webapp in /usr/share/java/webapps/*; do if [ -e "$webapp" -a ! -e "$WEBAPPDIR/`basename $webapp`" \ -a ! -e "$WEBAPPDIR/`basename $webapp .war`" ]; then echo "Symlinking new webapp $webapp" >>"$LOGDIR/catalina.out" ln -s "$webapp" "$WEBAPPDIR" || true fi done # Create catalina.policy (for the security manager) # rm -f /var/lib/tomcat/catalina.policy # cat /etc/tomcat/policy.d/*.policy >/var/lib/tomcat/catalina.policy mkdir -p "$CATALINA_HOME/work/_temp" touch "$PIDFILE" "$LOGDIR/catalina.out" || true chown --dereference "$TOMCAT5_USER" "$PIDFILE" "$LOGDIR" \ "$LOGDIR/catalina.out" "$CATALINA_HOME/work" \ "$CATALINA_HOME/temp" || true if start-stop-daemon --test --start --pidfile "$PIDFILE" \ --user $TOMCAT5_USER --startas "$DAEMON" >/dev/null; then # -p preserves the environment (for $JAVA_HOME etc.) # -s is required because tomcat5's login shell is /bin/false su -p -s /bin/sh $TOMCAT5_USER \ -c "\"$DAEMON\" start $STARTUP_OPTS" \ >>"$LOGDIR/catalina.out" 2>&1 echo "$NAME." else echo "(already running)." fi ;; stop) echo -n "Stopping $DESC: " if start-stop-daemon --test --start --pidfile "$PIDFILE" \ --user $TOMCAT5_USER --startas "$DAEMON" >/dev/null; then echo "(not running)." else su -p $TOMCAT5_USER -c "\"$DAEMON\" stop" >/dev/null 2>&1 || true # Fallback to kill the JVM process in case stopping did not work sleep 1 start-stop-daemon --stop --oknodo --quiet --pidfile "$PIDFILE" \ --user "$TOMCAT5_USER" rm -f "$PIDFILE" echo "$NAME." fi ;; restart|force-reload) $0 stop sleep 1 $0 start ;; *) echo "Usage: /etc/init.d/tomcat {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0

Make sure that you don't have a file /etc/defaults/tomcat that contains a line TOMCAT5_USER=root. Otherwise, comment out that line.

Finally, we start tomcat again with:

$ /etc/init.d/tomcat start

If something doesn't work as expected you may have a look at /opt/tomcat/logs/catalina.out and /var/log/shibboleth/shib-error.log for further details. If there are no new Shibboleth log entries after a restart of Tomcat, the user tomcat probably cannot write to the log directory /var/log/shibboleth.

Links

SWITCH AAI project website
http://www.switch.ch/aai/
Shibboleth at Internet2
http://shibboleth.internet2.edu
Debian
http://www.debian.org
Sun Java
http://java.sun.com
Apache Tomcat
http://tomcat.apache.org

--
$Id: run-tomcat-unprivileged.html,v 1.6 2006/05/19 12:56:48 haemmer Exp $