0

Please note: this question mentions Java and Java keystores, but in reality has nothing to do with Java and should be answerable by anyone with sufficient knowledge of Docker and writing Dockerfiles.


I have a web service (myservice) that is a JVM application that has been Dockerized. The Dockerfile for it looks like this:

FROM openjdk:8-jdk-alpine as myenv

COPY application.yml application.yml
COPY ${KS_FILE} mykeystore.p12
COPY build/libs/myservice.jar myservice.jar

EXPOSE 9200

ENTRYPOINT [ \
    "java", \
    "-Dspring.config=.", \
    "-Dkeystore.file=mykeystore.p12", \
    "-jar", \
    "myservice.jar" \
]

For the record, mykeystore.p12 is a file (not a directory!) that stores the SSL certificate that the service will serve back to any clients attempting to make a connection with it. Specifically it is an encrypted Java keystore but that is way outside the scope of this question, and is besides the point. All that matters is that you understand it is a file, not a directory. I have the file stored on my local machine.

I have the following .env file in the root of this project as well:

MYENV=local
KS_FILE=../apprunner/certs/mykeystore.p12

Hence, ../apprunner/certs/mykeystore.p12 is a relative path to a location on my file system where mykeystore.p12 lives, and I want that URI stored in the .env file and used as an environment variable from inside the Dockerfile.

I build the image like so:

docker build -t myorg/myservice .

And I run a container of that image like so:

docker run -d -p9200:9200 --name myservice --net myrunner_default --env-file .env myorg/myservice

When I SSH into the running container, I do see mykeystore.p12 on the local file system, except...

Its a directory! Not a file!

$ docker exec -it 8391601f451b /bin/sh
/ # ls -al
total 62936
drwxr-xr-x    1 root     root          4096 Feb  1 21:24 .
drwxr-xr-x    1 root     root          4096 Feb  1 21:24 ..
-rwxr-xr-x    1 root     root             0 Feb  1 21:24 .dockerenv
-rw-r--r--    1 root     root          1510 Jan 29 16:27 application.yml
drwxr-xr-x    2 root     root          4096 May  9  2019 bin
drwxr-xr-x    7 root     root          4096 Jan 30 22:50 mykeystore.p12
-rw-r--r--    1 root     root      64371878 Jan 29 16:27 myservice.jar
drwxr-xr-x    5 root     root           340 Feb  1 21:24 dev
drwxr-xr-x    1 root     root          4096 Feb  1 21:24 etc
... etc.

And its got lots of weird, unexpected stuff in it, like source files from other directories in my project!

Clearly, something is wrong with either the use of ${KS_FILE} from inside my Dockerfile, or with how the URI value is stored in my .env file. Regardless, what do I have to change such that:

  1. I specify the file path and name of the KS_FILE env var in my .env file; and
  2. Between my .env and Dockerfile, that filepath + name are copied over as a file into the running container, such that when I ls -al from inside the container, I see it as a file?
hotmeatballsoup
  • 385
  • 6
  • 58
  • 136
  • 1
    Should the keystore be persisted to the image itself? My thought would be that the keystore would be mounted as a volume into the running container, keeping secrets out of the application. Then if you rotated the certificate, you wouldn't have to re-build your app – C.Nivs Feb 01 '21 at 21:36
  • Thanks @C.Nivs (+1) I'm certainly open to this as a possibility but would need to see a useful example of what you mean so I can see the "forest through the trees". I'm still very new to Docker. Also, to me, the keystore and the config file (`application.yml`) are both build/deployment artifacts, so I feel like you could make the same argument about mounting the config file as well (so I could change the config file without rebuilding the app). Thoughts on that? Thanks again! – hotmeatballsoup Feb 01 '21 at 21:43

1 Answers1

3

There are two problems here.

You can only copy files from your build directory

You cannot copy files that exist outside of your build directory (such as ../apprunner/certs/mykeystore.p12). If you try reference this file directory in your Dockerfile, like this:

FROM openjdk:8-jdk-alpine as myenv

COPY application.yml application.yml
COPY ../apprunner/certs/mykeystore.p12 mykeystore.p12

You will see the following error:

Step 3/6 : COPY ../apprunner/certs/mykeystore.p12 mykeystore.p12
COPY failed: forbidden path outside the build context: ../apprunner/certs/mykeystore.p12 ()

Environment variable expansion doesn't work that way

From the documentation:

Environment variables (declared with the ENV statement) can also be used in certain instructions as variables to be interpreted by the Dockerfile. Escapes are also handled for including variable-like syntax into a statement literally.

Since you haven't declared any ENV statements in your Dockerfile, that environment expands as an empty string, so you are getting, in effect:

COPY "" mykeystore.p12

This will simply copy the contents of the build directory into mykeystore.p12.

docker build doesn't make use of a .env file.

This article might be helpful.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • Thanks @larsks (+1). Is the "build directory" the dir in which the `Dockerfile` is living? If so, I _suppose_ I could (1) add an `ENV` declaration to my Dockerfile and then (2) wrap my `docker build` inside either a Makefile or a shell script that copies the keystore file into the build directory, uses it to build the image, and then deletes it. But that feels awkward. Do you see any other solution here? Thanks again! – hotmeatballsoup Feb 01 '21 at 21:49
  • 1
    The `build directory` is the last argument to the `docker build` command, usually `.`, which is also usually where the `Dockerfile` is living. The `Dockerfile` can live elsewhere if referenced with the `-f /path/to/Dockerfile` option. – larsks Feb 01 '21 at 21:51
  • W/r/t to your second question regarding the keystore, I wonder if that is something that should be injected at runtime (as per the comment from @C.Nivs). – larsks Feb 01 '21 at 21:52
  • For the record I removed the 2nd `COPY` from my Dockerfile (that copies the keystore file), rebuilt the image, sourced the `.env` file and then ran a container of it adding `-v ${KS_FILE}:/mykeystore.p12` to the run invocation. **It worked perfectly.** Thanks for the help here! – hotmeatballsoup Feb 02 '21 at 11:52