SSH server in containers

This is a write-up of my encounter with https://docs.linuxserver.io/images/docker-openssh-server/. I needed to copy few files from one laptop to another (Mac). Both laptops had file sharing disabled (ie no ssh server) by management software. Given I use containers almost every day, I decided to run an ssh server on a custom (non-privileged) port on destination laptop and copy the files over.

Disclaimer

There are easier and faster ways to copy from one machine to another. This method is neither.

Setup

I was thinking of simply pulling a base image and installing OpenSSH server on it. Luckily, I didn't have to - a google search ("docker hub openssh") yielded a link to linuxserver.io site full of goodies - list of widely used container images with nice documentation.

This works as long as you have docker running on your machine.

  1. Create an ssh key on the client and copy its public key over to machine hosting the openssh-server
    • Never share SSH private keys (or any private keys) with users/systems you do not know or trust.
  2. Run the server on port 8022
  3. Copy with scp

SSH keys

This is optional. You could just use your existing keys, but I recommend creating dedicated ones in case you accidentally expose the private key (for example, you commit it into a repo when blogging about it).

If you don't have the ssh-keygen utility installed, you can generate one with the container image:

$ mkdir mykeys
$ docker run --rm --entrypoint=/bin/sh -v "$(pwd)/mykeys:/keys" linuxserver/openssh-server -c 'ssh-keygen -q -f /keys/id_rsa -N ""'
$ ls -lR mykeys
-rw-------  1 user  usergroup  2602 Jan 21 14:52 id_rsa
-rw-r--r--  1 user  usergroup   571 Jan 21 14:52 id_rsa.pub

The id_rsa.pub is the public key that needs to be transferred to target machine.

For prompted options, you can choose to use the bundled generator script

$ docker run --rm -it --entrypoint /keygen.sh linuxserver/openssh-server
Please select your key type to generate
1.) ecdsa
2.) rsa
3.) ed25519
4.) dsa
[default ecdsa]:
...

Running the server

On the target machine, create a directory named openssh under your home directory. Inside it create a directory for public SSH keys and another one for putting the sent files - let's name them pubkeys and dropzone respectively. Put the SSH public key from the previous step (file name can be arbitrary).

$ mkdir -p $HOME/openssh/{pubkeys,dropzone}
$ cp <mykey.pub> $HOME/openssh/pubkeys/pubkey

Instead of putting these parameters into command line, let's run it with docker compose. Create a docker-compose.yml file (anywhere on the system, or under $HOME/openssh) with following contents:

# ~/openssh/docker-compose.yml
---
version: "2.1"
services:
  openssh-server:
    image: lscr.io/linuxserver/openssh-server:latest
    container_name: openssh-server
    hostname: openssh-server #optional
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - PUBLIC_KEY_DIR=/config/pubkeys
      - SUDO_ACCESS=false
      - USER_NAME=johndoe
    volumes:
      - ${HOME}/openssh/:/config
    ports:
      - 8022:2222
    restart: unless-stopped

The PUID and PGID parameters can be ignored as docker will use local user's UID and GID for copied files. The USER_NAME will be used on the client side to establish the connection.

Now it's time to launch the server with docker compose up. Add -d flag at the end to daemonize the process.

Copying the files from the client

Rest is a regular SSH transfer. Assuming the private key is stored under ~/.ssh/mykey, you can copy a file with scp:

$ scp -F /dev/null -o UserKnowHostsFile=/dev/null -o StrictHostKeyChecking=no -P 8022 -i ~/.ssh/mykey archive.zip johndoe@192.168.1.2:dropzone/

What are these /dev/nulls? We are telling the scp not to read any ssh_config (if there is one under ~/.ssh which might affect connection), write the host info into nothing and not to check server's host key (as the key will be rotating with each container boot-up). As this was a test usage in local network, I did not care about validating the server. In work or production environments, you need to pay attention to those.

Conclusion

With this post, I wanted to highlight using containerized tools to accomplish task without relying on system applications. In reality, I was annoyed by not being able to simply copy files between laptops (btw, external cards were disabled too). I could have easily uploaded files to cloud and download it from the other side, but did the hard way from a principle.