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

Inject host's SSH keys into Docker Machine with Docker Compose

我在Mac OS X上使用docker和docker machine(使用默认的boot2docker machine),并使用docker compose设置开发环境。

假设其中一个容器称为"EDOCX1"(0)。现在我要做的是打电话:

1
docker-composer run stack ssh [email protected]

我的公钥(已添加到stackoverflow.com中并将用于对我进行身份验证)位于主机上。我希望此密钥可用于Docker机器容器,这样我就可以使用容器中的该密钥对stackoverflow进行身份验证。最好不要把我的钥匙复制到Docker机器上。

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


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

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

此外,您还可以使用ssh代理检查更高级的解决方案(我自己没有尝试过)。


Docker有一个称为Secrets的功能,在这里很有用。要使用它,可以在docker-compose.yml中添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
---
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中访问新的机密文件,如下所示:

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

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

When you grant a newly-created or running service access to a secret, the decrypted secret is mounted into the container in an in-memory filesystem

有关详细信息,请参阅:

  • https://docs.docker.com/engine/swarm/secrets网站/
  • https://docs.docker.com/compose/compose-file/机密


您可以转发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


如果你使用的是OSX和加密密钥,这将是PITA。这是我解决这个问题的步骤。

直截了当的方法

有人可能认为没问题。只需安装ssh文件夹:

1
2
3
4
...
volumes:
  - ~/.ssh:/root/.ssh:ro
...

这应该是可行的,对吧?

用户问题

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
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中不起作用,因为每次我们启动一个容器时输入一个密码是不切实际的。我们可以删除一个密码短语(见下面的示例),但存在安全问题。

1
2
3
4
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代理基本上是一个服务器,它监听一个称为"ssh auth sock"的特殊文件unix socket。您可以在系统上看到它的位置:

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

ssh客户机通过这个文件与ssh代理通信,这样您只需输入一次密码。一旦它未加密,ssh代理将把它存储在内存中,并根据请求发送到ssh客户机。我们能在Docker里用吗?当然,只需挂载该特殊文件并指定相应的环境变量:

1
2
3
4
5
environment:
  SSH_AUTH_SOCK: $SSH_AUTH_SOCK
  ...
volumes:
  - $SSH_AUTH_SOCK:$SSH_AUTH_SOCK

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

1
2
3
4
5
6
if [ -z"$SSH_AUTH_SOCK" ]; then
  echo"No ssh agent detected"
else
  echo $SSH_AUTH_SOCK
  ssh-add -l
fi

Mac Docker中的Unix套接字安装支持问题

不幸的是,对于OS X用户来说,Docker for Mac有许多缺点,其中之一就是它无法在Mac和Linux之间共享Unix套接字。在D4M Github中有一个未解决的问题。截至2019年2月,它仍在营业。

那么,这是一个死胡同吗?不,这里有一个简单的工作区。

ssh代理转发解决方案

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

使用Docker for Mac,我们可以使用一个智能技巧:使用tcpsh连接将ssh代理共享到Docker虚拟机,并将该文件从虚拟机装载到需要ssh连接的另一个容器。下面是演示解决方案的图片:

SSH forwarding

首先,我们通过TCP端口在Linux虚拟机的容器内创建一个到ssh服务器的ssh会话。我们在这里使用一个真正的ssh auth sock。

接下来,ssh服务器将我们的ssh密钥转发到容器上的ssh代理。ssh代理有一个unix套接字,它使用一个安装到linux vm上的位置。例如,Unix套接字在Linux中工作。Mac中不工作的Unix套接字文件不起作用。

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

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

结论

让ssh在docker工作可能会容易些。但这是可以做到的。而且将来可能会有所改善。至少Docker开发人员知道这个问题。甚至为有构建时间秘密的dockerfiles解决了这个问题。还有一个如何支持Unix域套接字的建议。