简体   繁体   中英

DockerFile : RUN Pass arguments

We need to change the JKS password for the Keystore inside our docker image.

The following command in DockerFile works

1. RUN [\
2.  "/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool",\
3.  "-storepasswd",\
4.  "-storepass",\
5.  "changeit",\
6.  "-new",\
7.  "NEW_JKS_PASSWORD",\
8.  "-cacerts"\
9.  ]

However, the changed password (line number 7) is still a hard-coded string. We would like to read it from the environment and set in the command. How can I use environment variables to achieve the same?

You can use ARG and then the shell form of the RUN command:

ARG jks_password
RUN "/usr/lib/jvm/java-11-openjdk-amd64/bin/keytool -storepasswd -storepass …"

then, when building use docker build --build-arg jks_password=XYZ .

BUT this comes with a caveat, see the warning in the Docker docs:

Warning: It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc. Build-time variable values are visible to any user of the image with the docker history command. Refer to the “build images with BuildKit” section to learn about secure ways to use secrets when building images.

Instead, the --secret flag should be used:

RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

And the corresponding docker build arguments: --secret id=mysecret,src=mysecret.txt .

Think of the Dockerfile like the Java source code to your application, and the built image like a jar file. Anything you do in a RUN command will be fixed when you run container, and like a jar file, if you know how to use the host tools well it is very easy to extract the contents of the image (and in this case the corresponding secret).

The easiest thing to do here is to remove this RUN directive. Instead, run the keytool command on the host system, and then use a docker run -v bind mount option to inject the file into the container when you run it.

# Get the existing cacerts file out of the image
docker run --rm yourimage \
  cat /usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  > cacerts
# Change its password
keytool \
  -keystore cacerts \
  -storepass changeit \
  -storepasswd -new NEW_JKS_PASSWD
# Run the container, replacing the cacerts file with the local one
docker run \
  -v $PWD/cacerts:/usr/lib/jvm/java-11-openjdk-amd64/lib/security/cacerts \
  ...
  yourimage

If you really want to do this in the container, you need to use an entrypoint script to change the password. A typical pattern for using ENTRYPOINT and CMD together is to have the ENTRYPOINT be a wrapper script that does some required first-time setup, then runs the shell exec "$@" command to run the CMD (the rest of the command-line arguments) as the main container process.

In this context, remember that anyone who has Docker access can docker inspect the container to find the environment variables and other options it was started with, and anyone who has root access to the system can find the environment variables of any process, so there is a not insignificant security risk of passing a password this way.

The entrypoint script would look something like:

#!/bin/sh

# Set the password if it's provided
if [ -n "$NEW_JKS_PASSWORD" ]; then
  /usr/lib/jvm/java-11-openjdk-amd64/bin/keytool \
    -storepasswd \
    -storepass changeit \
    -new "$NEW_JKS_PASSWORD" \
    -cacerts
  # Prevent the password from leaking into the main process
  # (`docker inspect` _etc._ will still show it)
  unset NEW_JKS_PASSWORD
fi

# Run the main container process
exec "$@"

In your Dockerfile, you'd make this script be the entrypoint, and whatever java command runs the application be the command:

FROM openjdk:11
COPY target/app.jar entrypoint.sh /
# MUST be JSON-array form
ENTRYPOINT ["/entrypoint.sh"]
# Can be either shell or JSON-array form
CMD ["java", "-jar", "/app.jar"]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM