SSH into a Docker Container

Just sometimes, it’s useful to SSH into a Docker Container. While docker exec or docker attach are usually sufficient to run commands in a container, sometimes you specifically need SSH. For example, to connect directly from a remote machine or when an application needs to run commands on your container. Most Docker images don’t come with the SSHd service installed so it is not possible to SSH to them. This post demonstrates how to install and run the SSHd service to an existing image so that you can connect to it.

Is this a good idea?

There are several reasons why you may not want or need to enable SSH on a Docker container. First, you often don’t need to run SSHd in Docker to do stuff you’d normally use SSH for. You can access logs, restart services and browse a container file system with docker attach or docker exec.

Second, it goes against the philosophy of Docker containers. A container should do exactly one thing. An nginx or HTTPd container serves web resources, a Redis or Mongo container provides NoSQL databases. A complex application is composed from several single-purpose containers orchestrated together. A container that that runs nginx and SSHd does two things. It’s not wrong but it’s just not the Docker way.

Finally, it may be a security risk. Exposing an additional means of accessing a container certainly increases the attack surface. You may also need to deal with password / key management.

That said, if you really want to SSH into a Docker Container, it’s not too hard.

Install SSHd

In this example, I’ll demonstrate how to enable SSH for an nginx container but the same technique applies to other container types. So let’s start with the default nginx image:

FROM nginx:stable

Then we need to install the SSHd service to the container:

RUN apt-get update && apt-get install -y openssh-server

Create a user account

We want a specific user account for the SSH connection. We’ll allow access to this account and no other.

RUN groupadd sshgroup && useradd -ms /bin/bash -g sshgroup sshuser

This creates an account called sshuser which belongs to the sshgroup. It creates a user directory in /home/sshuser and sets the user’s shell to Bash.

Next need to decide how to authenticate the user. It’s possible to allow username / password authentication but this is inadvisable. It bakes the password into the image definition and is generally less secure.

# NOT RECOMMENDED: Set a password on the sshuser account
RUN echo 'sshuser:Pa$$word' | chpasswd

This sets a password of Pa$$word on our new account.

Create an SSH key

A better option is to use an SSH key. Plenty of good resources exist to help you do this. So let’s assume you’ve created a public key called id_rsa.pub and the private key called id_rsa. Copy the public key into your container in a file called ~/.ssh/authorized_keys.

ARG home=/home/sshuser
RUN mkdir $home/.ssh
COPY id_rsa.pub $home/.ssh/authorized_keys
RUN chown sshuser:sshgroup $home/.ssh/authorized_keys && \
    chmod 600 $home/.ssh/authorized_keys

Be sure to keep the private key private! Anyone with access to the private key will be able to access the container so this should not be shared or copied into the container.

Start the SSH service

This last step is a little fiddly. We can use CMD instruction to run a command when the container starts. An image can include only one CMD instruction but it’s already used to start nginx. The simple solution is to use && to run multiple commands. First we start the SSH service, then we run nginx:

CMD service ssh start && nginx -g 'daemon off;'

SSH into a Docker Container

We now have a container image that will allow SSH access. So let’s start it up and try it. I like using docker-compose to do my build, config and startup with a single simple command. My docker-compose.yml file looks like this:

version: '3'
services:
  nginx-ssh:
    build: .
    ports:
      - "80:80"
      - "1022:22"

I can run it like this:

docker-compose up -d --build

And then I can SSH into a docker container like this:

ssh [email protected] -p 1022 -i id_rsa

That is, connect to localhost as the sshuser and use the id_rsa private key to authenticate. We specify port 1022 because that’s the port on our machine (localhost, the Docker host) that forwards to port 22 on the container.

After running this command we have a secure shell (Bash) on the nginx Docker container.

The full source code for this example is in GitHub.

2 Comments

  • April 26, 2020 - 11:50 am | Permalink

    Was wondering if you could set up $home/.ssh as a volume, that way you could add others keys.

    • April 26, 2020 - 8:32 pm | Permalink

      That’s a great suggestion Ben! Yes, if you set up $home/.ssh as a volume you could add SSH keys at run time rather than building them in at image build time.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *