Saturday, October 23, 2021

[SOLVED] Can I give a docker container ssh access to a host without also giving it the private key?

Issue

Request

This docker command injects my ssh key and username into a container, connects that container to a remote host, and then runs: echo hello world on the host:

docker run --rm \
    -e "host=the.host" \
    -e "user=my.user.name" \
    -v "/home/me/.ssh/id_rsa:/key" \
    ubuntu:18.04 /bin/bash -c \
        'apt update && apt install -y ssh \
         && ssh -o StrictHostKeyChecking=no $user@$host -i /key
             echo hello world'

I want the command to be able to connect to the remote host, but I don't want it to be able to cat /key and see my ssh key.

What changes can I make to achieve this?


Context

I'm writing a test runner. My code is responsible for determining which tests can be run against which hosts, but the test itself might not be written by me (it gets pulled in from a git repo when my test runner starts up).

I am not worried about my colleagues abusing the server with their test code because that abuse would be visible in source control. They are semi-trusted in this case. I am worried about somebody writing a test which causes my ssh key to appear in log output somewhere.

Ideally, I would set up the ssh connection first, then create the container--somehow granting it access to the connection, but not the key.


Solution

The feature I needed was SSH Multiplexing which I learned about here

This file goes in the docker image at ~/.ssh.config

Host foo
    ControlMaster auto
    ControlPath ~/.ssh/cm_socket/%r@%h:%p

And this file goes on the host:

Host foo
    HostName foo.bar.com
    User my.username
    IdentityFile /path/to/key
    IdentitiesOnly yes
    ControlMaster auto
    ControlPath ~/.ssh/cm_socket/%r@%h:%p

I called the image keylesssh, so this command creates a container which doesn't have the key, but does have the folder which will contain a socket if there is an existing connection.

docker run --rm \
    -v "/home/matt/.ssh/cm_socket:/root/.ssh/cm_socket" \
        keylesssh /bin/bash -c 'ssh dev1 hostname'

It comes together like this:

  1. I open an ssh connection from the machine that is hosting the docker daemon to the remote host
  2. SSH will creates a socket and put it in ~/.ssh/cm_socket with a predictable name
  3. I create a container, sharing that folder with it
  4. The container tries to ssh to the host, notices that the socket exists, and uses the existing connection without authenticating--no key required
  5. Once the test has finished running, the container shuts down
  6. When my code notices the container shutting down, I kill the master ssh connection

I know it works because the hostname command resolves the hostname of the remote server, not of the container or of the docker host.



Answered By - MatrixManAtYrService