Loading a SSL certificate to container's JVM

Viewed 2581

I have a Helm Chart. Which on deployment creates the following Kubernetes resources:

  1. A SSL enabled Service
  2. A Container created with my Docker image which internally runs a Java process that communicates with the above service.
  3. Kubernetes Secrets (SSL certificates and access keys), that are mounted inside the above container.

The problem is my Container can not talk to my Service unless I add the SSL certificate to Java certificates. I do this by running the following command manually inside my docker with root user.

cd ${JAVA_HOME}/jre/lib/security 

keytool -keystore cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias myservicecert -file /home/myuser/.myservice/certs/public.crt

I want to automate the above steps but facing issues:

  • I can not perform above steps inside the Dockerfile since the certificate is not available at that time. Obivious.
  • I can not add these steps in entrypoint.sh file of the docker because entrypoint file is executed by a different user than the root.
  • Another solution that I could think of is that change the permission of path ${JAVA_HOME}/jre/lib/security/cacerts to 777 so that I can run these commands in entrypoint.sh without an issue. But I am not sure if this is the correct way to go due to security reasons. Suggestions on this ?
  • I do not want to set a custom location for certificates using Djavax.net.ssl.trustStore because then java will not have access to root certificates.

What is correct way to achieve this?

2 Answers

The easiest way is probably to allow running the keytool command using sudo.

You may need to install sudo in your Dockerfile and then you can set up /etc/sudoers to allow the user running entrypoint.sh to run that specific command (without a password). Have a look at this answer on Unix for how to do it.

All you need to do then is locate the certificate and run

sudo keytool -keystore cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias myservicecert -file /home/myuser/.myservice/certs/public.crt

In entrypoint.sh

Alternatively you can install a script when building your container that sets up the certificate and installs it and allow that to be run with sudo. That way you don't open up the possibility to add more certificates if someone gains access to the container

TL;DR

You can add the certificate to the keystore in an initContainer. The keystore can be shared between the initContainer and the container using a volume.


In this example we're mounting the tls-secret to the pod as a Kubernetes Secret. The tls-secret contains the tls.crt which needs to be added to the keystore. We add the certificate to the keystore in the initContainer after copying the security files into a volume which is mounted to the pod's container.

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  containers:
  - name: myapp
    image: openjdk:8-jdk-slim-buster
    command:
    - bash
    - -c
    - |
      keytool -list -keystore /usr/local/openjdk-8/jre/lib/security/cacerts -alias local
      sleep infinity
    volumeMounts:
    - mountPath: /usr/local/openjdk-8/jre/lib/security
      name: cacerts
  initContainers:
  - name: init-cacerts
    image: openjdk:8-jdk-slim-buster
    command:
    - bash
    - -c
    - |
      cp -R /usr/local/openjdk-8/jre/lib/security/* /cacerts/
      keytool -import -noprompt -trustcacerts -alias local -file /security/tls.crt -keystore /cacerts/cacerts -storepass changeit
      keytool -list -keystore /cacerts/cacerts -alias local
    volumeMounts:
    - mountPath: /cacerts
      name: cacerts
    - mountPath: /security
      name: tls
  volumes:
  - name: cacerts
    emptyDir: {}
  - name: tls
    secret:
      secretName: tls-secret
      defaultMode: 0400

shared keystore

Related