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

后端 未结 6 1426
故里飘歌
故里飘歌 2020-12-13 13:00

I am using Docker on Mac OS X with Docker Machine (with the default boot2docker machine), and I use docker-compose to setup my development environment.

Let\'s say th

相关标签:
6条回答
  • 2020-12-13 13:40

    If you're using OS X and encrypted keys this is going to be PITA. Here are the steps I went through figuring this out.

    Straightforward approach

    One might think that there’s no problem. Just mount your ssh folder:

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

    This should be working, right?

    User problem

    Next thing we’ll notice is that we’re using the wrong user id. Fine, we’ll write a script to copy and change the owner of ssh keys. We’ll also set ssh user in config so that ssh server knows who’s connecting.

    ...
    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 key passphrase problem

    In our company we protect SSH keys using a passphrase. That wouldn’t work in docker since it’s impractical to enter a passphrase each time we start a container. We could remove a passphrase (see example below), but there’s a security concern.

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

    SSH agent solution

    You may have noticed that locally you don’t need to enter a passphrase each time you need ssh access. Why is that? That’s what SSH agent is for. SSH agent is basically a server which listens to a special file, unix socket, called “ssh auth sock”. You can see its location on your system:

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

    SSH client communicates with SSH agent through this file so that you’d enter passphrase only once. Once it’s unencrypted, SSH agent will store it in memory and send to SSH client on request. Can we use that in Docker? Sure, just mount that special file and specify a corresponding environment variable:

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

    We don’t even need to copy keys in this case. To confirm that keys are available we can use ssh-add utility:

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

    The problem of unix socket mount support in Docker for Mac

    Unfortunately for OS X users, Docker for Mac has a number of shortcomings, one of which is its inability to share Unix sockets between Mac and Linux. There’s an open issue in D4M Github. As of February 2019 it’s still open.

    So, is that a dead end? No, there is a hacky workaround.

    SSH agent forwarding solution

    Luckily, this issue isn’t new. Long before Docker there was a way to use local ssh keys within a remote ssh session. This is called ssh agent forwarding. The idea is simple: you connect to a remote server through ssh and you can use all the same remote servers there, thus sharing your keys.

    With Docker for Mac we can use a smart trick: share ssh agent to the docker virtual machine using TCP ssh connection, and mount that file from virtual machine to another container where we need that SSH connection. Here’s a picture to demonstrate the solution:

    First, we create an ssh session to the ssh server inside a container inside a linux VM through a TCP port. We use a real ssh auth sock here.

    Next, ssh server forwards our ssh keys to ssh agent on that container. SSH agent has a Unix socket which uses a location mounted to Linux VM. I.e. Unix socket works in Linux. Non-working Unix socket file in Mac has no effect.

    After that we create our useful container with an SSH client. We share the Unix socket file which our local SSH session uses.

    There’s a bunch of scripts that simplifies that process: https://github.com/avsm/docker-ssh-agent-forward

    Conclusion

    Getting SSH to work in Docker could’ve been easier. But it can be done. And it’ll likely to be improved in the future. At least Docker developers are aware of this issue. And even solved it for Dockerfiles with build time secrets. And there's a suggestion how to support Unix domain sockets.

    0 讨论(0)
  • 2020-12-13 13:44

    You can use multi stage build to build containers This is the approach you can take :-

    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
    

    Stage 2: build your container

    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"] 
    

    add env attribute in your compose file:

    environment:
          - SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY}
    

    then pass args from build script like this:

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

    And remove the intermediate container it for security. This Will help you cheers.

    0 讨论(0)
  • 2020-12-13 13:50

    Docker has a feature called secrets, which can be helpful here. To use it one could add the following code to 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
    

    Then the new secret file can be accessed in Dockerfile like this:

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

    Secret files won't be copied into container:

    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

    For more details please refer to:

    • https://docs.docker.com/engine/swarm/secrets/
    • https://docs.docker.com/compose/compose-file/#secrets
    0 讨论(0)
  • 2020-12-13 13:51

    You can add this to your docker-compose.yml (assuming your user inside container is root):

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

    Also you can check for more advanced solution with ssh agent (I did not tried it myself)

    0 讨论(0)
  • 2020-12-13 13:51

    You can forward SSH agent:

    something:
        container_name: something
        volumes:
            - $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker
        environment:
            SSH_AUTH_SOCK: /ssh-agent
    
    0 讨论(0)
  • 2020-12-13 13:57

    Docker for Mac now supports mounting the ssh agent socket on macOS.

    0 讨论(0)
提交回复
热议问题