Tomcat Security Basics

Introduction

A fresh Tomcat install includes sample applications, open Manager tools, default ports, and readable version hints—fine for localhost learning, not for a public VPS. This chapter hardens a standalone Tomcat instance: remove unused apps, restrict Manager, run as a non-root user, bind HTTP to localhost behind Nginx, and apply basic file permissions—without repeating full Spring Security (that lives in your WAR or Spring Boot track).

Prerequisites

Threat Model (Learning vs Production)

RiskLearning on laptopProduction VPS
Default examples appLowRemove—known attack surface
Manager on public internetLow if localhost onlyCritical if exposed
Tomcat on 0.0.0.0
ConvenientHide behind Nginx; firewall
Running as rootSometimes happensNever

This chapter targets a single VPS running your WAR + Nginx—common Hello Code deploy pattern.

Remove or Disable Sample Applications

Default webapps/ may include:

AppPathAction
docs/docsRemove for production
examples/examplesRemove—demos with vulnerabilities history
manager/managerRemove or restrict heavily
host-manager/host-managerRemove unless needed
ROOT/Replace with your app only

Stop Tomcat, then:

bash
$CATALINA_HOME/bin/shutdown.sh
rm -rf $CATALINA_HOME/webapps/docs \
       $CATALINA_HOME/webapps/examples \
       $CATALINA_HOME/webapps/manager \
       $CATALINA_HOME/webapps/host-manager
rm -f $CATALINA_HOME/webapps/docs.war \
      $CATALINA_HOME/webapps/examples.war \
      $CATALINA_HOME/webapps/manager.war \
      $CATALINA_HOME/webapps/host-manager.war
$CATALINA_HOME/bin/startup.sh

Verify:

bash
curl -I http://127.0.0.1:8080/examples/    # expect 404

Warning

Keep Manager Only If You Need It

If you use Manager for deploy, keep it localhost-only (next section)—do not expose on 0.0.0.0 without strong auth and IP rules.

Lock Down Manager (If Retained)

Strong users in tomcat-users.xml

xml
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<user username="deploy_admin"
      password="{SHA-512}{salt}{hash}"
      roles="manager-gui,manager-script"/>

Generate hash:

bash
$CATALINA_HOME/bin/digest.sh -a SHA-512 -s 0 YourLongRandomPassword

Use the output format Tomcat prints (may include salt parameters in Tomcat 10+).

Restrict by IP in Manager context

Edit webapps/manager/META-INF/context.xml (or overlay in your deploy process):

xml
<Context antiResourceLocking="false" privileged="true">
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.0\.0\.1|10\.0\.0\.100" />
</Context>

Code explanation:

  • RemoteAddrValve — allow only listed IPs (regex)
  • Adjust allow for your admin VPN or bastion IP
  • Prefer SSH tunnel to Manager: ssh -L 8080:127.0.0.1:8080 user@vps then open http://127.0.0.1:8080/manager/html locally

Disable unused Manager roles

Do not grant admin-gui / admin-script unless required—Host Manager can alter virtual hosts globally.

Run as a Dedicated User (Linux)

Create user (if not done at install):

bash
sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
sudo chown -R tomcat:tomcat /opt/apache-tomcat-10.1.34

systemd unit from Start, Stop, and Environment:

ini
User=tomcat
Group=tomcat

Code explanation:

  • Compromised Servlet should not write to /etc or install system packages
  • webapps/, logs/, work/, temp/ must be writable by tomcat

Never:

bash
sudo $CATALINA_HOME/bin/startup.sh   # avoid routine root start

Bind HTTP to Localhost

In conf/server.xml:

xml
<Connector port="8080" protocol="HTTP/1.1"
           address="127.0.0.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Pair with Nginx on 80/443 facing the world—see Nginx reverse proxy Tomcat.

Firewall (Ubuntu ufw example):

bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8080/tcp
sudo ufw enable

Cloud security groups: allow 443, block 8080 from 0.0.0.0/0.

Hide Version Information (Optional)

Reduce fingerprinting in HTTP Server header—set on Connector:

xml
<Connector port="8080" protocol="HTTP/1.1"
           address="127.0.0.1"
           server="WebServer" />

Code explanation:

  • Does not remove all fingerprint vectors—still keep apps patched
  • Error pages should not leak stack traces to clients in production

Shutdown Port and Connectors

  • Keep shutdown port 8005 on localhost only (default bind is often localhost—verify)
  • Remove or comment unused AJP Connector (8009) if not required—reduces attack surface
  • Do not expose 8443 unless you actively terminate TLS on Tomcat

File Permissions

Minimal Linux guidance:

bash
# Config: tomcat can read, admins write via deploy user in group
chmod 640 $CATALINA_HOME/conf/server.xml
chown root:tomcat $CATALINA_HOME/conf/server.xml
 
# Logs and runtime dirs
chown -R tomcat:tomcat $CATALINA_HOME/logs $CATALINA_HOME/work $CATALINA_HOME/temp
chown -R tomcat:tomcat $CATALINA_HOME/webapps

Do not store secrets in world-readable files under conf/.

HTTPS: Prefer Nginx Termination

Tomcat SSL on 8443 with self-signed or manual keystore is valid but operational overhead (renewal, cipher tuning).

Recommended:

text
Client --HTTPS--> Nginx --HTTP--> Tomcat 127.0.0.1:8080

Follow Nginx HTTPS and Let's Encrypt. Tomcat sees plain HTTP on loopback—acceptable when the network path is trusted.

Application-Level Security (Brief)

Container hardening does not replace:

  • Authentication in your WAR (Spring Security, etc.)
  • Input validation, SQL injection prevention
  • Session cookie flags (HttpOnly, Secure, SameSite)
  • Dependency updates (Log4j, Spring CVEs)

Standalone Servlet apps: use Filter for auth or migrate critical APIs to Spring Boot with Spring Security.

Security Checklist

  • examples, docs, unused manager removed or restricted
  • tomcat-users.xml strong passwords; no default admin/admin
  • Tomcat runs as tomcat user, not root
  • Connector address="127.0.0.1" when Nginx fronts the server
  • Firewall blocks public 8080
  • Access logs enabled (chapter 10)
  • WAR apps updated; no secrets in Git or webapps/

What Comes Next

ChapterTopic
12 — Nginx + TomcatProduction edge setup
20 — TroubleshootingIncident response
Spring Boot Web SecurityApp-layer auth (when using Boot)

FAQ

Is Tomcat 10 secure out of the box?

It is safe for local learning with defaults. Public deploy requires hardening in this chapter.

Delete ROOT welcome page?

Replace ROOT.war with your frontend or an empty redirect—default Apache Tomcat page advertises the stack.

Manager over HTTPS only?

If Manager must exist, put it behind Nginx with TLS + IP allow list + strong auth—or SSH tunnel only.

SELinux on RHEL?

Custom docBase paths need correct context (tomcat_exec_t, etc.)—check audit.log if deploy fails after enforcing.

Security scanner flags Tomcat version?

Patch Tomcat minor releases; hide Server header; reduce exposed apps.

Same security for Docker Tomcat?

Mount read-only conf, non-root user in image, publish 8080 only on internal network—not 0.0.0.0 on host without firewall.