All Projects → Unidata → tomcat-docker

Unidata / tomcat-docker

Licence: BSD-3-Clause license
Security-hardened Tomcat container for thredds-docker and ramadda-docker

Programming Languages

Dockerfile
14818 projects
shell
77523 projects

Unidata Tomcat Docker

This repository contains files necessary to build and run a security hardened Tomcat Docker container, based off of the canonical Tomcat base image. The Unidata Tomcat Docker images associated with this repository are available on Docker Hub. All default web application have been expunged from this container so it will primarily serve a base image for other containers.

Versions

  • unidata/tomcat-docker:latest (inherits tomcat:8.5-jdk8-openjdk)
  • unidata/tomcat-docker:8.5 (inherits tomcat:8.5-jdk8-openjdk)
  • unidata/tomcat-docker:8.5-jdk11-openjdk (inherits tomcat:8.5-jdk11-openjdk)

Security Hardening Measures

This Tomcat container was security hardened according to OWASP recommendations. Specifically,

  • Eliminated default Tomcat web applications
  • Run Tomcat with unprivileged user tomcat (via entrypoint.sh)
  • Start Tomcat via Tomcat Security Manager (via entrypoint.sh)
  • All files in CATALINA_HOME are owned by user tomcat (via entrypoint.sh)
  • Files in CATALINA_HOME/conf are read only (400) by user tomcat (via entrypoint.sh)
  • Container-wide umask of 007

web.xml Enhancements

The following changes have been made to web.xml from the out-of-the-box version:

  • Added SAMEORIGIN anti-clickjacking option
  • HTTP header security filter (httpHeaderSecurity) uncommented/enabled
  • Cross-origin resource sharing (CORS) filtering (CorsFilter) added/enabled (see below to disable)
  • Stack traces are not returned to user through error-page element.

CORS

This image enables the Apache Tomcat CORS filter by default. To disable it (maybe you want to handle CORS uniformly in a proxying webserver?), set environment variable DISABLE_CORS to 1.

server.xml Enhancements

The following changes have been made to server.xml from the out-of-the-box version:

  • Server version information is obscured to user via server attribute for all Connector elements
  • secure attribute set to true for all Connector elements
  • Shutdown port disabled
  • Digested passwords. See next section.

The active Connector has relaxedPathChars and relaxedQueryChars attributes. This change may not be optimal for security, but must be done to accommodate DAP requests which THREDDS and RAMADDA must perform.

Digested Passwords

This container has a UserDatabaseRealm, Realm element in server.xml with a default CredentialHandler algorithm of sha-512. This modification is an improvement over the clear text password default that comes with the parent container (tomcat:8.5-jre8). Passwords defined in tomcat-users.xml must use digested passwords in the password attributes of the user elements. Generating a digested password is simple. Here is an example for the sha-512 digest algorithm:

docker run tomcat  /usr/local/tomcat/bin/digest.sh -a "sha-512" mysupersecretpassword

This command will yield something like:

mysupersecretpassword:94e334bc71163a69f2e984e73741f610e083a8e11764ee3e396f6935c3911f49$1$a5530e17501f83a60286f6363a8647a277c9cfdb

The hash after the : is what you will use for the password attribute in tomcat-users.xml.

More information about this topic is available in the Tomcat documentation.

HTTPS

This Tomcat container can support HTTPS for either self-signed certificates which can be useful for experimentation or certificates from a CA for a production server. For a complete treatment on this topic, see https://tomcat.apache.org/tomcat-8.5-doc/ssl-howto.html.

Self-signed Certificates

This Tomcat container can support HTTP over SSL. For example, generate a self-signed certificate with openssl (or better yet, obtain a real certificate from a certificate authority):

openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj \
    "/C=US/ST=Colorado/L=Boulder/O=Unidata/CN=tomcat.example.com" -keyout \
    ./ssl.key -out ./ssl.crt

Then augment the server.xml from this repository with this additional XML snippet for Tomcat SSL capability:

<Connector port="8443"
       maxThreads="150"
       enableLookups="false"
       disableUploadTimeout="true"
       acceptCount="100"
       scheme="https"
       secure="true"
       SSLEnabled="true"
       SSLCertificateFile="${catalina.base}/conf/ssl.crt"
       SSLCertificateKeyFile="${catalina.base}/conf/ssl.key" />

Mount over the existing server.xml and add the SSL certificate and private key with:

docker run -it -d  -p 80:8080 -p 443:8443 \
    -v /path/to/server.xml:/usr/local/tomcat/conf/server.xml \
    -v /path/to/ssl.crt:/usr/local/tomcat/conf/ssl.crt \
    -v /path/to/ssl.key:/usr/local/tomcat/conf/ssl.key \
    unidata/tomcat-docker:8

or if using docker-compose the docker-compose.yml will look like:

unidata-tomcat:
  image: unidata/tomcat-docker:8
  ports:
    - "80:8080"
    - "443:8443"
  volumes:
    - /path/to/ssl.crt:/usr/local/tomcat/conf/ssl.crt
    - /path/to/ssl.key:/usr/local/tomcat/conf/ssl.key
    - /path/to/server.xml:/usr/local/tomcat/conf/server.xml

Certificate from CA

First, obtain a certificate from a certificate authority (CA). This process will yield a .key and .crt file. To meet enhanced security guidelines you, will want serve a certificate with the intermediate and root certificates present in the ssl.crt file. For Tomcat to serve the certificate chain, you have to put your .key and .crt (containing the intermediate and root certificates) in a Java keystore. The Keystore Explorer tool is a helpful app to assist you in building a valid certificate chain as well as exploring Java keystores.

First put the .key and .crt in a .p12 file:

openssl pkcs12 -export -in ssl.crt.fullchain -inkey ssl.key -out ssl.p12 -name \
    mydomain.com

Then add the .p12 file to the keystore:

keytool -importkeystore -destkeystore keystore.jks -srckeystore ssl.p12 \
    -srcstoretype PKCS12

When prompted for passwords in the two steps above, consider reusing the same password to reduce cognitive load. If you see the following message

Warning: The JKS keystore uses a proprietary format. It is recommended to
migrate to PKCS12 which is an industry standard format using "keytool
-importkeystore -srckeystore keystore.jks -destkeystore keystore.jks
-deststoretype pkcs12".

ignore it.

You'll then refer to that keystore in your server.xml:

<Connector port="8443"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           clientAuth="false"
           sslProtocol="TLSv1.2, TLSv1.3"
           ciphers="ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,DHE-RSA-AES128-GCM-SHA256,DHE-RSA-AES256-GCM-SHA384"
           maxThreads="150"
           enableLookups="false"
           disableUploadTimeout="true"
           acceptCount="100"
           scheme="https"
           secure="true"
           SSLEnabled="true"
           keystoreFile="${catalina.base}/conf/keystore.jks"
           keyAlias="mydomain.com"
           keystorePass="xxxx"
           />

Note there are a few differences with the Connector described for the self-signed certificate above. These additions are made according to enhanced security guidelines.

Mount over the existing server.xml and add the SSL certificate and private key with:

docker run -it -d  -p 80:8080 -p 443:8443 \
    -v /path/to/server.xml:/usr/local/tomcat/conf/server.xml \
    -v /path/to/ssl.jks:/usr/local/tomcat/conf/ssl.jks \
    unidata/tomcat-docker:8

or if using docker-compose the docker-compose.yml will look like:

unidata-tomcat:
  image: unidata/tomcat-docker:8
  ports:
    - "80:8080"
    - "443:8443"
  volumes:
    - /path/to/ssl.jks:/usr/local/tomcat/conf/ssl.jks
    - /path/to/server.xml:/usr/local/tomcat/conf/server.xml

Force HTTPS

Once you have your certificates in order, make HTTPS mandatory. Add this snippet as the final element in web.xml. Mount over the web.xml inside the container with this enhanced web.xml in the same manner we have been doing to server.xml as discussed herein.

<!-- Force HTTPS, required for HTTP redirect! -->
<security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected Context</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Configurable Tomcat UID and GID

The problem with mounted Docker volumes and UID/DIG mismatch headaches is best explained here: https://denibertovic.com/posts/handling-permissions-with-docker-volumes/.

This container allows the possibility of controlling the UID/GID of the tomcat user inside the container via TOMCAT_USER_ID and TOMCAT_GROUP_ID environment variables. If not set, the default UID/GID is 1000/1000. For example,

docker run --name tomcat \
     -e TOMCAT_USER_ID=`id -u` \
     -e TOMCAT_GROUP_ID=`getent group $USER | cut -d':' -f3` \
     -v `pwd`/logs:/usr/local/tomcat/logs/ \
     -v  /path/to/your/webapp:/usr/local/tomcat/webapps \
     -d -p 8080:8080 unidata/tomcat-docker:latest

where TOMCAT_USER_ID and TOMCAT_GROUP_ID have been configured with the UID/GID of the user running the container. If using docker-compose, see compose.env to configure the UID/GID of user tomcat inside the container.

This feature enables greater control of file permissions written outside the container via mounted volumes (e.g., files contained within the Tomcat logs directory such as catalina.out).

Note that containers that inherit this container and have overridden entrypoint.sh will have to take into account user tomcat is no longer assumed in the Dockerfile. Rather the tomcat user is now created within the entrypoint.sh and those overriding entrypoint.sh should take this fact into account. Also note that this UID/GID configuration option will not work on operating systems where Docker is not native (e.g., macOS).

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].