19

I'm new to Docker and trying to learn it. I'm using Docker Quickstart Terminal on Windows 7. I've a simple requirement where I'm using Tomcat in a Docker container. My DockerFile is as following:

FROM tomcat:8.0.47-jre7
RUN cd /usr/local/tomcat/webapps
COPY test.war /usr/local/tomcat/webapps/test.war

Then I issue simple build and run commands in the Docker console.

test.war is a Java web-service. This web-service internally calls other web-services on remote hosts using HTTPS. I've the certs for the remote hosts.

I tried several ways available on the internet to import or copy those certs to different locations as mentioned on different forums/blogs, but in vain. Whenever I use HTTPS to call the external web-service from test.war, it gives me SSL Handshake error.

I also have a Java keystore. I tried to use Java also in my Docker file and tried to use the keystore, but again, in vain.

When I use the same test.war on the tomcat installed directly on my machine, it works absolutely fine.

Can someone help me by providing the steps to be able to import/use SSL certs/keystore in this scenario. Also, how can I import more than one certs?

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
Gagan Singh
  • 241
  • 1
  • 3
  • 8
  • What steps have you followed to import certs? – Rao Oct 25 '17 at 04:05
  • From what you described you need to add the certs to truststore (don't confuse with keystore). There are different ways of doing that depending on the technologies you use to make a call. For instance, for spring boot it might be like this https://stackoverflow.com/a/43235569/2065796 You can also update the jvm inside the container with the trusted certs, etc. Provide more details on how you make a call so we'll be able to help you. – Sasha Shpota Oct 25 '17 at 06:51
  • Unrelated comment to your problem, but instead of `RUN cd /usr/local/tomcat/webapps` you can/should use `WORKDIR /usr/local/tomcat/webapps` to keep it more lightweight and best practice friendly :) – samprog Oct 25 '17 at 09:10
  • @Rao, I've tried the following: FROM registry.access.redhat.com/rhel7.1 ENV JAVA_HOME=/usr/lib/jvm/jre COPY ./certs/My_Root_CA.cer /etc/ssl/certs/ COPY ./certs/My_Issuing_CA.cer /etc/ssl/certs/ RUN $JAVA_HOME/bin/keytool -storepasswd -new mysecretpassword -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit ... – Gagan Singh Oct 26 '17 at 15:38
  • @Rao, also tried the following: ADD your_ca_root.crt /usr/local/share/ca-certificates/foo.crt RUN update-ca-certificates Steps provided at the following locations: https://docs.docker.com/engine/security/https/#create-a-ca-server-and-client-keys-with-openssl https://docs.docker.com/engine/security/https/#secure-by-default – Gagan Singh Oct 26 '17 at 15:42
  • @Oleksandr, Can you please point me to adding trusted certs to jvm option...i'm making all these calls through the respective web-service urls - both my web-service as well as the internal web-services that it calls...pls let me know if you're looking for any specific information about how i'm making the calls. – Gagan Singh Oct 26 '17 at 15:47
  • I also added the Base64 certs to the file /usr/ssl/certs/ca-bundle.crt; I also tried: COPY certs/xyz.cer /usr/ssl/certs/xyz.cer RUN update-ca-certificates – Gagan Singh Oct 26 '17 at 16:46
  • @GaganSingh I added it as an answer – Sasha Shpota Oct 26 '17 at 17:21

4 Answers4

21

You can try importing the certificate into jvm trusted store inside docker.

I've the certs for the remote hosts.

You can use these certificates but in fact you don't need them, you only need the root certificate of the authority that issued the certificates. You can download it from the internet.

Usually they are given in pem format, but you'll need der for jvm.

First you need to convert the certificate:

openssl x509 -in ca.pem -inform pem -out ca.der -outform der

Then install it into jvm keystore:

keytool -importcert -alias startssl -keystore \
    $JAVA_HOME/lib/security/cacerts -storepass changeit -file ca.der 

This command asks if you really want to add the certificate, you shoudl type "yes".

And all together in a Dockerfile:

FROM tomcat:8.0.47-jre7

COPY ca.pem ca.pem

RUN openssl x509 -in ca.pem -inform pem -out ca.der -outform der

RUN echo yes | keytool -importcert -alias startssl -keystore \
    /docker-java-home/jre/lib/security/cacerts -storepass changeit -file ca.der 

COPY test.war /usr/local/tomcat/webapps/test.war

WORKDIR /usr/local/tomcat/webapps

Note: if you already have certificate in der format you don't need openssl call, just copy the certificate directly.

To verify that the certificate is really applied you can run the container, ssh into it

$ docker exec -it <CONTAINER-ID> bash

and check the keystore:

$ keytool -keystore "/docker-java-home/jre/lib/security/cacerts" -storepass changeit -list | grep <NAME-OF-YOUR-CERT-AUTHORITY>
Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
  • Thanks a lot Oleksandr, Rao and Samprog, It worked ** – Gagan Singh Oct 26 '17 at 20:37
  • 3
    You don't need DER; `keytool` (more exactly `CertificateFactory`) has been able to read PEM certs since at least j6 in 2008, although PEM with extraneous text (like the 'comments' OpenSSL often puts) not until j7 in 2012. Also FWIW you can avoid the `echo yes |` with `-noprompt` – dave_thompson_085 Feb 03 '19 at 14:57
5

For Java apps in RHEL/Centos images, you can use update-ca-trust, which will update your trust stores for you, from files you place into /etc/pki/ca-trust. It also accepts .pem files directly:

FROM ...

USER root
COPY yourcertificate.pem /etc/pki/ca-trust/source/anchors/yourcertificate.pem
RUN update-ca-trust

This will update /etc/pki/java/cacerts for you automatically, so that Java will trust the new certificate.

Or, if your cert is hosted on a web server, then you can use curl to download it instead of copying the file - for example:

RUN curl -k https://badssl.com/certs/ca-untrusted-root.crt -o /etc/pki/ca-trust/source/anchors/ca-untrusted-root.crt && \
    update-ca-trust
Tom Donohue
  • 363
  • 2
  • 8
1

=> Use classpath:/some/location/cerkey.jks in case of Docker location, to refer the docker instance.

=> Use file:/some/location/cerkey.jks in case of host location, where the docker is running.

Hint: Value of server.ssl.key-store

Sireesh Yarlagadda
  • 12,978
  • 3
  • 74
  • 76
1

Building on Sasha Shpota's answer, we can do the same using Docker Compose (without needing to create a new image):

service:
  image: tomcat:9.0.62-jdk17
  volumes:
    - ./myapp.war:/usr/local/tomcat/webapps/myapp.war
    - /path/to/public.pem:/tmp/public.pem
  command: bash -c "keytool -importcert -alias startssl -keystore /usr/local/openjdk-17/lib/security/cacerts -storepass changeit -file /tmp/public.pem -noprompt && catalina.sh run"

The disadvantage is that one has to add the certificate every time the container is recreated. On the other hand, the certificate can be changed without having to rebuild the image. Useful for development.

Druckles
  • 3,161
  • 2
  • 41
  • 65