40
  • A Java Service is running inside the Docker container, which access the external HTTPS url and its self-sign certificate is unavailable to the service/ JRE cacert keystore and therefore connection fails.
  • Hence imported the self-signed certificate of HTTPS external URL into Docker container's JRE cacert keystore. (after checking the $JAVA_HOME env. variable)
  • Restarted the Docker container (using docker restart command), hoping that the service is also get restarted and pick the changes from JRE cacert. But this didn't happen, the Java service still fails to access external HTTPS URL.

Any idea how a Java service running inside the Docker container pick the JRE cacert changes with new certificate import?

Subodh Joshi
  • 12,717
  • 29
  • 108
  • 202
Zeigeist
  • 3,755
  • 3
  • 20
  • 22

3 Answers3

70

Hence imported the self-signed certificate of HTTPS external URL into Docker container's JRE cacert keystore.

No: you need to import it into the Docker image from which you run your container.

Importing it into the container would only create a temporary writable data layer, which will be discarded when you restart your container.

Something like this answer:

USER root
COPY ldap.cer $JAVA_HOME/jre/lib/security
RUN \
    cd $JAVA_HOME/jre/lib/security \
    && keytool -keystore cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias ldapcert -file ldap.cer
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I think this is the best solution just export the certificate as base-64 encoded X.509 to avoid hassle of copying a binary file – Ramy Feteha Sep 27 '18 at 09:51
  • 1
    Restart does not remove the files from a docker container instance. docker run -d --name=nginx nginx; docker exec -ti nginx /bin/bash; cd ~; touch foo.bar; Do ls to verify that foo.bar got created. exit the docker container terminal docker restart nginx; docker exec -ti nginx /bin/bash; cd ~; ls You will see that foo.bar is still there – Dojo Jan 28 '20 at 11:27
  • @fongfong Strange ಠಿ_ಠಿ Could you make that a separate question, with details on the Docker host, docker VM, base image used? – VonC Feb 16 '20 at 19:53
  • Hi VonC, I posted my question at https://stackoverflow.com/questions/60253847/dockerfile-keytool-getting-certificate-alias-name-already-exists-even-using Thanks! – fongfong Feb 16 '20 at 22:37
  • 1
    @Archmede I have not: you might consider putting this as a separate question. – VonC Mar 31 '20 at 20:13
  • @VonC I did thanks, https://stackoverflow.com/questions/60959274/certificate-always-expires-5-days-ago-in-docker – Archmede Mar 31 '20 at 20:28
28

For using already configured java based containers like jenkins, sonarqube or nexus (e. g. if you run your own build server) I find it more convenient to mount a suitable cacerts-file into these containers with a parameter for docker run .

I use the cacerts file from openjdk as base:

  1. extracting cacerts from openjdk image using a temporary container:
docker pull openjdk:latest
docker run --rm --entrypoint cat openjdk:latest /etc/ssl/certs/java/cacerts > cacerts
  1. adding certificate to the extracted cacerts using a temporary container started from the same folder which also contains ldap.cer:
docker run --rm -v `pwd`:/tmp/certs openjdk:latest bash -c 'cd /tmp/certs && keytool -keystore cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias buenting-root -file ldap.cer'
  1. run your target docker container(s) mounting the extracted cacerts with a run-parameter, e. g. for sonarqube:
docker run ... -v /path/to/your/prepared/cacerts:/etc/ssl/certs/java/cacerts:ro ... sonarqube:lts

If there is a new version of openjdk you can update the cacerts-file on the host with commands from 1. and 2.

For updating the target image (e. g. sonarqube) you do not need to create your own image using Dockerfile and docker build.

Volker Seibt
  • 1,479
  • 16
  • 19
  • 4
    This may work fine, until your image provider decides to change Java version. Staying with old `cacerts` file may have unforeseen consequences including trusting CAs which should not be trusted anymore. To avoid this you must track Java version changes in the image of interest and extract new file when that happens. Because of this it's getting not so convenient anymore. – wst May 08 '18 at 13:50
  • 1
    @waste It's even easier. With a new java version available you only have to update the cacerts file on your docker host and you do not even have to replace the image or the container. Then the certificates used may even be newer than those provided in the image. – Volker Seibt May 09 '18 at 07:45
  • @VolkerSeibt Will this work if I have a self signed certificate which I need in my docker image? It works fine if I mention the certificate in the Dockerfile and run keypass. However, I want a solution such that I don't need to modify the image. I would like to mention the certificate as part of running my container. – Anthony Oct 16 '18 at 10:51
  • 1
    @Anthony The latest command is intended to be run your target container, which then uses the cacerts file saved on the docker host. No need to modify the image. – Volker Seibt Oct 16 '18 at 14:41
  • This should be preferred answer, imho. If you work for a "bigger" organization then building new images just because of updates to your `cacerts` creates additional costs without any business value. – Adam Bogdan Boczek Mar 03 '20 at 14:15
11

Here is a solution that worked for OpenJDK Java 11 based image.

A thing to mention before is that you can use either JDK image or JRE. The second option will require ca-certificates-java installed.

  • Dockerfile for JDK based image:
FROM openjdk:11-jdk-slim
WORKDIR /opt/workdir/

#.crt file in the same folder as your Dockerfile
ARG CERT="certificate.crt"

#import cert into java
COPY $CERT /opt/workdir/
RUN keytool -importcert -file $CERT -alias $CERT -cacerts -storepass changeit -noprompt

...
  • Dockerfile for JRE based image:
FROM openjdk:11-jre-slim
WORKDIR /opt/workdir/

#.crt file in the same folder as your Dockerfile
ARG CERT="certificate.crt"

#installing ca-certificates-java and then import cert into java
COPY $CERT /opt/workdir/
RUN mkdir -p /usr/share/man/man1 \
    && apt-get update \
    && apt-get install -y ca-certificates-java \
    && keytool -importcert -file $CERT -alias $CERT -cacerts -storepass changeit -noprompt

...

Also, as you can see from Dockerfiles' instructions above both of them require your certificate.crt file to be in the same folder.

Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
Serhii Povísenko
  • 3,352
  • 1
  • 30
  • 48