Accessing Amazon DocumentDB from dockerized Java with TLS enabled

There is a good set of documentation available when developing with Amazon DocumentDB, including connecting programmatically with TLS enabled. There is a variety of languages covered there, including Java, but what I did not find was the steps to build a docker container where Java code would be able to connect to the Amazon DocumentDB.

Photo by ammiel jr on Unsplash

For reference, see the whole sample project on GitHub.

As described in the AWS documentation, Java code needs Amazon RDS CA certificate inside a trust store. In short, we will create the trust store when building the Docker image, bake it into the image, so that later the Java code running in that container can use it.

AWS documentation in Java connecting with TLS enabled provides a shell script that creates a trust store with the RDS CA certificate. We slightly modify that script to read the trust store password from an argument.

See import_rds_certs.sh

Let’s store the shell script next to the Dockerfile that will:

  1. Start from openjdk:12-alpine
  2. Install curl, openssl, and perl needed by the shell script
  3. Read a docker argument
  4. Run the shell script to create the trust store /certs/rds-truststore.jks baked inside the image
FROM openjdk:12-alpine# Install needed packages
RUN apk update \
&& apk upgrade \
&& apk --no-cache add curl openssl perl
# add truststore needed for connection to DocumentDB cluster
COPY import_rds_certs.sh /certs/import_rds_certs.sh
ARG trustStorePassword
RUN /certs/import_rds_certs.sh $trustStorePassword

AWS documentation tells us to use the keystore in your program by setting the following system properties in your application before making a connection to the Amazon DocumentDB cluster.

javax.net.ssl.trustStore: <truststore>
javax.net.ssl.trustStorePassword: <truststorePassword>

You can use the JavaMain class showed in the AWS documentation to connect to the cluster.

See our modified Main.java where we will set the SSL system properties using -D flags. Don’t forget to configure your cluster hostname, user, password, database, and collection in the code of Main.java. For running the jar from the docker, we will finalize the Dockerfile with:

ENV TRUST_STORE_PASSWORD=$trustStorePassword# copy app
COPY build/libs/java-docker-rds.jar /java-docker-rds.jar
CMD java -Djavax.net.ssl.trustStore=/certs/rds-truststore.jks -Djavax.net.ssl.trustStorePassword=$TRUST_STORE_PASSWORD -jar java-docker-rds.jar

See the complete Dockerfile.

When building the image, we will provide the trustStorePassword in --build-arg.

docker build --tag my-prj/service:0.0.0-local --build-arg trustStorePassword=somePassword .

Simply run using the image we have just built:

docker run abcdef12345

This should output the number of entries in your collection.

Troubleshooting

When running into problems, first make sure you can run the Main.java from your IDE and connect to the cluster using -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStorePassword. Notice, for that you will need to have the trust store JKS file locally on your laptop — use the shell script from AWS Documentation to create it. It is better to troubleshoot and debug in your IDE, get familiar with the setup, and then proceed with the Docker container.

Together with IOException: Keystore was tampered with, or password was incorrect usually means that you did not point javax.net.ssl.trustStore correctly to the JKS file. Remember that when running from your IDE, the path is on your disk. But when running in the Docker container, it is the path inside that container — /certs/rds-truststore.jks in our sample project.

javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption. In the stack trace, you will also see IOException: keystore password was incorrect and that it exactly it — you did not provide the correct password in javax.net.ssl.trustStorePassword.

Software engineer and active sportsman