en:tech:idp:jetty

Java servlet container for Shibboleth IdP

This page describes Jetty installation, a Java servlet container, required by Shibboleth IdP 3 in Linux distribution Debian. After successful installation, the last step is to install Shibboleth IdP.


Jetty

Shibboleth consortium states that Apache Tomcat 7+ or Jetty 8+ is required to run Shibboleth IdP 3. Debian 8 (Jessie) contains Tomcat versions 7 and 8 as well as Jetty version 8 packages, however, we think it is best to stick to recommendations and use Jetty 9.3 which is being used for Shibboleth IdP development and testing. By the way, only Tomcat 8 and Jetty 9.2–9.3 are supported.

IdP starts much faster when deployed using Jetty compared to Tomcat. There is a way to improve starting time in Tomcat, but still it is not as fast as Jetty. The truth is there is no need to restart Java container after every Shibboleth IdP configuration change, but still we are on our own if we have any troubles. There is quite nice article Why Choose Jetty? with reasons for choosing Jetty over any other servlet container.

If you still prefer Tomcat, you will probably be fine, but you have been warned. Should you have any issues, be prepared that you might be asked to reproduce your problem with Jetty when you are seeking help.

Prerequisites

In order to Jetty to run as an HTTP server on the port 443 (standard port for HTTPS protocol) it requires root privileges. Due to security reasons, it is advisable to run Jetty under unprivileged user on the port 8443 and arrange listening on the standard HTTPS port 443 using iptables and ip6tables redirects.

The unprivileged user is going to be named idp and also a member of the group idp. This user will own directories containing Jetty and Shibboleth IdP as well.

# Creating idp group and idp user
groupadd idp
useradd -m -g idp -s /bin/bash idp

Now port redirects are required. The first command redirects all the traffic for the port 80 at the local interface (so called loopback) to the port 8080 where Jetty listens for unencrypted requests. It takes care only of the local interface lo, thus running Shibboleth IdP scripts such as status.sh, reload-service.sh and others is possible without defining any parameters. No parameters means that those scripts connect to http://localhost/. The second command redirects all the traffic for the port 443 to the port 8443 which is the port awaiting encrypted communication.

# Redirecting ports using iptables (IPv4)
iptables -t nat -A OUTPUT -o lo -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 443 -j DNAT --to-destination :8443

In case of IPv6 (if available on the machine), only encrypted communication needs redirecting.

# Redirecting ports using ip6tables (IPv6)
ip6tables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination :8443

iptables and ip6tables rules need to be saved and reloaded after system restart. In Debian, for example, one can save the rules into /etc directory.

# Saving the rules for iptables and ip6tables
iptables-save > /etc/iptables.conf
ip6tables-save > /etc/ip6tables.conf

The last thing is to write scripts that will load the saved rules after reboot. Forgetting to set those scripts as executable causes not running them.

# Creating the scripts for reloading the iptables and ip6tables rules
echo '#!/bin/bash' > /etc/network/if-up.d/iptables
echo 'iptables-restore < /etc/iptables.conf' >> /etc/network/if-up.d/iptables
echo '#!/bin/bash' > /etc/network/if-up.d/ip6tables
echo 'ip6tables-restore < /etc/ip6tables.conf' >> /etc/network/if-up.d/ip6tables
chmod +x /etc/network/if-up.d/iptables /etc/network/if-up.d/ip6tables

Installation

Jetty installation is very simple. Just downloading Jetty source code (verifying the fingerprints!), putting it into /opt/src directory and running a few commands as follows.

# Basic Jetty installation
cd /opt
mkdir -p src jetty/tmp
chown -R idp:idp src jetty
su idp
cd /opt/src
wget https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.14.v20161028/jetty-distribution-9.3.14.v20161028.tar.gz \
  https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.14.v20161028/jetty-distribution-9.3.14.v20161028.tar.gz.sha1 \
  https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.14.v20161028/jetty-distribution-9.3.14.v20161028.tar.gz.asc \
  https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.3.14.v20161028/jetty-distribution-9.3.14.v20161028.tar.gz.asc.asc.sha1
 
# Verifying the SHA1 fingerprints
# The sha1sum output must be the same as the file content
# jetty-distribution-9.3.14.v20161028.tar.gz.sha1
sha1sum jetty-distribution-9.3.14.v20161028.tar.gz && cat jetty-distribution-9.3.14.v20161028.tar.gz.sha1
 
# It is recommended to also verify the source code signature
# 8FB67BAC key has to be imported first
gpg --keyserver hkp://keys.gnupg.net --search-keys 8FB67BAC
# Now the signature can be verified
gpg --verify jetty-distribution-9.3.14.v20161028.tar.gz.asc
 
exit
tar -xzf src/jetty-distribution-9.3.14.v20161028.tar.gz
chown -R idp:idp /opt/jetty-distribution-9.3.14.v20161028
ln -snf /opt/jetty-distribution-9.3.14.v20161028/bin/jetty.sh /etc/init.d/jetty
echo "JETTY_HOME=/opt/jetty-distribution-9.3.14.v20161028/" > /etc/default/jetty
echo "JETTY_BASE=/opt/jetty" >> /etc/default/jetty
echo "JETTY_USER=idp" >> /etc/default/jetty

Now it is time to configure Jetty. The basic configuration is done by running Jetty with defining required modules used for Shibboleth IdP:

# Basic Jetty configuration (as idp user)
su idp
cd /opt/jetty
java -jar /opt/jetty-distribution-9.3.14.v20161028/start.jar \
    --add-to-startd=http,https,logging,deploy,jsp,jstl,plus,servlets,annotations,ext,resources,logging,requestlog

It is advisable to limit Jetty for unencrypted communication only via local interface lo. This enables one to easily run Shibboleth IdP scripts such as status.sh, reload-service.sh and others. In the configuration file start.d/http.ini, a restriction has to be defined.

# Tweaking Jetty configuration for HTTP (as idp user)
#su idp
vi start.d/http.ini

Uncommenting jetty.http.host variable and setting it to localhost value (default is 0.0.0.0).

// Enabling HTTP only for localhost
jetty.http.host=localhost

In /opt/jetty/webapps/root directory, a simple web page to be shown after accessing Jetty installation should take place. This is not mandatory, however, if a user mistakenly accesses that page a useful information is highly appreciated. The index.html file content is arbitrary – for example, a redirect to the organizational home page.

# Creating a simple web page for Jetty (as idp user)
#su idp
mkdir -p /opt/jetty/webapps/root
vi /opt/jetty/webapps/root/index.html

Jetty also needs a configuration file for the IdP. This file is idp.xml and defines which WAR (Web application ARchive) contains the IdP, in this case https://SERVER_HOSTNAME/idp.

# Downloading idp.xml file (as idp user)
#su idp
cd /opt/jetty/webapps/
wget https://www.eduid.cz/jetty/idp.xml

The content of /opt/jetty/webapps/idp.xml is available online and contains the following lines.

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
    <Set name="war">/opt/shibboleth-idp/war/idp.war</Set>
    <Set name="contextPath">/idp</Set>
    <Set name="extractWAR">false</Set>
    <Set name="copyWebDir">false</Set>
    <Set name="copyWebInf">true</Set>
    <Set name="tempDirectory">/opt/jetty/tmp</Set>
</Configure>

Now, Jetty is almost ready to run, however, Shibboleth IdP is not installed, so no idp.war is available and no SSL certificate has been configured to run Jetty via HTTPS protocol.

SSL certificate

We need to configure SSL certificate used by Jetty web server, so users are logging in using HTTPS. Jetty uses Java Keystore. It is essential to import not only the certificate, but the whole key chain.

In the following, cert.pem file contains the server's certificate and chain_TERENA_SSL_CA_2.pem file contains certificate chain. At first, certificate and chain have to be merged into one file.

# Merging the server certificate with the chain
cat cert.pem chain_TERENA_SSL_CA_3.pem >> jetty-cert.txt

Next, it is required to transform the file to PKCS #12 format. We will be asked for a password for the key (file key.pem) and then for a new password for the new file jetty-cert.pkcs12.

# Transforming the certificate to PKCS #12 format
openssl pkcs12 -export -inkey serverkey-XYZ.pem -in jetty-cert.txt -out jetty-cert.pkcs12

We will be asked for the private key (key.pem file) password and a new password (marked as #1) for jetty-cert.pkcs12 file which has to be typed twice.

# Terminal output
Enter pass phrase for key.pem:
Enter Export Password:
Verifying - Enter Export Password:

Now it is time to import the certificate in PKCS #12 format (jetty-cert.pkcs12 file) into Java keystore:

# Importing the certificate into keystore
/opt/jdk1.8.0_111/bin/keytool -importkeystore -srckeystore jetty-cert.pkcs12 -srcstoretype PKCS12 -destkeystore keystore

Firstly, we will be asked for a newly created keystore (Enter destination keystore password, marked as #2). Next, we will be asked to repeat that password (Re-enter new password). Secondly, we will be asked for the password (marked previously as #1) to the certificate (jetty-cert.pkcs12 file) to be imported to keystore (Enter source keystore password).

# Terminal output
Enter destination keystore password:  
Re-enter new password: 
Enter source keystore password:  
Entry for alias 1 successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled

When finished, move the keystore into Jetty.

# Moving the keystore to Jetty
mv keystore /opt/jetty/etc
chown idp:idp /opt/jetty/etc/keystore

To obtain obfuscated passwords for plain text configuration files, jetty-util from Jetty can be employed.

Obfuscated password (#2) for the keystore:

# Obfuscated password (#2) for the keystore
java -cp /opt/jetty-distribution-9.3.14.v20161028/lib/jetty-util-9.3.14.v20161028.jar \
    org.eclipse.jetty.util.security.Password <password_(2)_for_the_keystore>
# Terminal output
2015-06-16 15:56:58.986:INFO::main: Logging initialized @322ms
keystore
OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l
MD5:5fba3d2b004d68d3c5ca4e174024fc81

Obfuscated password (#1) for the certificate:

# Obfuscated password (#1) for the certificate
java -cp /opt/jetty-distribution-9.3.14.v20161028/lib/jetty-util-9.3.14.v20161028.jar \
    org.eclipse.jetty.util.security.Password <password_(1)_for_the_certificate>
# Terminal output
2015-06-16 15:57:02.322:INFO::main: Logging initialized @308ms
certificate
OBF:1sot1w1c1uvk1vo01unz1thb1unz1vn21uum1w261sox
MD5:e0d30cef5c6139275b58b525001b413c

Passwords are to be inserted into start.d/ssl.ini configuration file (jetty.sslContext.keyStorePassword is identical to jetty.sslContext.trustStorePassword):

# Entering obfuscated passwords #1 and #2 to Jetty configuration (as idp user)
su idp
vi /opt/jetty/start.d/ssl.ini
// Configuration changes in the file 'ssl.ini'
jetty.sslContext.keyStorePassword=OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l                # password (#2)
jetty.sslContext.keyManagerPassword=OBF:1sot1w1c1uvk1vo01unz1thb1unz1vn21uum1w261sox  # password (#1)
jetty.sslContext.trustStorePassword=OBF:1u9x1vn61z0p1yta1ytc1z051vnw1u9l              # password (#2)

Variables jetty.sslContext.keyStorePassword and jetty.sslContext.trustStorePassword have to be set to obfuscated password (#2) to the „keystore“. A variable jetty.sslContext.keyManagerPassword has to be se to obfuscated password (#1) to the key of certificate (jetty-cert.pkcs12 file). If you set it incorrectly, Jetty will not start.

SSL configuration

Default Jetty configuration allows usage of ciphers not trustworthy anymore, so we disable them. For this purpose, we download the prepared tweak-ssl.xml configuration file into /opt/jetty/etc directory.

# SSL tweaks for Jetty (as idp user)
#su idp
cd /opt/jetty/etc/
wget https://www.eduid.cz/_media/en/tech/idp/tweak-ssl.xml

The content of tweak-ssl.xml file is as follows.

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
 
<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
 
  <!-- Prohibit old and untrustworthy ciphers -->
  <Call name="addExcludeCipherSuites">
    <Arg>
      <Array type="String">
        <Item>.*DES.*</Item>
        <Item>.*DSS.*</Item>
        <Item>.*MD5.*</Item>
        <Item>.*NULL.*</Item>
        <Item>.*RC4.*</Item>
        <Item>.*_RSA_.*MD5$</Item>
        <Item>.*_RSA_.*SHA$</Item>
        <Item>.*_RSA_.*SHA1$</Item>
        <Item>TLS_DHE_RSA_WITH_AES_128.*</Item>
        <Item>TLS_DHE_RSA_WITH_AES_256.*</Item>
      </Array>
    </Arg>
  </Call>
 
  <!-- Prohibit untrustworthy protocols -->
  <Call name="addExcludeProtocols">
    <Arg>
     <Array type="java.lang.String">
       <Item>SSL</Item>
       <Item>SSLv2</Item>
       <Item>SSLv2Hello</Item>
       <Item>SSLv3</Item>
     </Array>
    </Arg>
  </Call>
 
  <!-- Enabling Forward Secrecy -->
  <Set name="IncludeCipherSuites">
    <Array type="String">
      <Item>TLS_DHE_RSA.*</Item>
      <Item>TLS_ECDHE.*</Item>
    </Array>
  </Set>
 
</Configure>

Now we just need Jetty to load this configuration file stating the following line into start.d/https.ini:

# Load tweak-ssl.xml into Jetty (as idp user -- then run EXIT command to become 'root' user again!)
echo etc/tweak-ssl.xml >> /opt/jetty/start.d/https.ini
exit

This configuration changes make Jetty more secure and trustworthy.

Starting Jetty

The second last thing before starting Jetty is make sure the file permissions to Jetty are set to idp user from idp group.

# Setting file permissions for Jetty and starting it (as root user)
chown -R idp:idp /opt/jetty/ /opt/jetty-distribution*/
/etc/init.d/jetty start

Jetty now runs so we check that it listens for unencrypted traffic (port 8080) only at localhost (IPv4 address 127.0.0.1) using netstat command:

# Checking for unencrypted listening at localhost
netstat -an | grep ":8080"

We should see the following.

# Terminal output
tcp6       0      0 127.0.0.1:8080          :::*                    LISTEN

If we now look into Jetty logs, we will see an error. And that this OK at this time. Shibboleth IdP is not installed yet, thus /opt/shibboleth-idp/war/idp.war file does not exist.

2015-08-05 09:02:22.871:WARN:oejw.WebInfConfiguration:main: Web application not found /opt/shibboleth-idp/war/idp.war
2015-08-05 09:02:22.872:WARN:oejw.WebAppContext:main: Failed startup of context o.e.j.w.WebAppContext@df27fae{/idp,null,null}{/opt/shibboleth-idp/war/idp.war}
java.io.FileNotFoundException: /opt/shibboleth-idp/war/idp.war
        at org.eclipse.jetty.webapp.WebInfConfiguration.unpack(WebInfConfiguration.java:495)
        at org.eclipse.jetty.webapp.WebInfConfiguration.preConfigure(WebInfConfiguration.java:72)
        at org.eclipse.jetty.webapp.WebAppContext.preConfigure(WebAppContext.java:474)
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:510)
...

Anyway, Jetty runs and we can check that it is serving our index.html file we have written previously. Try HTTPS access from your computer and HTTP access from the server's command line.

# HTTP access from the server's command line
wget -q -O - http://127.0.0.1

You should see the content of /opt/jetty/webapps/root/index.html file.

The last thing is to set Jetty to autorun after the system boots up.

# Autorun Jetty after system start ups (as root user)
insserv jetty

Now, as Jetty is installed and configured, it is time for Shibboleth IdP installation.

Last modified:: 2020/01/09 13:37