Using SSH keys inside docker container
我有一个应用程序可以用git执行各种有趣的东西(比如运行git clone&git push),我正在尝试将其Docker化。
不过,我遇到了一个问题,我需要向容器添加一个ssh密钥,以便容器"用户"使用。
我尝试将它复制到
以下是Dockerfile供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #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"] |
如果您需要在构建时使用ssh,这将是一个更困难的问题。例如,如果您使用
我发现的解决方案是使用
建立命令
1 | $ docker build -t example --build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" --build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" --squash . |
文档文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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,并且在上有实验特性,那么可以将
结果发现,使用Ubuntu时,ssh配置不正确。你需要添加
1 | RUN echo" IdentityFile ~/.ssh/id_rsa">> /etc/ssh/ssh_config |
到你的dockerfile,以便让它识别你的ssh密钥。
Note: only use this approach for images that are private and will always be!
ssh密钥仍然存储在映像中,即使在添加后删除layer命令中的密钥(请参阅本文中的注释)。
在我的情况下,这是可以的,所以这是我正在使用的:
1 2 3 4 5 6 7 | # 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 \tStrictHostKeyChecking no ">> /root/.ssh/config |
如果您使用docker compose,一个简单的选择是像这样转发ssh代理:
1 2 3 4 5 6 | something: container_name: something volumes: - $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker environment: SSH_AUTH_SOCK: /ssh-agent |
为了向您注入ssh密钥,在一个容器中,您有多个解决方案:
使用dockerfile和
只是做一些像EDOCX1[1]
使用
扩展了PeterGrainger的答案,我可以使用Docker 17.05以后提供的多阶段构建。官方页面声明:
With multi-stage builds, you use multiple
FROM statements in your Dockerfile. EachFROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.
记住这一点,这里是我的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # 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} " | 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"] |
1 2 3 4 | .idea dist node_modules *.log |
构建图像的命令示例:
1 2 3 4 | $ 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密钥没有密码短语,只需指定空的
这就是它的工作原理:
1)。在第一阶段,只有
2)。第二阶段构建和缩小Web应用程序的源代码,并将其放在名为
1 | COPY --from=sources /app/ /app/ |
可能它也可以是以下行:
1 | COPY --from=sources /app/node_modules/ /app/node_modules/ |
我们只有来自第一个中间图像的
3)。在第三阶段,我们通过只包括名为
列出图像会得到如下输出:
1 2 3 4 5 | 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 |
其中非标记图像对应于第一个和第二个中间构建阶段。
如果你跑
1 | $ docker history ezze/geoport:0.6.0 --no-trunc |
在最终的图像中,您不会看到任何提到
这一行有问题:
1 | ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa |
指定要复制到图像中的文件时,只能使用相对路径(相对于DockerFile所在的目录)。因此,您应该使用:
1 | ADD id_rsa /root/.ssh/id_rsa |
把id_rsa文件放在dockerfile所在的目录中。
有关更多详细信息,请查看:http://docs.docker.io/reference/builder/add
码头集装箱应被视为自己的"服务"。要分离关注点,您应该分离功能:
1)数据应在数据容器中:使用链接卷将repo克隆到中。然后,可以将该数据容器链接到需要它的服务。
2)使用容器运行git克隆任务(即它的唯一任务是克隆),在运行数据容器时将其链接到它。
3)ssh密钥也一样:把它放在一个卷上(如上建议),需要时链接到git clone服务。
这样,克隆任务和密钥都是短暂的,只有在需要时才是活动的。
现在,如果您的应用程序本身是一个Git接口,那么您可能需要直接考虑GitHub或BitBucket REST API来完成您的工作:这就是它们的设计目的。
在Docker构建期间进行NPM安装时,我们也遇到了类似的问题。
从Daniel van Flymen的解决方案中得到启发,并将其与Git URL Rewrite结合,我们发现了一种更简单的方法来验证从私有Github Repos安装的NPM,我们使用OAuth2令牌而不是密钥。
在我们的例子中,NPM依赖项被指定为"git+https://github.com/…"
对于容器中的身份验证,需要重写URL以适用于ssh身份验证(ssh://[email protected]/)或令牌身份验证(https://[email protected]/)
构建命令:
1 | docker build -t sometag --build-arg GITHUB_TOKEN=$GITHUB_TOKEN . |
不幸的是,我在docker 1.9上,所以--squash选项还没有出现,最终需要添加它
Dockerfile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 |
这个问题真的很烦人。因为您不能在docker file上下文之外添加/复制任何文件,这意味着不可能只将~/.ssh/id_rsa链接到映像的/root/.ssh/id_rsa中,并且在构建docker映像的过程中,当您确实需要一个密钥来执行某些sshed操作时,例如从私有repo链接执行git克隆…。
不管怎样,我找到了一个解决办法,不是那么说服人,而是为我工作。
在你的文件里:
- 将此文件添加为/root/.ssh/id_rsa
- 做你想做的,比如Git克隆,作曲家…
- 最后是rm/root/.ssh/id_rsa
一次拍摄的脚本:
- 把你的钥匙放在存放dockerfile的文件夹里
- 码头施工
- rm复制的密钥
每当您需要从这个映像运行一个容器,并且有一些ssh需求时,只需为run命令添加-v,比如:
docker run-v~/.ssh/id_rsa:/root/.ssh/id_rsa--name container image命令
这个解决方案在您的项目源代码和构建的Docker映像中都不会产生私钥,因此不再需要担心安全问题。
将ssh身份验证套接字转发到容器:
1 2 3 4 5 | docker run --rm -ti \ -v $SSH_AUTH_SOCK:/tmp/ssh_auth.sock \ -e SSH_AUTH_SOCK=/tmp/ssh_auth.sock \ -w /src \ my_image |
您的脚本将能够执行
额外:如果您希望克隆的文件属于某个特定用户,则需要使用
您可以将一些附加变量发布到容器的环境中:
1 2 3 4 | docker run ... -e OWNER_USER=$(id -u) \ -e OWNER_GROUP=$(id -g) \ ... |
克隆之后,必须在离开容器之前执行
一种解决方案是使用以下选项将主机的ssh密钥装载到Docker中:
1 | docker run -v /home/<host user>/.ssh:/home/<docker user>/.ssh <image> |
类似于上述溶液。但是可以与非根用户一起使用。与Github完美合作。
'您可以有选择地让远程服务器像在服务器上运行一样访问本地ssh代理'
https://developer.github.com/guides/using-ssh-agent-forwarding/
您也可以在主机和容器之间链接.ssh目录,我不知道这个方法是否有任何安全隐患,但它可能是最简单的方法。这样的方法应该有效:
1 | $ sudo docker run -it -v /root/.ssh:/root/.ssh someimage bash |
请记住,docker使用sudo运行(除非您不使用),如果是这种情况,您将使用根ssh密钥。
今天我遇到了同样的问题,以前的文章中有一些修改过的版本,我发现这种方法对我更有用。
1 | docker run -it -v ~/.ssh/id_rsa:/root/.my-key:ro image /bin/bash |
(注意readonly标志,这样容器在任何情况下都不会弄乱我的ssh密钥。)
在容器内,我现在可以运行:
1 | ssh-agent bash -c"ssh-add ~/.my-key; git clone <gitrepourl> <target>" |
所以我不知道@kross指出的EDOCX1[0]错误
您可以使用共享文件夹将授权密钥传入容器,并使用Docker文件设置权限,如下所示:
1 2 3 4 5 6 7 8 9 10 | 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运行包含如下内容:与容器共享主机上的auth目录(保存授权的_密钥),然后打开ssh端口,该端口可以通过主机上的端口7001访问。
1 | -d -v /home/thatsme/dockerfiles/auth:/root/auth -–publish=127.0.0.1:7001:22 |
您可能需要查看https://github.com/jpetazzo/nsenter,这似乎是在容器上打开shell并在容器内执行命令的另一种方法。
在Docker(17.05)的较新版本中,您可以使用多阶段构建。这是最安全的选择,因为以前的构建只能由后续的构建使用,然后被销毁
有关详细信息,请参阅我的stackoverflow问题的答案。
如果您不关心ssh密钥的安全性,这里有很多好的答案。如果你这样做了,我找到的最佳答案是从上面的评论链接到Diegosandrim的这个Github评论。因此,其他人更可能看到它,只是为了防止回购协议消失,下面是该答案的编辑版本:
这里的大多数解决方案最终都会将私钥留在图像中。这很糟糕,因为任何有权访问映像的人都可以访问您的私钥。因为我们对
我们生成了一个预签名的URL来使用AWS S3 CLI访问密钥,并限制访问大约5分钟,我们将这个预签名的URL保存到repo目录中的一个文件中,然后在dockerfile中将其添加到映像中。
在dockerfile中,我们有一个执行所有这些步骤的run命令:使用预先命名的url获取ssh密钥,运行npm安装,然后删除ssh密钥。
通过在一个命令中这样做,ssh密钥不会存储在任何层中,但会存储预签名的URL,这不是问题,因为该URL在5分钟后将无效。
生成脚本如下所示:
1 2 3 | # build.sh aws s3 presign s3://my_bucket/my_key --expires-in 300 > ./pre_sign_url docker build -t my-service . |
Dockerfile如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 [email protected] || true && \ npm install --production && \ rm ./my_key && \ rm -rf ~/.ssh/* ENTRYPOINT ["npm","run"] CMD ["start"] |
我试图用另一种方式解决这个问题:向映像添加公共ssh密钥。但在我的试验中,我发现"docker cp"是用于从容器复制到主机的。creak回答中的第3项似乎是说可以使用docker cp将文件注入容器。请参阅https://docs.docker.com/engine/reference/commandline/cp/
摘录
Copy files/folders from a container's filesystem to the host path.
Paths are relative to the root of the filesystem.
1
2
3 Usage: docker cp CONTAINER:PATH HOSTPATH
Copy files/folders from the PATH to the HOSTPATH
You can use secrets to manage any sensitive data which a container
needs at runtime but you don’t want to store in the image or in source
control, such as:
- Usernames and passwords
- TLS certificates and keys
- SSH keys
- Other important data such as the name of a database or internal server
- Generic strings or binary content (up to 500 kb in size)
https://docs.docker.com/engine/swarm/secrets/
我试图弄清楚如何将签名密钥添加到一个容器中,以便在运行时(而不是构建)使用,然后遇到了这个问题。Docker秘密似乎是我的用例的解决方案,因为还没有人提到它,所以我会添加它。
毫无疑问,迟到了,这会让您的主机操作系统密钥可以在容器内即时根目录,如何?
1 | docker run -v ~/.ssh:/mnt -it my_image /bin/bash -c"ln -s /mnt /root/.ssh; ssh [email protected]" |
我不赞成使用dockerfile来安装密钥,因为容器的迭代可能会留下私钥。
实现这一点的一个简单而安全的方法是,不在Docker图像层中保存密钥,也不通过ssh_代理体操:
作为
下面指示您要将ssh目录作为卷装载:
通过添加以下行,确保容器的
运行时向容器公开本地用户的
或者在您的
最终的
1 2 3 4 5 6 7 8 9 10 | FROM node:6.9.1 RUN mkdir -p /root/.ssh RUN echo" IdentityFile /root/.ssh/id_rsa">> /etc/ssh/ssh_config VOLUME ["/root/.ssh" ] EXPOSE 3000 CMD ["launch" ] |
正如Eczajk在Daniel van Flymen的回答中所评论的那样,移除钥匙并使用
与Docker 18.09不同,您现在可以使用"构建秘密"功能。在我的例子中,我使用主机ssh密钥克隆了一个私有的git repo,在我的dockerfile中包含以下内容:
1 2 3 4 5 6 7 | # syntax=docker/dockerfile:experimental [...] RUN --mount=type=ssh git clone [...] [...] |
要使用它,您需要在运行
1 | export DOCKER_BUILDKIT=1 |
您需要将
有关此的详细信息,请访问:https://medium.com/@tonistigi/build-secrets-and-ssh-forwarding-in-docker-18-09-ae8161d066
最简单的方法是,获取一个launchpad帐户并使用:ssh import id
在一个运行中的docker容器中,可以使用docker-i(交互)选项发布ssh keygen。这将转发容器提示以在Docker容器内创建密钥。