在docker容器内使用SSH密钥

我有一个应用程序,使用Git执行各种有趣的东西(比如运行git clone& git push),我正在尝试停靠它。

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

我尝试将其复制到app.js,更改git pull,创建一个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

22个解决方案
75 votes

使用Ubuntu时,ssh_config不正确。 你需要添加

RUN  echo "    IdentityFile ~/.ssh/id_rsa" >> /etc/ssh/ssh_config

到您的Dockerfile,以使其识别您的ssh密钥。

ebensing answered 2019-02-19T19:54:58Z
73 votes

如果您需要在构建时使用SSH,这是一个更难的问题。 例如,如果您使用git clone,或者在我的情况下使用pipnpm从专用存储库下载。

我找到的解决方案是使用--squash标志添加密钥。 然后,您可以使用新的实验docker history命令(添加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并且具有实验性功能,则可以将--squash附加到构建命令,该命令将合并图层,删除SSH密钥并将其隐藏在docker history中。

Daniel van Flymen answered 2019-02-19T19:55:50Z
68 votes

注意:只对私有的图像使用此方法,并且永远都是!

即使您在添加图层命令后删除了图层命令中的密钥,ssh密钥仍会存储在图像中(请参阅本文中的评论)。

在我的情况下,这是好的,所以这就是我正在使用的:

# Setup for ssh onto github
RUN mkdir -p /root/.ssh
ADD id_rsa /root/.ssh/id_rsa
RUN chmod 700 /root/.ssh/id_rsa
RUN echo "Host github.com\n\tStrictHostKeyChecking no\n" >> /root/.ssh/config
yellowcap answered 2019-02-19T19:56:28Z
44 votes

如果你正在使用docker compose,一个简单的选择就是转发SSH代理:

something:
    container_name: something
    volumes:
        - $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker
    environment:
        SSH_AUTH_SOCK: /ssh-agent
Aistis answered 2019-02-19T19:56:54Z
39 votes

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

  1. 使用带有docker cp指令的Dockerfile,您可以在构建过程中注入它

  2. 只需做docker cp之类的事情

  3. 使用docker cp命令,该命令允许您在容器运行时注入文件。

creack answered 2019-02-19T19:57:40Z
20 votes

扩展Peter Grainger的答案我能够使用自Docker 17.05以来的多阶段构建。 官方页面说明:

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

记住这一点是我的SSH_KEY的例子,包括三个构建阶段。 它旨在创建客户端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"]

SSH_KEY重复SSH_KEY_PASSPHRASE文件的内容(它防止production和项目的sources目录被复制):

.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密钥没有密码,则只需指定空SSH_KEY参数。

这是它的工作原理:

1)。 在第一阶段,仅将SSH_KEY,SSH_KEY_PASSPHRASE文件和私有SSH密钥复制到名为production的第一个中间映像。为了避免进一步的SSH密钥密码提示,它会自动添加到sources.最后yarn命令从NPM和克隆安装所有必需的依赖项 来自Bitbucket的私人git存储库通过SSH。

2)。 第二阶段构建并缩小Web应用程序的源代码,并将其放在SSH_KEY下一个名为SSH_KEY_PASSPHRASE的中间图像的目录中。请注意,已安装的production的源代码是从第一个阶段生成的第sources图像中复制的:

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

可能它也可能是以下行:

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

我们这里的第一个中间图像只有SSH_KEY目录,没有SSH_KEY_PASSPHRASEproduction参数。 构建所需的所有其余内容都从我们的项目目录中复制。

3)。 在第三阶段,我们通过仅包含来自第二个中间映像productionSSH_KEY_PASSPHRASE目录并安装Node Express以启动Web服务器来减小将被标记为SSH_KEY的最终映像的大小。

列出图像会得到如下输出:

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_KEYSSH_KEY_PASSPHRASE的任何提及。

Ezze answered 2019-02-19T19:59:43Z
14 votes

这行是个问题:

ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa

指定要复制到映像中的文件时,只能使用相对路径 - 相对于Dockerfile所在的目录。 所以你应该使用:

ADD id_rsa /root/.ssh/id_rsa

并将id_rsa文件放入Dockerfile所在的目录中。

查看更多详细信息:[http://docs.docker.io/reference/builder/#add]

Dan Keder answered 2019-02-19T20:00:29Z
13 votes

Docker容器应该被视为自己的“服务”。 要分开关注点,您应该分开功能:

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

2)使用容器运行git克隆任务(即,它只是克隆工作),在运行它时将数据容器链接到它。

3)ssh-key相同:把它放在一个卷(如上所述)并在需要时将它链接到git clone服务

这样,克隆任务和密钥都是短暂的,只有在需要时才有效。

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

MrE answered 2019-02-19T20:01:30Z
10 votes

这个问题真的很烦人。 因为你不能在dockerfile上下文之外添加/复制任何文件,这意味着不可能只将〜/ .ssh / id_rsa链接到图像的/root/.ssh/id_rsa,当你肯定需要一个键来做一些sshed的东西时 喜欢来自私人仓库链接的git clone ...,在建造你的码头图像时。

无论如何,我找到了一个解决方法,不是那么说服,但确实对我有用。

  1. 在您的dockerfile中:

    • 将此文件添加为/root/.ssh/id_rsa
    • 做你想要的,比如git clone,composer ......
    • rm /root/.ssh/id_rsa在最后
  2. 一次拍摄的剧本:

    • cp你持有dockerfile的文件夹的密钥
    • 码头工人建造
    • rm复制的密钥
  3. 任何时候你必须使用一些ssh要求从这个映像运行一个容器,只需为run命令添加-v,如:

    docker run -v~ / .ssh / id_rsa:/root/.ssh/id_rsa --name container image命令

此解决方案在项目源和构建的docker镜像中都没有私钥,因此不再需要担心安全问题。

ImLeo answered 2019-02-19T20:03:17Z
10 votes

在docker构建时进行npm install时遇到了类似的问题。

灵感来自Daniel van Flymen的解决方案并将其与git url重写相结合,我们发现了一个更简单的方法来验证来自私有github repos的npm安装 - 我们使用了oauth2令牌而不是密钥。

在我们的例子中,npm依赖项被指定为“git + [https://github.com / ...”]

对于容器中的身份验证,需要将URL重写为适合ssh身份验证([ssh://git@github.com/)]或令牌身份验证([https:// $ {GITHUB_TOKEN} @ github.com /)]

构建命令:

docker build -t sometag --build-arg GITHUB_TOKEN=$GITHUB_TOKEN . 

不幸的是,我在docker 1.9上,所以--squash选项还没有,最终需要添加

Dockerfile:

FROM node:5.10.0

ARG GITHUB_TOKEN

#Install dependencies
COPY package.json ./

# add rewrite rule to authenticate github user
RUN git config --global url."https://${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"

RUN npm install

# remove the secret token from the git config file, remember to use --squash option for docker build, when it becomes available in docker 1.13
RUN git config --global --unset url."https://${GITHUB_TOKEN}@github.com/".insteadOf

# Expose the ports that the app uses
EXPOSE 8000

#Copy server and client code
COPY server /server 
COPY clients /clients
Markko Paas answered 2019-02-19T20:04:21Z
8 votes

将ssh身份验证套接字转发到容器:

docker run --rm -ti \
        -v $SSH_AUTH_SOCK:/tmp/ssh_auth.sock \
        -e SSH_AUTH_SOCK=/tmp/ssh_auth.sock \
        -w /src \
        my_image

您的脚本将能够执行chown $OWNER_USER:$OWNER_GROUP -R <source_folder>

额外:如果您希望克隆文件属于特定用户,则需要使用chown $OWNER_USER:$OWNER_GROUP -R <source_folder>,因为在容器内使用除root之外的其他用户将使git失败。

您可以在容器的环境中发布一些其他变量:

docker run ...
        -e OWNER_USER=$(id -u) \
        -e OWNER_GROUP=$(id -g) \
        ...

克隆后,必须执行chown $OWNER_USER:$OWNER_GROUP -R <source_folder>以在离开容器之前设置正确的所有权,以便容器外的非root用户可以访问这些文件。

edupo answered 2019-02-19T20:05:16Z
7 votes

'你可以有选择地让远程服务器访问你的本地ssh-agent,就像它在服务器上运行一样'

[https://developer.github.com/guides/using-ssh-agent-forwarding/]

arreche answered 2019-02-19T20:05:49Z
6 votes

您还可以在主机和容器之间链接.ssh目录,我不知道这种方法是否有任何安全隐患,但它可能是最简单的方法。 这样的事情应该有效:

$ sudo docker run -it -v /root/.ssh:/root/.ssh someimage bash

请记住,docker使用sudo运行(除非你没有),如果是这种情况你将使用root ssh密钥。

Luis Elizondo answered 2019-02-19T20:06:24Z
6 votes

一种解决方案是使用以下选项将主机的ssh密钥安装到docker中:

docker run -v /home/<host user>/.ssh:/home/<docker user>/.ssh <image>

与上述解决方案类似。 但适用于非root用户。 与github完美配合。

Mohammad Azim answered 2019-02-19T20:06:58Z
5 votes

我今天遇到了同样的问题和以前的帖子稍微修改过的版本我发现这种方法对我来说更有用

docker run -it -v ~/.ssh/id_rsa:/root/.my-key:ro image /bin/bash

(注意readonly标志,所以容器在任何情况下都不会弄乱我的ssh密钥。)

在容器里面我现在可以运行:

ssh-agent bash -c "ssh-add ~/.my-key; git clone <gitrepourl> <target>"

所以我没有得到@kross注意到的Bad owner or permissions on /root/.ssh/..错误

tojo answered 2019-02-19T20:07:46Z
1 votes

我试图以另一种方式解决问题:向图像添加公共ssh密钥。 但在我的试验中,我发现“docker cp”用于从容器复制到主机。 吱吱声中的第3项似乎是说你可以使用docker cp将文件注入容器中。 见[https://docs.docker.com/engine/reference/commandline/cp/]

摘抄

将文件/文件夹从容器的文件系统复制到主机路径。   路径相对于文件系统的根目录。

  Usage: docker cp CONTAINER:PATH HOSTPATH

  Copy files/folders from the PATH to the HOSTPATH
EricGreg answered 2019-02-19T20:08:34Z
1 votes

您可以使用共享文件夹将授权密钥传递到容器,并使用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 run包含类似以下内容的东西,用于在容器上共享主机上的auth目录(保持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并在容器中执行命令的另一种方法。

andrew pate answered 2019-02-19T20:09:16Z
1 votes

如果您不关心SSH密钥的安全性,那么这里有很多好的答案。 如果你这样做,我找到的最佳答案是从上面评论中的链接到diegocsandrim的这个GitHub评论。 所以其他人更有可能看到它,并且只是在回购消失的情况下,这是该答案的编辑版本:

这里的大多数解决方案最终将私钥留在图像中。 这很糟糕,因为任何有权访问图像的人都可以访问您的私钥。 由于我们对squash的行为了解不足,即使您删除密钥并压缩该层,情况仍可能如此。

我们使用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"]
Sam H. answered 2019-02-19T20:10:26Z
1 votes

在更高版本的docker(17.05)中,您可以使用多阶段构建。 哪个是最安全的选项,因为以前的构建只能由后续构建使用,然后销毁

有关详细信息,请参阅我的stackoverflow问题的答案

Peter Grainger answered 2019-02-19T20:11:01Z
0 votes

最简单的方法是,获取一个启动板帐户并使用:ssh-import-id

Sam Saffron answered 2019-02-19T20:11:29Z
0 votes

到目前为止,对于这方面来说,这将使您的主机操作系统密钥可以在容器内随时使用,如何:

docker run -v ~/.ssh:/mnt -it my_image /bin/bash -c "ln -s /mnt /root/.ssh; ssh user@10.20.30.40"

我不赞成使用Dockerfile来安装密钥,因为容器的迭代可能会留下私钥。

Jepper answered 2019-02-19T20:12:05Z
-1 votes

在正在运行的docker容器中,您可以使用docker -i(交互式)选项发出ssh-keygen。 这将转发容器提示以在docker容器内创建密钥。

Jerome Anthony answered 2019-02-19T20:12:32Z
translate from https://stackoverflow.com:/questions/18136389/using-ssh-keys-inside-docker-container