Published on Feb 1 2018 in Java Tomcat

This tutorial is about how to get Apache Tomcat with APR secured with free 'A' grade SSL as per Qualys ssllabs test. It should not take you more than 5 minutes in a clean Centos 7 VPS.

In this copy and paste tutorial we will use CentOS Linux release 7.4.1708 (minimal install) with public IP, Apache Tomcat 8.0.48, Oracle JDK 1.8.0_161 and FQDN hostname resolvable to your server's public IP.

First make sure we use latest software

yum -y upgrade

Secure the host with firewall

Open only ports 80, 443 and SSH port of your choice.

SSHPORT=paste_your_cutom_ssh_port_here
sed -i "s/^#Port 22/Port ${SSHPORT}/" /etc/ssh/sshd_config
systemctl restart sshd

yum -y install mc iptables unzip firewalld
systemctl enable firewalld && systemctl start firewalld
firewall-cmd --zone=public --add-port=${SSHPORT}/tcp --permanent
firewall-cmd --zone=public --remove-port=22/tcp --permanent
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-ports

Install Tomcat and JDK

Tomcat will run under regular user and use privileged ports 80 and 443. Another option would be to redirect ports 80 and 443 to default Tomcat ports 8080 and 8443 with iptables. In this tutorial we use Linux capabilities to allow for binding to low ports.

cd /opt && curl -C - -L -O -s -S -H "Cookie: oraclelicense=accept-securebackup-cookie" \
"http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz"
tar xzf jdk-8u161-linux-x64.tar.gz
ln -s jdk1.8.0_161 jdk

export TOMCAT_USER=tomcat
adduser $TOMCAT_USER
su - $TOMCAT_USER
wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.0.48/bin/apache-tomcat-8.0.48.tar.gz
tar xzf apache-tomcat-8.0.48.tar.gz && rm -f apache-tomcat-8.0.48.tar.gz
ln -s apache-tomcat-8.0.48 tomcat

cat >> ~/.bashrc <<EOF
export JAVA_HOME=/opt/jdk
export JRE_HOME=\$JAVA_HOME
export JAVA_OPTS="-Xms128m -Xmx256m -XX:MaxMetaspaceSize=128m -Djava.awt.headless=true -Dfile.encoding=UTF-8"
export CATALINA_OPTS="\$CATALINA_OPTS -Djava.library.path=/opt/native"
export CATALINA_HOME=\$HOME/tomcat
export PATH=/opt/jdk/bin:\$HOME/tomcat/bin:\$PATH
EOF
exit

Install APR library for Tomcat (tomcat-native)

APR can be used with both HTTP and HTTPS connectors.

yum -y install gcc make openssl-devel apr-devel
cd /opt
cp /home/tomcat/tomcat/bin/tomcat-native.tar.gz .
tar xzf tomcat-native.tar.gz
cd /opt/tomcat-native-1.2.16-src/native/
JAVA_HOME=/opt/jdk ./configure --with-apr=/usr/bin/apr-1-config
make && make install
ln -s /usr/local/apr/lib /opt/native

Allow Java to bind privileged ports (<1024) instead of using port forwarding or high (default) ports

setcap cap_net_bind_service=+ep `readlink -f \`which java\``
echo $(dirname `find /opt/jdk/jre -name libjli.so`) > /etc/ld.so.conf.d/java_setcap.conf
ldconfig

Get free Letsencrypt certificate for use with the Tomcat

Update your contact email below.

wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
yum-config-manager --disable epel
yum --enablerepo=epel install certbot -y
certbot certonly --standalone --preferred-challenges http -d $(hostname -f) -m your.contact@email --agree-tos -n 

You should get "Congratulations! Your certificate and chain have been saved at: PATH TO YOUR PEMS" message.

Install haveged to provide more entropy for SSL routines

yum -y install --enablerepo=epel haveged rng-tools
systemctl enable haveged
systemctl start haveged
cat /dev/random | rngtest -c 1000 2>&1 | grep successes

Expect 99-1000 sucesses. Without haveged Tomcat bootup was taking a 200 seconds. With haveged it now takes 0.5 sec.

Setup HTTPS APR connector in Tomcat's server.xml

We will change default HTTP port 8080 to 80 as well as insert Connector with port 443.

sed -i.bak 's/<Connector port="8080" protocol="HTTP\/1.1"/<Connector port="80" protocol="org.apache.coyote.http11.Http11AprProtocol"/' /home/${TOMCAT_USER}/tomcat/conf/server.xml

Insert the following uncommented Connector nearby existing (commented out by default) Connector for HTTPS.

<Connector port="443" scheme="https" secure="true" SSLEnabled="true" URIEncoding="UTF-8" protocol="org.apache.coyote.http11.Http11AprProtocol"
      SSLCertificateFile="${catalina.base}/cert.pem"
      SSLCertificateKeyFile="${catalina.base}/privkey.pem"
      SSLPassword=""
      SSLVerifyClient="none"
      SSLCertificateChainFile="${catalina.base}/chain.pem"
      SSLProtocol="all -SSLv2 -SSLv3"
      SSLHonorCipherOrder="on"
      SSLCipherSuite="EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"
      compression="on"
      compressableMimeTypes="application/json,text/html,text/css,text/plain,application/javascript" />

Copy your certificates so that they are readable by Tomcat.

cp -v /etc/letsencrypt/live/$(hostname -f)/*.pem /home/${TOMCAT_USER}/tomcat
chown ${TOMCAT_USER}: /home/${TOMCAT_USER}/tomcat/*.pem

Start Tomcat and verify its startup result

su - tomcat -c "startup.sh"
cat /home/${TOMCAT_USER}/tomcat/logs/catalina.out

Qualys SSLLabs verification

SSLLabs result for Tomcat APR

The test was performed on Feb 1, 2018. Things could change since then.