So you decide that the application is ready for prime time, it’s time to deploy them to production. Before that happens, let’s make sure everything is fine and dandy on the server side. Here a couple of steps that I usually do in securing tomcat deployment for production:

1. Secure the operating system

Runaway services

You can start by reviewing out services that are listening to incoming connection and which port they are using. See if something isn’t supposed to be there:

surfer@myoui:~$ sudo netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      328/systemd-resolve 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      4735/sshd           
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      18860/cupsd         
tcp6       0      0 :::8080                 :::*                    LISTEN      3058/java           
tcp6       0      0 :::22                   :::*                    LISTEN      4735/sshd
tcp6       0      0 :::8443                 :::*                    LISTEN      8152/java        
tcp6       0      0 ::1:631                 :::*                    LISTEN      18860/cupsd         
tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN      3058/java           
tcp6       0      0 :::8009                 :::*                    LISTEN      3058/java

We won’t be needing printing system, won’t we? Removing the service is as easy as

surfer@myoui:~$ sudo apt remove cups-daemon

And check whether the service is still listed or not

surfer@myoui:~$ sudo netstat -plnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      328/systemd-resolve 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      4735/sshd           
tcp6       0      0 :::8080                 :::*                    LISTEN      3058/java           
tcp6       0      0 :::22                   :::*                    LISTEN      4735/sshd 
tcp6       0      0 :::8443                 :::*                    LISTEN      8152/java
tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN      3058/java           
tcp6       0      0 :::8009                 :::*                    LISTEN      3058/java
Configuring the Firewall

…And make sure the host’s firewall is configured accordingly. Since we are on ubuntu, I’m going to use ufw. To check whether ufw is running, do

surfer@myoui:~$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
8443                       ALLOW       Anywhere                  
22                         ALLOW       Anywhere                  
8443 (v6)                  ALLOW       Anywhere (v6)             
22 (v6)                    ALLOW       Anywhere (v6)

If ufw is disabled, you can enable the service by executing

surfer@myoui:~$ sudo ufw enable

This will also configure ufw to start everytime the server is booted. As you can see from the output above, while tomcat is running on both 8080 and 8443, we will only expose port 8443 since client connection will be through SSL only. To improve the server’s security, we can also configure ssh service to listen on a custom ports, and setup fail2ban to prevent DDoS.  We can also configure the firewall to accept ssh connection only from specific IP or from internal network. My final firewall configuration kind of looks like this:

surfer@myoui:~$ sudo ufw status
[sudo] password for surfer:
Status: active

To Action From
-- ------ ----
8443 ALLOW Anywhere
2212 on enp0s3 ALLOW Anywhere
8443 (v6) ALLOW Anywhere (v6)
2212 (v6) on enp0s3 ALLOW Anywhere (v6)

SSH is connection can now only be established on a port that is facing internal network

2. Removing the default
Removing the default Front page

First, the default front page. This is how tomcat looks the first time we load them after installation:

At the very least, the front page shows which version of tomcat is installed on the server. The page above is served from CATALINA_HOME/webapps/ROOT. What we can do is to remove all pre-built app on CATALINA_HOME

surfer@myoui:/opt/tomcat/current/webapps$ ls
ROOT  docs  examples  host-manager  manager

What I do is to remove everything aside from ROOT, and put a replacement index.html file inside ROOT that redirect away all hits to the root directory, such as

<html>
     <head>
         <meta http-equiv="refresh" content="0; url=https://www.youtube.com/watch?v=mAKsZ26SabQ" />
     </head>
</html>
Removing the default error page

Tomcat’s default error page also shows the version of tomcat that is running on the server

To mitigate this issue, customize Tomcat’s Error Report Valve to omit server version from the error page. Open and make some change on

surfer@myoui:~$ nano /opt/tomcat/current/conf/server.xml

We need to add the configuration on <Host>. You can do that by going to the very end of the file and add the Error Report Valve configuration right before </Host>

   <Valve className="org.apache.catalina.valves.ErrorReportValve"
          showReport="false" 
          showServerInfo="false" />
</Host>

Save, and restart the service. The newly configured error page should now look like this:

3. Poking around the service

There are a lot of online resource that we can use to test out our configuration. Below are two that I use the most, a long with what to do to fix stuff they found on our server.

Immuniweb

Immuniweb rates how good our ssl configuration is based on several standard, including PCI-DSS compliance, HIPAA guideline, and other best practices. There a couple of stuff that

  1. Supported TLS versions. PCI-DSS has taken out support for TLS v1.0
  2. Preferred chiper. It’s a common best practice to configure TLS service to offer preferred chiper, to ensure the encryption is used.
GeekFlare tools

GeekFlare provides a couple of tools to test out our configuration, such as

  1. HTTP Strict Transport Security (HSTS) Header checker
  2. Cross Site Scripting checker
  3. Clickjacking attack
  4. MIME Sniffing test

..and a couple of other tests

4. Closing up those holes

After you’ve finished poking around the service that you’ve just recently erected, now is the time to close all of those holes you just discovered. Let’s start with the first config file

CATALINA_HOME/conf/server.xml

<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig protocols="TLSv1.1+TLSv1.2"
ciphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"
honorCipherOrder="true">
<Certificate certificateKeyFile="conf/privkey.pem"
certificateFile="conf/cert.pem"
certificateChainFile="conf/chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>

The configuration above will set the server to

  • Remove support for TLS v1.0, and only support TLS v1.1 and v1.2, and
  • Offer preferred cipher

CATALINA_HOME/conf/web.xml

<filter>
<filter-name>httpHeaderSecurity</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>hstsMaxAgeSeconds</param-name>
<param-value>6592000</param-value>
</init-param>
<init-param>
<param-name>antiClickJackingEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>antiClickJackingOption</param-name>
<param-value>DENY</param-value>
</init-param>
<init-param>
<param-name>xssProtectionEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>blockContentTypeSniffingEnabled</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>httpHeaderSecurity</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

The configuration above covers the HTTP Header configuration for:

  • HTTP Strict Transport Security (HSTS)
  • Anti Clickjacking, and
  • Cross Site Scripting (XSS) Protection

Restart the service and see if you can pass those tests now.

Do you have anything to add? Drop a comment!

 

By ikhsan

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.