In this blog, we look at eight ways to improve your Apache Tomcat security hardening, ranging from basic best practices like not running your Tomcat as the root user, to more advanced tips like using realms to control resource access. At the end of the blog, we’ll wrap up with some final thoughts about how to secure Tomcat and then link to some related resources you should check out. Let’s dive in!
Apache Tomcat is a robust application server that includes many features available right out of the box. However, just because these features and settings are available right away doesn’t mean that your Tomcat server is ready for production. Before you go to production, you need to perform thorough tuning and security hardening to ensure your Tomcat server is secure.
There are many ways to improve Apache Tomcat security, and this blog is no replacement for a thorough dive into the possible ways in which you can do so. However, the tips below are a good starting point for people interested in hardening their Tomcat server deployment.
5. Enable TLS
A critical step in hardening your configuration is setting up end-to-end encryption between the browser and the application server. The first step is creating a keystore using the JDK’s keytool:
keytool -genkey -alias openlogic -keyalg RSA -keysize 2048 -keystore keystore.jks
keytool will ask a series of questions. The most important question is “What is your first and last name?” This should be set to the domain name the server will sit behind and not your first and last name. The question should be reworded to: “What is your CN (Common Name)?” This means the domain which your server will be known by. The output of the keytool should look like the following:
Enter keystore password: changeit
Re-enter new password: changeit
Enter the distinguished name. Provide a single dot (.) to leave a sub-component empty or press ENTER to use the default value in braces.
What is your first and last name?
[Unknown]: openlogic.com
What is the name of your organizational unit?
[Unknown]: OpenLogic
What is the name of your organization?
[Unknown]: Perforce
What is the name of your City or Locality?
[Unknown]: Minneapolis
What is the name of your State or Province?
[Unknown]: MN
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=openlogic.com, OU=OpenLogic, O=Perforce, L=Minneapolis, ST=MN, C=US correct?
[no]: yes
Generating 2,048 bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 90 days
for: CN=openlogic.com, OU=OpenLogic, O=Perforce, L=Minneapolis, ST=MN, C=US
This command will create a keystore.jks in the directory keytool was run from.
A certificate signing request (CSR) will need to be generated from the keystore.jks and sent to a trusted certificate authority if you want the certificate to be trusted by the browser. This step is optional if you are testing. The traffic will still be encrypted, but you will receive a “not trusted” message from the browser.
To generate a CSR run:
keytool -genkey -alias openlogic -keyalg RSA -file openlogic.csr -keystore keystore.jks
Then send openlogic.csr to a trusted certificate authority for signing. We will not cover the steps here, but the certificate authority will send you back a certificate to import into your keystore.jks.
There are certificate authorities which will send you a free 90-day signed certificate for free as long as you are the domain owner. They will require you to import their root, intermediate, and your signed domain certificate into keystore.jks.
First import the root certificate:
keytool -importcert -alias root -file root.cer -keystore keystore.jks
Then import the intermediate certificate:
keytool -importcert -alias intermediate -file intermediate.cer -keystore keystore.jks
Last, import your signed domain certificate:
keytool -importcert -alias openlogic -file openlogic.cer -keystore keystore.jks
You cannot only import your signed certificate because the browser also needs the root and any intermediate certificates to trust the domain certificate.
The next step is configuring your server.xml to listen on a trusted secure port by presenting a valid certificate and end-to-end encryption. The syntax assumes Tomcat 9.0+; versions of Tomcat prior to 9.0 require a different syntax which we will not cover here.
Create the following snippet of XML in Tomcat’s conf/server.xml:
…
<Server port=”8005″ shutdown=”SHUTDOWN”>
…
<Service name=”Catalina”>
…
<Connector port=”8443″
protocol=”org.apache.coyote.http11.Http11NioProtocol”
maxThreads=”150″ SSLEnabled=”true”>
<UpgradeProtocol className=”org.apache.coyote.http2.Http2Protocol” />
<SSLHostConfig>
<Certificate certificateKeystoreFile=”conf/keystore.jks”
certificateKeystorePassword=”changeit”
type=”RSA”
/>
</SSLHostConfig>
</Connector>
…
</Service>
</Server>
This assumes the keystore.jks is in Tomcat’s conf directory.
The configuration changes up to this point do not force plain-text port 8080 to redirect to 8443. To enable this functionality, modify Tomcat’s conf/web.xml by adding the following XML snippet:
<web-app…>
…
<security-constraint>
<web-resource-collection>
<web-resource-name>everything</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
</web-app>
By modifying Tomcat’s conf/web.xml with this change, this tells the application server that you want all unencrypted traffic to be handled by an encrypted port. Restart Tomcat for the configuration changes to take effect. Then go to http://localhost:8080.
If you did not send the CSR from the earlier step to a trusted certificate authority, then you may receive some warnings from the browser. Tomcat will then redirect the browser to https://localhost:8443.
The server I tested with is Apache Tomcat 11 with OpenJDK 21.0.4. After running a protocol test, the server was found to support TLS 1.2 and 1.3 with no support of outdated protocols SSLv3, TLS v1.0 and 1.1 (which is desired due to vulnerabilities).
6. Log Your Network Traffic
To enable logging of network traffic in Tomcat, use the AccessLogValve component. This can be configured on a host, engine, or context basis and will create a standard web server log file for traffic to any resources associated with it.
The Access Log Valve supports a variety of attributes to control the output of the valve. This valve is enabled by default in server.xml:
…
<Host name=”localhost”…
<Valve className=”org.apache.catalina.valves.AccessLogValve” directory=”logs”prefix=”localhost_access_log” suffix=”.txt”
pattern=”%h %l %u %t "%r" %s %b” />
</Host>
…
This valve creates a daily rotating localhost_access_log.yyyy-mm-dd.txt file in Tomcat’s log directory. With the pattern configured in the statement above, the valve will print the remote host (%h), username (%l), date and time (%t), first line of the request (%r), HTTP status of the response (%s), and bytes sent (%b) of every request.
The following output results when the root page is accessed:
35.139.184.195 – – [30/Jul/2024:21:05:18 +0000] “GET / HTTP/2.0” 200 11223
The pattern can be customized in numerous permutations; see Tomcat 11 documentation for details.
Be careful in using this valve as it can put write pressure on the disk if the application server is busy.
7. Limit Access to the Tomcat Manager App
The Tomcat Manager application is a built-in webapp used to manage Tomcat instances, application deployment and other various settings. By default, the Manager application can only be accessed from the machine it is running on or an address in the 127.0.0.0 subnet range using IPv4 or the IPv6 loopback (::1 or 0:0:0:0:0:0:0:1), and this is configured in the META-INF/context.xml using the Remote Address Valve:
<Valve className=”org.apache.catalina.valves.RemoteAddrValve”
allow=”127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1″ />
If there are specific IP addresses you want to allow, then use the following syntax:
<Valve className=”org.apache.catalina.valves.RemoteAddrValve”
allow=”192.168.1.2|192.168.1.3″ deny=”” />
This configuration allows access into the application if your IP address is either 192.168.1.2 or 192.168.1.3.
The Remote Address Valve also has a deny attribute which is used if there are any specific addresses separated by commas that you want to blacklist.
This valve can be used in any application that is deployed on Tomcat.
If a range of addresses is preferred to limit access, then use the Remote CIDR Valve in META-INF/context.xml:
<Valve className=”org.apache.catalina.valves.RemoteCIDRValve”
allow=”127.0.0.1, 192.168.1.0/24″ deny=”” />
This allows access from the loopback address as well as any addresses in the 192.168.1.0 subnet range.
8. Use Realms to Control Resource Access
Realms are another method of controlling authentication and authorization to resources in Tomcat. A realm is a collection of users and roles that are assigned access to a given application or group of applications and the privileges they have within the application once logged in.
There are four built-in manager roles:
- manager-gui: HTML GUI and the status pages
- manager-script: HTTP API and the status pages
- manager-jmx: JMX proxy and the status pages
- manager-status: Status pages only
Realms are pluggable. Realms can be configured to connect to a relational database, LDAP, JAAS, a global JNDI resource (such as an XML file), or a combination of realms.
The LockOut Realm is the default in Tomcat which uses the conf/tomcat-users.xml file to control authentication and authorization.The role and users are by default commented out, but a simple example with one user with the manager-gui role would look like the following:
<tomcat-users>
<role rolename=”manager-gui”/>
<user username=”tomcat” password=”changeme” roles=”manager-gui”/>
</tomcat-users>
The LockOut realm by default will cause a user to be locked out for five minutes if the password is guessed incorrectly five times which will be displayed in the catalina.out log file:
05-Aug-2024 21:29:39.980 WARNING [https-jsse-nio-8443-exec-4] org.apache.catalina.realm.LockOutRealm.filterLockedAccounts An attempt was made to authenticate the locked user [tomcat]
In addition, the plain-text passwords in tomcat-users.xml can be encrypted. In server.xml, find the UserDatabaseRealm and change it to:
<Realm className=”org.apache.catalina.realm.LockOutRealm”>
<Realm className=”org.apache.catalina.realm.UserDatabaseRealm”
resourceName=”UserDatabase”>
<CredentialHandler className=
“org.apache.cataline.realm.MessageDigestCredentialHandler” algorithm=”SHA-256″/>
</Realm>
Any changes to server.xml require a server restart. Modifications to tomcat-users.xml do not necessitate a server restart as this file is monitored for changes.
Generate a hash from a plain-text password:
${TOMCAT_HOME}/bin/digest.sh -a SHA-256 -h org.apache.catalina.realm.MessageDigestCredentialHandler changeme
The “-a” is for the algorithm to be used when encrypting the password. Any algorithm available to the JDK can be used such as SHA-512.
The hash of the password will be displayed after the colon:
changeme:5d56e72f51f7ec5a0bd724e026fa2856ce7f8821358c0f854b3 e18bf20780960$1$5979cdb240050fbb72ad6ed1f69ac8d161634ea91e3f f52e83176fb44fc1562f
Place the hash in the tomcat-users.xml for the particular user:
<tomcat-users>
<role rolename=”manager-gui”/>
<user username=”tomcat” password=”5d56e72f51f7ec5a0bd724e026fa2856ce7f8821358c0f854b 3e18bf20780960$1$5979cdb240050fbb72ad6ed1f69ac8d161634ea91e3 ff52e83176fb44fc1562f” roles=”manager-gui”/>
</tomcat-users>
Keep in mind that all passwords must be hashed in tomcat-users.xml if the MessageDigestCredentialHandler is used.
Tomcat should detect the file changed without a restart:
05-Aug-2024 21:26:22.987 INFO [Catalina-utility-2] org.apache.catalina.users.MemoryUserDatabase.backgroundProcess Reloading memory user database [UserDatabase] from updated source [file:/home/rocky/apache-tomcat-11.0.0/conf/tomcat-users.xml]
Lastly, file access to Tomcat’s conf should be limited to the account running Tomcat.
Final Thoughts
While these are some of the many ways you can secure Tomcat, there are still plenty of other things out there that can be done which go beyond the scope of just a blog article. We encourage all our Tomcat users to take a deep dive approach to Tomcat security, utilizing all the resources out there.