我有一个应用程序,执行各种有趣的东西与Git(像运行Git克隆& Git推送),我试图docker-ize它。

我遇到了一个问题,虽然我需要能够添加一个SSH密钥到容器的容器“用户”使用。

我试着把它复制到/root/。ssh/,更改$HOME,创建一个git ssh包装器,仍然没有运气。

以下是Dockerfile供参考:

#DOCKER-VERSION 0.3.4                                                           

from  ubuntu:12.04                                                              

RUN  apt-get update                                                             
RUN  apt-get install python-software-properties python g++ make git-core openssh-server -y
RUN  add-apt-repository ppa:chris-lea/node.js                                   
RUN  echo "deb http://archive.ubuntu.com/ubuntu precise universe" >> /etc/apt/sources.list
RUN  apt-get update                                                             
RUN  apt-get install nodejs -y                                                  

ADD . /src                                                                       
ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa                             
RUN   cd /src; npm install                                                      

EXPOSE  808:808                                                                 

CMD   [ "node", "/src/app.js"]

App.js运行git命令,比如git pull


当前回答

扩展Peter Grainger的回答,我可以使用Docker 17.05以来可用的多级构建。官方页面说明:

对于多阶段构建,您可以在Dockerfile中使用多个FROM语句。每个FROM指令都可以使用不同的基,并且每个FROM指令都开始构建的一个新阶段。您可以有选择地将工件从一个阶段复制到另一个阶段,在最终图像中留下您不想要的所有东西。

请记住这一点,这里是我的Dockerfile示例,包括三个构建阶段。它的目的是创建客户端web应用程序的生产映像。

# Stage 1: get sources from npm and git over ssh
FROM node:carbon AS sources
ARG SSH_KEY
ARG SSH_KEY_PASSPHRASE
RUN mkdir -p /root/.ssh && \
    chmod 0700 /root/.ssh && \
    ssh-keyscan bitbucket.org > /root/.ssh/known_hosts && \
    echo "${SSH_KEY}" > /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa
WORKDIR /app/
COPY package*.json yarn.lock /app/
RUN eval `ssh-agent -s` && \
    printf "${SSH_KEY_PASSPHRASE}\n" | ssh-add $HOME/.ssh/id_rsa && \
    yarn --pure-lockfile --mutex file --network-concurrency 1 && \
    rm -rf /root/.ssh/

# Stage 2: build minified production code
FROM node:carbon AS production
WORKDIR /app/
COPY --from=sources /app/ /app/
COPY . /app/
RUN yarn build:prod

# Stage 3: include only built production files and host them with Node Express server
FROM node:carbon
WORKDIR /app/
RUN yarn add express
COPY --from=production /app/dist/ /app/dist/
COPY server.js /app/
EXPOSE 33330
CMD ["node", "server.js"]

.dockerignore重复.gitignore文件的内容(它防止项目的node_modules和结果dist目录被复制):

.idea
dist
node_modules
*.log

创建镜像的命令示例:

$ docker build -t ezze/geoport:0.6.0 \
  --build-arg SSH_KEY="$(cat ~/.ssh/id_rsa)" \
  --build-arg SSH_KEY_PASSPHRASE="my_super_secret" \
  ./

如果您的私钥没有密码短语,请指定空的SSH_KEY_PASSPHRASE参数。

它是这样工作的:

1).只对第一级包装。json,纱线。锁文件和私有SSH密钥被复制到第一个名为sources的中间映像。为了避免进一步的SSH密钥口令提示,它被自动添加到SSH -agent。最后yarn命令从NPM安装所有需要的依赖项,并通过SSH从Bitbucket克隆私有git存储库。

2).第二阶段构建并缩小web应用程序的源代码,并将其放置在下一个名为production的中间映像的dist目录中。注意,安装的node_modules的源代码是从第一阶段生成的名为sources的镜像中复制的:

COPY --from=sources /app/ /app/

也可能是下面这句话:

COPY --from=sources /app/node_modules/ /app/node_modules/

我们只有来自第一个中间映像的node_modules目录,不再有SSH_KEY和SSH_KEY_PASSPHRASE参数。构建所需的所有其余内容都是从项目目录中复制的。

3).在第三阶段,我们减少最终图像的大小,将标记为ezze/geoport:0.6.0,只包括dist目录从第二个中间图像名为生产和安装Node Express启动一个web服务器。

列出图像给出如下输出:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ezze/geoport        0.6.0               8e8809c4e996        3 hours ago         717MB
<none>              <none>              1f6518644324        3 hours ago         1.1GB
<none>              <none>              fa00f1182917        4 hours ago         1.63GB
node                carbon              b87c2ad8344d        4 weeks ago         676MB

其中非标记图像对应于第一和第二个中间构建阶段。

如果你跑了

$ docker history ezze/geoport:0.6.0 --no-trunc

在最终的映像中,您不会看到任何提及SSH_KEY和SSH_KEY_PASSPHRASE的内容。

其他回答

一开始,有些元噪声

这里有两个得到高度好评的答案,其中有一个危险的错误建议。

我评论道,但由于我为此损失了很多天,请注意:

不要将私钥回显到文件中(意思是:回显"$ssh_prv_key" > /root/.ssh/id_ed25519)。这将破坏所需的行格式,至少在我的例子中是这样。

使用COPY或ADD代替。参见Docker加载密钥“/root/”。Ssh /id_rsa ":无效的详细格式。

另一名用户也证实了这一点:

我得到错误加载键"/root/。Ssh /id_ed25519":无效格式。回声将 为我删除换行符/双引号。这仅仅适用于ubuntu吗 或者对于alpine:3.10.3有什么不同?


1. 一种将私钥保存在映像中的工作方式(不太好!)

如果私钥存储在映像中,则需要注意从git网站上删除公钥,或者不发布映像。如果你处理好了,就安全了。请看下面(2.),你也可以“忘记关注”。

Dockerfile如下所示:

FROM ubuntu:latest
RUN apt-get update && apt-get install -y git
RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh
COPY /.ssh/id_ed25519 /root/.ssh/id_ed25519
RUN chmod 600 /root/.ssh/id_ed25519 && \
    apt-get -yqq install openssh-client && \
    ssh-keyscan -t ed25519 -H gitlab.com >> /root/.ssh/known_hosts
RUN git clone git@gitlab.com:GITLAB_USERNAME/test.git
RUN rm -r /root/.ssh

2. 一种不将私钥保存在映像中的工作方式(好!)

下面是更安全的方法,使用“多阶段构建”代替。 如果您需要一个映像,它的git repo目录中没有存储在其中一层中的私钥,那么您需要两个映像,最后只使用第二个映像。这意味着,你需要FROM两次,然后你可以只复制git回购目录从第一个映像到第二个映像,参见官方指南“使用多阶段构建”。

我们使用“alpine”作为最小的基图,使用apk代替apt-get;你也可以使用apt-get来代替上面的代码FROM ubuntu:latest。

Dockerfile如下所示:

# first image only to download the git repo
FROM alpine as MY_TMP_GIT_IMAGE

RUN apk add --no-cache git
RUN mkdir -p /root/.ssh &&  chmod 700 /root/.ssh
COPY /.ssh/id_ed25519 /root/.ssh/id_ed25519
RUN chmod 600 /root/.ssh/id_ed25519

RUN apk -yqq add --no-cache openssh-client && ssh-keyscan -t ed25519 -H gitlab.com >> /root/.ssh/known_hosts
RUN git clone git@gitlab.com:GITLAB_USERNAME/test.git
RUN rm -r /root/.ssh


# Start of the second image
FROM MY_BASE_IMAGE
COPY --from=MY_TMP_GIT_IMAGE /MY_GIT_REPO ./MY_GIT_REPO

我们在这里看到FROM只是一个名称空间,它就像它下面的行的标题,可以用别名来寻址。如果没有别名,——from=0将是第一个图像(= from命名空间)。

You could now publish or share the second image, as the private key is not in its layers, and you would not necessarily need to remove the public key from the git website after one usage! Thus, you do not need to create a new key pair at every cloning of the repo. Of course, be aware that a passwordless private key is still insecure if someone might get a hand on your data in another way. If you are not sure about this, better remove the public key from the server after usage, and have a new key pair at every run.


如何从Dockerfile构建映像的指南

Install Docker Desktop; or use docker inside WSL2 or Linux in a VirtualBox; or use docker in a standalone Linux partition / hard drive. Open a command prompt (PowerShell, terminal, ...). Go to the directory of the Dockerfile. Create a subfolder ".ssh/". For security reasons, create a new public and private SSH key pair - even if you already have another one lying around - for each Dockerfile run. In the command prompt, in your Dockerfile's folder, enter (mind, this overwrites without asking): Write-Output "y" | ssh-keygen -q -t ed25519 -f ./.ssh/id_ed25519 -N '""' (if you use PowerShell) or echo "y" | ssh-keygen -q -t ed25519 -f ./.ssh/id_ed25519 -N '' (if you do not use PowerShell). Your key pair will now be in the subfolder .ssh/. It is up to you whether you use that subfolder at all, you can also change the code to COPY id_ed25519 /root/.ssh/id_ed25519; then your private key needs to be in the Dockerfile's directory that you are in. Open the public key in an editor, copy the content and publish it to your server (e.g. GitHub / GitLab --> profile --> SSH keys). You can choose whatever name and end date. The final readable comment of the public key string (normally your computer name if you did not add a -C comment in the parameters of ssh-keygen) is not important, just leave it there. Start (Do not forget the "." at the end which is the build context): docker build -t test .

仅限1人使用。)

After the run, remove the public key from the server (most important, and at best at once). The script removes the private key from the image, and you may also remove the private key from your local computer, since you should never use the key pair again. The reason: someone could get the private key from the image even if it was removed from the image. Quoting a user's comment: If anyone gets a hold of your image, they can retrieve the key... even if you delete that file in a later layer, b/c they can go back to Step 7 when you added it The attacker could wait with this private key until you use the key pair again.

仅限2人。)

After the run, since the second image is the only image remaining after a build, we do not necessarily need to remove the key pair from client and host. We still have a small risk that the passwordless private key is taken from a local computer somewhere. That is why you may still remove the public key from the git server. You may also remove any stored private keys. But it is probably not needed in many projects where the main aim is rather to automate building the image, and less the security.


最后,更多的元噪声

对于两个高赞答案中使用了有问题的“私钥回声”方法的危险错误建议,以下是本文撰写时的投票情况:

https://stackoverflow.com/a/42125241/11154841 176个赞(前1) https://stackoverflow.com/a/48565025/11154841 55个赞(前5名) 这个问题获得了32.6万次观看,获得了376个赞

我们在这里看到答案中一定有错误,因为排名前1的答案投票至少不是在问题投票的水平上。

在排名前1的答案的评论列表的末尾,只有一个没有投票的小评论,命名了相同的私钥回声问题(在这个答案中也引用了这个问题)。而且,这一批评性评论是在给出答案三年后提出的。

I have upvoted the top 1 answer myself. I only realised later that it would not work for me. Thus, swarm intelligence is working, but on a low flame? If anyone can explain to me why echoing the private key might work for others, but not for me, please comment. Else, 326k views (minus 2 comments ;) ) would have overseen or left aside the error of the top 1 answer. I would not write such a long text here if that echo-of-the-private-key code line would not have cost me many working days, with absolutely frustrating code picking from everything on the net.

在一个运行的docker容器中,你可以使用docker -i (interactive)选项发出ssh-keygen。这将转发容器提示,在docker容器中创建密钥。

如果您需要在构建时使用SSH,那么这个问题就更难解决了。例如,如果你使用git克隆,或者在我的情况下,pip和npm从私有存储库下载。

我发现的解决方案是使用——build-arg标志添加键。然后你可以使用新的实验——squash命令(添加1.13)来合并图层,这样键在删除后就不再可用了。以下是我的解决方案:

建造的命令

$ docker build -t example --build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" --build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" --squash .

Dockerfile

FROM python:3.6-slim

ARG ssh_prv_key
ARG ssh_pub_key

RUN apt-get update && \
    apt-get install -y \
        git \
        openssh-server \
        libmysqlclient-dev

# Authorize SSH Host
RUN mkdir -p /root/.ssh && \
    chmod 0700 /root/.ssh && \
    ssh-keyscan github.com > /root/.ssh/known_hosts

# Add the keys and set permissions
RUN echo "$ssh_prv_key" > /root/.ssh/id_rsa && \
    echo "$ssh_pub_key" > /root/.ssh/id_rsa.pub && \
    chmod 600 /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa.pub

# Avoid cache purge by adding requirements first
ADD ./requirements.txt /app/requirements.txt

WORKDIR /app/

RUN pip install -r requirements.txt

# Remove SSH keys
RUN rm -rf /root/.ssh/

# Add the rest of the files
ADD . .

CMD python manage.py runserver

更新:如果你正在使用Docker 1.13并且有实验功能,你可以在build命令中添加——squash,它将合并层,删除SSH密钥并从Docker历史记录中隐藏它们。

您可以使用secrets来管理容器中的任何敏感数据 在运行时需要,但您不想存储在映像或源代码中 控件,例如: 用户名及密码 TLS证书和密钥 SSH密钥 其他重要数据,如数据库或内部服务器的名称 通用字符串或二进制内容(大小不超过500kb) https://docs.docker.com/engine/swarm/secrets/

我试图弄清楚如何将签名密钥添加到容器中,以便在运行时(而不是构建)使用,然后遇到了这个问题。Docker secrets似乎是我用例的解决方案,由于还没有人提到它,我将添加它。

这是现在可用的18.09版!

根据文档:

docker构建有一个——ssh选项来允许docker引擎 转发SSH代理连接。

下面是Dockerfile在容器中使用SSH的例子:

# syntax=docker/dockerfile:experimental
FROM alpine

# Install ssh client and git
RUN apk add --no-cache openssh-client git

# Download public key for github.com
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts

# Clone private repository
RUN --mount=type=ssh git clone git@github.com:myorg/myproject.git myproject

创建Dockerfile后,使用——ssh选项连接ssh代理:

$ docker build --ssh default .

另外,请查看https://medium.com/@tonistiigi/build- secrets.and -ssh-forwarding-in dock-18-09-ae8161d066