Terminus
Run SSH In Docker

Run SSH In Docker

Before getting started, if you want to inspect or execute commands in a running Docker container, you should read our other article on how to run a Bash shell in Docker.

[#write-a-dockerfile-for-password-based-auth] Writing a Dockerfile for an SSH server with password-based authentication [#write-a-dockerfile-for-password-based-auth]

To create a containerized SSH server running in Ubuntu with minimal configuration to which you can connect using a username/password pair, you can use the following Dockerfile:

FROM ubuntu

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

RUN mkdir /var/run/sshd

RUN echo "root:password" | chpasswd

RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config

CMD ["/usr/sbin/sshd", "-D"]

Where:

  • [.inline-code]FROM ubuntu[.inline-code] will set the base image as the latest available version of Ubuntu.
  • [.inline-code]RUN apt-get update && apt-get install -y openssh-server[.inline-code] will update the package index and install the [.inline-code]openssh-server[.inline-code] package used to run the SSH server.
  • [.inline-code]RUN mkdir /var/run/sshd[.inline-code] will create the [.inline-code]/var/run/sshd[.inline-code] directory used by the SSH server to store temporary runtime data.
  • [.inline-code]RUN echo "root:mypassword" | chpasswd[.inline-code] will set the password of the [.inline-code]root[.inline-code] user to [.inline-code]mypassword[.inline-code].
  • [.inline-code]RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config[.inline-code] will update the SSH configuration file to allow root login.
  • [.inline-code]CMD ["/usr/sbin/sshd", "-D"][.inline-code] will launch the SSH server as a background task when the container starts.

Writing a Dockerfile for an SSH server with SSH keys authentication

The second way to connect to an SSH server is to use a pair of public/private keys.

[#generate-ssh-keys] Generating a pair of SSH keys [#generate-ssh-keys]

To generate a new pair of SSH keys, you can use the [.inline-code]ssh-keygen[.inline-code] command as follows:

$ ssh-keygen -b 4096 -t rsa -f dockerkey

Where:

  • [.inline-code]-b 4096[.inline-code] specifies a key length of 4096 bits.
  • [.inline-code]-t rsa[.inline-code] specifies a RSA key type.
  • [.inline-code]-f dockerkey[.inline-code] specifies [.inline-code]dockerkey[.inline-code] as the name of the key file.

Upon execution, this command will generate two files: [.inline-code]dockerkey[.inline-code] containing the private key and [.inline-code]dockerkey.pub[.inline-code] containing the public key.

[#write-a-dockerfile-for-ssh-keys-auth] Writing the Dockerfile [#write-a-dockerfile-for-ssh-keys-auth]

To create a containerized SSH server running in Ubuntu with minimal configuration to which you can connect using a pair of SSH keys, you can use the following Dockerfile:

FROM ubuntu:latest

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

RUN mkdir /var/run/sshd

COPY ./dockerkey.pub /root/.ssh/authorized_keys

CMD ["/usr/sbin/sshd", "-D"]

Where:

  • COPY ./dockerkey.pub /root/.ssh/authorized_keys[.inline-code] will copy the public key generated using the [.inline-code]ssh-keygen[.inline-code] command from the local machine into the [.inline-code]/root/.ssh/authorized_keys[.inline-code] directory of the image.

[#build-the-ssh-image] Building and running the SSH container [#build-the-ssh-image]

To build the Dockerfile into a runnable Docker image, you can use the following [.inline-code]docker build[.inline-code] command:

$ docker build . -t ssh-host

Where:

  • [.inline-code].[.inline-code] specifies that the Dockerfile is located in the current directory.
  • [.inline-code]-t ssh-host[.inline-code] specifies the name of the Docker image.

Once built, you can launch a new container from this image using the following [.inline-code]docker run[.inline-code] command:

$ docker run -p 2222:22 ssh-host

Where:

  • [.inline-code]-p 2222:22[.inline-code] is used to map the port 2222 of the host to the SSH port 22 of the container.

[#connect-to-the-ssh-server] Connecting to the SSH container [#connect-to-the-ssh-server]

[#connect-with-a-username-password] Connecting with a username/password pair [#connect-with-a-username-password]

To connect to the SSH server running in the container using a username/password pair, you can use the [.inline-code]ssh[.inline-code] command as follows:

$ ssh <username>@<host> -p <port>

Where:

  • [.inline-code]username[.inline-code] is the username defined in the Dockerfile.
  • [.inline-code]host[.inline-code] is the IP or address of the machine the container is running on.
  • [.inline-code]port[.inline-code] is the port on the host system defined in the [.inline-code]docker run[.inline-code] command.

Once executed, you will be prompted to enter the password.

For example, the following command will connect to the SSH server running on your local machine through the port 2222 with the [.inline-code]root[.inline-code] user:

$ ssh root@localhost -p 2222

[#connect-with-ssh-keys] Connecting with a private key file [#connect-with-ssh-keys]

To connect to the SSH server running in the container using a private key file, you can use the [.inline-code]ssh[.inline-code] command with the [.inline-code]-i[.inline-code] flag as follows:

$ ssh -i <key> <username>@<host> -p <port>

Where:

  • [.inline-code]key[.inline-code] is the path to the private key file.
  • [.inline-code]username[.inline-code] is the username defined in the Dockerfile.
  • [.inline-code]host[.inline-code] is the IP or address of the machine the container is running on.
  • [.inline-code]port[.inline-code] is the port on the host system defined in the [.inline-code]docker run[.inline-code] command.

For example, the following command will connect to the SSH server running on your local machine through the port 2222 with the [.inline-code]root[.inline-code] user account using the [.inline-code]dockerkey[.inline-code] private key file:

$ ssh -i dockerkey root@localhost -p 2222

[#easily-recall-syntax-with-ai] Easily retrieve this syntax using the Warp AI feature [#easily-recall-syntax-with-ai]

If you’re using Warp as your terminal, you can easily retrieve this syntax using the Warp AI feature:

Entering [.inline-code]docker image with ssh and keys[.inline-code] in the AI question input will prompt a human-readable step by step guide including code snippets.

[#handle-terminal-signals] Handling terminal signals gracefully [#handle-terminal-signals]

As Linux tends to disregard the signals sent to the primary process, thus preventing it from being manually stopped using for instance [.inline-code]CTRL + C[.inline-code], you have to manually handle these signals by wrapping the main process in a script as follows:

#!/bin/bash

/usr/sbin/sshd -D &
pid="$!"
trap "kill -SIGTERM $pid" SIGINT SIGTERM
wait $pid

Where:

  • [.inline-code]/usr/sbin/sshd -D &[.inline-code] runs the SSH server as a background task.
  • [.inline-code]pid="$!"[.inline-code] stores the process ID of the server into the [.inline-code]pid[.inline-code] variable.
  • [.inline-code]trap[.inline-code] is used to catch and handle SIGINT and SIGTERM signals to kill the server.
  • [.inline-code]wait $pid[.inline-code] prevents the container from terminating while the server is still running.

In the server's Dockerfile, you can then replace this instruction:

CMD ["/usr/sbin/sshd", "-D"]

By the following one:

COPY ./init.sh /init.sh
RUN chmod +x /init.sh

CMD ["/init.sh"]

Where the new lines:

  • [.inline-code]COPY ./init.sh /init.sh[.inline-code] will copy the script into the container.
  • [.inline-code]RUN chmod +x /init.sh[.inline-code] will make the script executable.
  • [.inline-code]CMD[.inline-code] will set the script as the container's startup command.

[#secure-the-ssh-configuration] Securing the SSH server configuration [#secure-the-ssh-configuration]

When working with OpenSSH, you can mitigate potential exposure and enhance overall security measures by creating a configuration file named [.inline-code]sshd_config[.inline-code] containing the following properties:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
AcceptEnv LANG LC_*
Subsystem	sftp	/usr/lib/openssh/sftp-server

Where:

  • [.inline-code]PermitRootLogin no[.inline-code] will disable root login via SSH.
  • [.inline-code]PasswordAuthentication no[.inline-code] will disable password-based authentication, encouraging the use of more secure authentication methods such as SSH keys.
  • [.inline-code]KbdInteractiveAuthentication no[.inline-code] will disable keyboard-interactive authentication, which typically involves mechanisms like challenge-response authentication.
  • [.inline-code]AllowAgentForwarding no[.inline-code] will prevent the SSH client from using the SSH keys stored on the local host to connect to other remote servers.
  • [.inline-code]AllowTcpForwarding no[.inline-code] will prevent TCP traffic from being forwarded over the SSH connection.
  • [.inline-code]X11Forwarding no[.inline-code] will prevent GUI applications running on the remote server to display on the local machine
  • [.inline-code]AcceptEnv LANG LC_*[.inline-code] specifies that the only accepted environment variables from the client are related to language and locale settings.
  • [.inline-code]Subsystem sftp /usr/lib/openssh/sftp-server[.inline-code] will define the subsystem for handling SFTP requests.

And add the following instruction to your Dockerfile:

COPY ./sshd_config /etc/ssh/sshd_config

Which will copy the [.inline-code]sshd_config[.inline-code] file into the [.inline-code]/etc/ssh/[.inline-code] directory of the image at build time.

[#forward-the-ssh-agent] Forwarding the SSH agent to a Docker container [#forward-the-ssh-agent]

[.inline-code]ssh-agent[.inline-code] is a command-line tool used to simplify the management of SSH keys by keeping them encrypted in memory and automatically providing them to SSH when needed.

This allows users to avoid repeatedly typing passphrases when using SSH keys, and in the context of Docker, to avoid copying SSH keys into containers to connect to third-party services like Git.

[#create-an-ssh-client] Creating a containerized SSH client [#create-an-ssh-client]

To create a containerized SSH client that allows you to connect to an SSH server from within a Docker container, you can use the following Dockerfile:

FROM ubuntu

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

CMD ["sleep", "infinity"]

Where:

  • [.inline-code]FROM ubuntu[.inline-code] will set the base image as the latest available version of Ubuntu.
  • [.inline-code]RUN apt-get update && apt-get install -y openssh-client[.inline-code] will update the package index and install the OpenSSH client.
  • [.inline-code]CMD ["sleep", "infinity"][.inline-code] will make the container sleep indefinitely, keeping it running in the background without executing any other commands.

And build the Dockerfile into a Docker image named [.inline-code]ssh-client[.inline-code] using the following [.inline-code]docker build[.inline-code] command:

$ docker build . -t ssh-client

[#set-up-the-ssh-agent-for-docker] Setting up the SSH agent for a standard Docker installation [#set-up-the-ssh-agent-for-docker]

ssh-agent works by creating a socket file where tools can proxy requests to communicate commands and authenticate, so to forward the agent into a container you basically have to mount this socket into the container and set an environment variable with the path to the socket. This will make all requests inside the container for ssh-agent essentially be forwarded to the host session.

In a standard CLI installation of Docker you can create a new ssh-agent session by running:

To start the [.inline-code]ssh-agent[.inline-code] tool in the background and set the value of the [.inline-code]SSH_AUTH_SOCK[.inline-code] environment variable to the path of the SSH agent socket, you can use the following command:

$ eval $(ssh-agent)

Next, you can add SSH keys to the SSH agent session using the [.inline-code]ssh-add[.inline-code] command as follows:

$ ssh-add <path>

Where:

  • [.inline-code]path[.inline-code] is the relative or absolute path to the SSH key file.

Finally, you can run the [.inline-code]ssh-client[.inline-code] container in interactive mode using the following [.inline-code]docker run[.inline-code] command:

$ docker run -it -v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK -e SSH_AUTH_SOCK=$SSH_AUTH_SOCK ssh-client bash

Where: 

  • The [.inline-code]-v[.inline-code] flag is used to mount the SSH agent socket from the host into the container, whose location is stored in the [.inline-code]SSH_AUTH_SOCK[.inline-code] environment variable.
  • The [.inline-code]-e[.inline-code] flag is used to set the [.inline-code]SSH_AUTH_SOCK[.inline-code] environment variable so that the SSH client inside the container knows where to find the SSH agent socket.