使用 Docker Compose 将主机的 SSH 密钥注入 Docker Machine

Posted

技术标签:

【中文标题】使用 Docker Compose 将主机的 SSH 密钥注入 Docker Machine【英文标题】:Inject host's SSH keys into Docker Machine with Docker Compose 【发布时间】:2016-04-28 05:08:27 【问题描述】:

我在 Mac OS X 上使用 Docker 和 Docker 机器(使用默认的 boot2docker 机器),并且我使用 docker-compose 来设置我的开发环境。

假设其中一个容器名为“stack”。现在我想做的是调用:

docker-composer run stack ssh user@***.com

我的公钥(已添加到***.com 并将用于验证我的身份)位于主机上。我希望 Docker Machine 容器可以使用此密钥,以便我能够使用容器内的该密钥对 *** 进行身份验证。最好不要将我的密钥物理复制到 Docker Machine。

有没有办法做到这一点?另外,如果我的钥匙有密码保护,有什么办法可以解锁一次,这样每次注射后我就不必手动输入密码了?

【问题讨论】:

【参考方案1】:

您可以将其添加到您的 docker-compose.yml(假设您在容器中的用户是 root):

volumes:
    - ~/.ssh:/root/.ssh

你也可以查看more advanced solution with ssh agent(我自己没试过)

【讨论】:

请注意,如果您的 SSH 密钥属于主机上的用户,此解决方案可能会失败 虽然您可以在运行时执行此操作,但您将无法在构建时访问该卷。 也不能在 Windows 上工作。 Permissions 0755 for '/root/.ssh/id_rsa' are too open. 您可以在此 sn-p 的末尾添加 :ro 以只读方式挂载密钥,这通常会绕过来自 SSH 的有关权限的警告,但如果您的密钥有密码(真的应该)你仍然需要使用 ssh-agent 做一些诡计。 另一个重要的注意事项是,以这个为卷【参考方案2】:

您可以转发 SSH 代理:

something:
    container_name: something
    volumes:
        - $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker
    environment:
        SSH_AUTH_SOCK: /ssh-agent

【讨论】:

你已经在这里给出了答案***.com/a/36648428/228370 还有一个注意事项:正如@joe-saw 指出的那样,这对 mac 不起作用,因为 unix 域套接字没有被代理 如何获取本机ssh代理目录/ssh-agent?【参考方案3】:

警告:此功能在 Docker Compose 中的支持似乎有限,并且更多地是为 Docker Swarm 设计的。

(我没有检查确定,但是)我目前的印象是:

在 Docker Compose 中,机密只是绑定挂载卷,因此与卷相比没有额外的安全性 使用 Linux 主机更改机密权限的能力可能受到限制

查看答案 cmets 了解更多详情。


Docker 有一个名为 secrets 的功能,在这里可以提供帮助。要使用它,可以将以下代码添加到docker-compose.yml

---
version: '3.1' # Note the minimum file version for this feature to work
services:
  stack:
    ...
    secrets:
      - host_ssh_key

secrets:
  host_ssh_key:
    file: ~/.ssh/id_rsa

然后可以在Dockerfile中访问新的秘密文件,如下所示:

RUN mkdir ~/.ssh && ln -s /run/secrets/host_ssh_key ~/.ssh/id_rsa

秘密文件不会被复制到容器中:

当您授予新创建或正在运行的服务对密钥的访问权限时,解密的密钥将挂载到内存文件系统中的容器中

更多详情请参考:

https://docs.docker.com/engine/swarm/secrets/ https://docs.docker.com/compose/compose-file/compose-file-v3/#secrets

【讨论】:

嗨 astyagun :我同意使用 Secrets 功能是解决此问题的好方法,但是为了使您的答案更好,在此处包含答案的基本部分并提供以下链接会很有用参考。 谢谢。添加示例 不幸的是链接 id_rsa 不起作用,SSH 将返回错误,因为链接具有不同的权限。因此,您仍然需要复制文件并保留 RO 权限。但是好的一点是,你可以使用ssh -i /run/secrets/host_ssh_key ...参数,因为秘密文件有RO权限 @Kostanos 会在创建链接帮助后添加chown -h $(id -u):$(id -g) ~/.ssh/id_rsa 吗? 请注意,秘密是 github.com/docker/compose/issues/4994 和 ***.com/questions/49955542/… 中指出的“群模式功能”。它不会在 docker-compose up 中挂载秘密。【参考方案4】:

如果您使用的是 OS X 和加密密钥,这将是 PITA。 Here are the steps我想通了。

直截了当的方法

人们可能认为没有问题。只需挂载您的 ssh 文件夹:

...
volumes:
  - ~/.ssh:/root/.ssh:ro
...

这应该可以了吧?

用户问题

接下来我们会注意到我们使用了错误的用户 ID。好的,我们将编写一个脚本来复制和更改 ssh 密钥的所有者。我们还将在配置中设置 ssh 用户,以便 ssh 服务器知道谁在连接。

...
volumes:
  - ~/.ssh:/root/.ssh-keys:ro
command: sh -c ‘./.ssh-keys.sh && ...’
environment:
  SSH_USER: $USER
...

# ssh-keys.sh
mkdir -p ~/.ssh
cp -r /root/.ssh-keys/* ~/.ssh/
chown -R $(id -u):$(id -g) ~/.ssh

cat <<EOF >> ~/.ssh/config
  User $SSH_USER
EOF

SSH 密钥密码问题

在我们公司,我们使用密码保护 SSH 密钥。这在 docker 中是行不通的,因为每次启动容器时都输入密码是不切实际的。 我们可以删除密码(参见下面的示例),但存在安全问题。

openssl rsa -in id_rsa -out id_rsa2
# enter passphrase
# replace passphrase-encrypted key with plaintext key:
mv id_rsa2 id_rsa

SSH代理解决方案

您可能已经注意到,在本地您不需要每次需要 ssh 访问时都输入密码。这是为什么? 这就是 SSH 代理的用途。 SSH 代理基本上是一个服务器,它监听一个特殊的文件,unix 套接字,称为“ssh auth sock”。您可以在系统上看到它的位置:

echo $SSH_AUTH_SOCK
# /run/user/1000/keyring-AvTfL3/ssh

SSH 客户端通过此文件与 SSH 代理通信,因此您只需输入一次密码。解密后,SSH 代理会将其存储在内存中,并根据请求发送给 SSH 客户端。 我们可以在 Docker 中使用它吗?当然,只需挂载该特殊文件并指定相应的环境变量:

environment:
  SSH_AUTH_SOCK: $SSH_AUTH_SOCK
  ...
volumes:
  - $SSH_AUTH_SOCK:$SSH_AUTH_SOCK

在这种情况下,我们甚至不需要复制密钥。 要确认密钥可用,我们可以使用 ssh-add 实用程序:

if [ -z "$SSH_AUTH_SOCK" ]; then
  echo "No ssh agent detected"
else
  echo $SSH_AUTH_SOCK
  ssh-add -l
fi

Docker for Mac 支持 unix socket mount 的问题

不幸的是,对于 OS X 用户来说,Docker for Mac 有许多缺点,其中之一是它无法在 Mac 和 Linux 之间共享 Unix 套接字。有一个open issue in D4M Github。截至 2019 年 2 月,它仍然开放。

那么,这是一个死胡同吗?不,有一个 hacky 解决方法。

SSH代理转发解决方案

幸运的是,这个问题并不新鲜。早在 Docker 之前,就有一种方法可以在远程 ssh 会话中使用本地 ssh 密钥。这称为 ssh 代理转发。这个想法很简单:您通过 ssh 连接到远程服务器,您可以在那里使用所有相同的远程服务器,从而共享您的密钥。

使用 Docker for Mac,我们可以使用一个聪明的技巧:使用 TCP ssh 连接将 ssh 代理共享到 docker 虚拟机,然后将该文件从虚拟机挂载到另一个需要 SSH 连接的容器。这是一个演示解决方案的图片:

首先,我们通过 TCP 端口在 linux VM 内的容器内创建与 ssh 服务器的 ssh 会话。我们在这里使用真正的 ssh auth sock。

接下来,ssh 服务器将我们的 ssh 密钥转发到该容器上的 ssh 代理。 SSH 代理有一个 Unix 套接字,它使用安装到 Linux VM 的位置。 IE。 Unix 套接字在 Linux 中工作。 Mac 中非工作的 Unix 套接字文件无效。

之后,我们使用 SSH 客户端创建有用的容器。我们共享本地 SSH 会话使用的 Unix 套接字文件。

有很多脚本可以简化这个过程: https://github.com/avsm/docker-ssh-agent-forward

结论

让 SSH 在 Docker 中工作可能会更容易。但这是可以完成的。并且将来可能会得到改进。至少 Docker 开发人员意识到了这个问题。甚至solved it for Dockerfiles with build time secrets。还有there's a suggestion如何支持Unix域套接字。

【讨论】:

【参考方案5】:

Docker for Mac 现在在 macOS 上supports mounting the ssh agent socket。

【讨论】:

不幸的是,Docker 方面的文档已经更改,没有提到osxfs。典型的 Docker,维护不善或重定向的文档。这里至少是您的链接的archive.org 版本:web.archive.org/web/20200414203352/https://docs.docker.com/… 对于后代,关键点:在卷安装中需要type: bind【参考方案6】:

您可以使用多阶段构建来构建容器这是您可以采用的方法:-

Stage 1 building an image with ssh

FROM ubuntu as sshImage
LABEL stage=sshImage
ARG SSH_PRIVATE_KEY
WORKDIR /root/temp

RUN apt-get update && \
    apt-get install -y git npm 

RUN mkdir /root/.ssh/ &&\
    echo "$SSH_PRIVATE_KEY" > /root/.ssh/id_rsa &&\
    chmod 600 /root/.ssh/id_rsa &&\
    touch /root/.ssh/known_hosts &&\
    ssh-keyscan github.com >> /root/.ssh/known_hosts

COPY package*.json ./

RUN npm install

RUN cp -R node_modules prod_node_modules

第 2 阶段:构建您的容器

FROM node:10-alpine

RUN mkdir -p /usr/app

WORKDIR /usr/app

COPY ./ ./

COPY --from=sshImage /root/temp/prod_node_modules ./node_modules

EXPOSE 3006

CMD ["npm", "run", "dev"] 

在 compose 文件中添加 env 属性:

environment:
      - SSH_PRIVATE_KEY=$SSH_PRIVATE_KEY

然后像这样从构建脚本传递参数:

docker-compose build --build-arg SSH_PRIVATE_KEY="$(cat ~/.ssh/id_rsa)"

为了安全起见,移除中间容器。这将帮助你欢呼。

【讨论】:

最后一部分可以添加到docker_compose.yml build.args 请注意,虽然密钥现在存储在图像中,即使您使用多级,密钥仍然存在于其中一层中。潜在的安全风险。

以上是关于使用 Docker Compose 将主机的 SSH 密钥注入 Docker Machine的主要内容,如果未能解决你的问题,请参考以下文章

带有 django 的 Docker-compose 无法将主机名“db”转换为地址:名称或服务未知

无法使用 Postgres、Docker Compose 和 Psycopg2 将主机名“db”转换为地址

使用 docker-compose 时如何在容器内使用主机用户修改卷文件

如何在 Docker-Compose 中一起使用主机网络和任何其他用户定义的网络?

docker-compose将mysql传输到另一台主机

无法使用nginx-proxy和dnsmasq在带有docker-compose的容器之间访问虚拟主机