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
- Installing Tomcat and Directory Layout
- Logging and Access Log — audit Manager activity
- web.xml and context.xml —
tomcat-users.xml
Threat Model (Learning vs Production)
| Risk | Learning on laptop | Production VPS |
|---|---|---|
| Default examples app | Low | Remove—known attack surface |
| Manager on public internet | Low if localhost only | Critical if exposed |
| Tomcat on 0.0.0.0 | Convenient | Hide behind Nginx; firewall |
| Running as root | Sometimes happens | Never |
This chapter targets a single VPS running your WAR + Nginx—common Hello Code deploy pattern.
Remove or Disable Sample Applications
Default webapps/ may include:
| App | Path | Action |
|---|---|---|
| docs | /docs | Remove for production |
| examples | /examples | Remove—demos with vulnerabilities history |
| manager | /manager | Remove or restrict heavily |
| host-manager | /host-manager | Remove unless needed |
| ROOT | / | Replace with your app only |
Stop Tomcat, then:
$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.shVerify:
curl -I http://127.0.0.1:8080/examples/ # expect 404Warning
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
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<user username="deploy_admin"
password="{SHA-512}{salt}{hash}"
roles="manager-gui,manager-script"/>Generate hash:
$CATALINA_HOME/bin/digest.sh -a SHA-512 -s 0 YourLongRandomPasswordUse 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):
<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
allowfor your admin VPN or bastion IP - Prefer SSH tunnel to Manager:
ssh -L 8080:127.0.0.1:8080 user@vpsthen openhttp://127.0.0.1:8080/manager/htmllocally
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):
sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
sudo chown -R tomcat:tomcat /opt/apache-tomcat-10.1.34systemd unit from Start, Stop, and Environment:
User=tomcat
Group=tomcatCode explanation:
- Compromised Servlet should not write to
/etcor install system packages webapps/,logs/,work/,temp/must be writable bytomcat
Never:
sudo $CATALINA_HOME/bin/startup.sh # avoid routine root startBind HTTP to Localhost
In conf/server.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):
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8080/tcp
sudo ufw enableCloud 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:
<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:
# 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/webappsDo 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:
Client --HTTPS--> Nginx --HTTP--> Tomcat 127.0.0.1:8080Follow 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.xmlstrong passwords; no defaultadmin/admin - Tomcat runs as
tomcatuser, 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
| Chapter | Topic |
|---|---|
| 12 — Nginx + Tomcat | Production edge setup |
| 20 — Troubleshooting | Incident response |
| Spring Boot Web Security | App-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.