【问题标题】:DockerFile : RUN Pass argumentsDockerFile : RUN 传递参数
【发布时间】:2020-10-05 14:49:06
【问题描述】:

我们需要更改 docker 镜像中 Keystore 的 JKS 密码。

DockerFile 中的以下命令有效

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.  ]

但是,更改后的密码(第 7 行)仍然是硬编码字符串。 我们想从环境中读取它并在命令中设置。 我怎样才能使用环境变量来达到同样的效果?

【问题讨论】:

    标签: java docker security dockerfile


    【解决方案1】:

    将 Dockerfile 视为应用程序的 Java 源代码,将构建的映像视为 jar 文件。当你运行容器时,你在RUN 命令中所做的任何事情都会被修复,就像一个 jar 文件一样,如果你知道如何很好地使用主机工具,那么很容易提取图像的内容(在这种情况下是对应的秘密)。

    这里最简单的做法是删除这个RUN 指令。而是在主机系统上运行keytool 命令,然后在运行时使用docker run -v 绑定挂载选项将文件注入容器。

    # 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
    

    如果您真的想在容器中执行此操作,则需要使用入口点脚本来更改密码。一起使用ENTRYPOINTCMD 的典型模式是让ENTRYPOINT 成为一个包装脚本,执行一些必要的首次设置,然后运行shell exec "$@" 命令来运行CMD(其余的的命令行参数)作为主容器进程。

    在这种情况下,请记住,任何拥有 Docker 访问权限的人都可以docker inspect 找到容器启动时使用的环境变量和其他选项,任何拥有系统 root 访问权限的人都可以找到任何进程的环境变量,因此以这种方式传递密码存在不小的安全风险。

    入口点脚本如下所示:

    #!/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 "$@"
    

    在您的 Dockerfile 中,您可以将此脚本设置为入口点,而无论 java 命令运行应用程序的任何命令都是命令:

    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"]
    

    【讨论】:

    • 感谢您用简单的语言进行解释。真的很有帮助。
    【解决方案2】:

    您可以使用ARG,然后使用RUN 命令的shell 形式:

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

    那么,在构建时使用docker build --build-arg jks_password=XYZ .

    但是这有一个警告,请参阅 Docker 文档中的警告:

    警告: 不建议使用构建时变量来传递 github 密钥、用户凭据等机密信息。使用 docker history 命令的图像的任何用户都可以看到构建时变量值。 请参阅“使用 BuildKit 构建镜像”部分,了解构建镜像时使用机密的安全方法。

    应该使用--secret flag

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

    以及对应的 docker build 参数:--secret id=mysecret,src=mysecret.txt .

    【讨论】:

      猜你喜欢
      • 2017-04-13
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      相关资源
      最近更新 更多