我有一个应用程序,执行各种有趣的东西与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


当前回答

一开始,有些元噪声

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

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

不要将私钥回显到文件中(意思是:回显"$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文件设置权限,如下所示:

FROM ubuntu:16.04
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
EXPOSE 22
RUN cp /root/auth/id_rsa.pub /root/.ssh/authorized_keys
RUN rm -f /root/auth
RUN chmod 700 /root/.ssh
RUN chmod 400 /root/.ssh/authorized_keys
RUN chown root. /root/.ssh/authorized_keys
CMD /usr/sbin/sshd -D

您的docker运行包含如下内容,与容器共享主机上的认证目录(持有authorised_keys),然后打开ssh端口,该端口将通过主机上的端口7001访问。

-d -v /home/thatsme/dockerfiles/auth:/root/auth -–publish=127.0.0.1:7001:22

您可能想要查看https://github.com/jpetazzo/nsenter,它似乎是打开容器上的shell并在容器中执行命令的另一种方式。

Docker容器应该被视为它们自己的“服务”。为了分离关注点,你应该分离功能:

1)数据应该在数据容器中:使用链接卷将repo克隆到。然后可以将该数据容器链接到需要它的服务。

2)使用一个容器来运行git克隆任务(即它唯一的任务是克隆),当你运行它时将数据容器链接到它。

3) ssh-key也一样:把它作为一个卷(如上所述),当你需要它时链接到git克隆服务

这样,克隆任务和密钥都是临时的,只在需要时才活动。

现在,如果你的应用程序本身是一个git接口,你可能会考虑直接使用github或bitbucket REST api来完成你的工作:这就是它们的设计目的。

为了在容器中注入ssh密钥,你有多种解决方案:

使用带有ADD指令的Dockerfile,你可以在构建过程中注入它 简单地执行cat id_rsa | docker run -i <image> sh -c 'cat > /root/.ssh/id_rsa' 使用docker cp命令,允许您在容器运行时注入文件。

这是现在可用的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

如果您不关心SSH密钥的安全性,这里有许多很好的答案。如果你有,我找到的最好的答案是从上面一个评论的链接到这个GitHub评论由diegosandrim。所以其他人更有可能看到它,以防回购消失,这里是这个答案的编辑版本:

这里的大多数解决方案最终都将私钥保留在映像中。这很糟糕,因为任何访问映像的人都可以访问您的私钥。由于我们对挤压的行为了解不够,即使你删除键并挤压该层,情况仍然可能是这样。

我们用aws s3 cli生成一个预签名URL来访问密钥,并限制访问大约5分钟,我们将这个预签名URL保存到repo目录下的一个文件中,然后在dockerfile中将其添加到映像中。

在dockerfile中,我们有一个RUN命令,执行所有这些步骤:使用pre-sing URL获取ssh密钥,运行npm install,并删除ssh密钥。

通过在单个命令中执行此操作,ssh密钥将不会存储在任何层中,但将存储预签名URL,这不是问题,因为URL将在5分钟后失效。

构建脚本看起来像:

# build.sh
aws s3 presign s3://my_bucket/my_key --expires-in 300 > ./pre_sign_url
docker build -t my-service .

Dockerfile是这样的:

FROM node

COPY . .

RUN eval "$(ssh-agent -s)" && \
    wget -i ./pre_sign_url -q -O - > ./my_key && \
    chmod 700 ./my_key && \
    ssh-add ./my_key && \
    ssh -o StrictHostKeyChecking=no git@github.com || true && \
    npm install --production && \
    rm ./my_key && \
    rm -rf ~/.ssh/*

ENTRYPOINT ["npm", "run"]

CMD ["start"]