SSH in Docker Alpine

I have a Jingo wiki, it's where I keep notes on all aspects. Mostly code and configuration snippets for things I had to learn the hard way and don't want to have to learn again. There's also stuff in there about photography, and other interests I have.

Jingo works by storing everything in markdown files and automatically keeping things backed up using a Git remote repository. Because my wiki is private, I have a private repository. Access to this is controlled using SSH keys.

Jingo is built in NodeJS and requires the NodeJS runtime. Traditionally I have maintained two remote VPS instances, one with Node installed and one with PHP installed for running various personal webapps. But I decided against this and decided to consolodate to one server running each environment in Docker containers. This was prompted by some annoything errors I was getting when running Jingo under NodeJS v14.x. I wanted to run it under v12, but still have other apps run using v14.

Ordinarily that's not a problem, but because Jingo requires a secure connection with Git, I needed to get SSH key access inside the container. Before doing this, ensure you've created your SSH keys on the server.

First approach

My first approach was to use SSH agent, and Docker's ability to forward this using a mount-type of ssh. Here's the short version:

# syntax=docker/dockerfile:1.0.0-experimental
FROM node:12-alpine
RUN apk update && apk add --no-cache git openssh-client
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts
RUN --mount=type=ssh,id=id_rsa git clone [email protected]:<workspace>/<my-repo>.git /app
RUN npm i
CMD npm start

The first line above tells Docker that we'll be using some experimental features, in our case the mounting of ssh. For this to work we need to add the following to /etc/docker/daemon.json:

{
  "features" : {
    "buildkit" : true
  }
}

Line three, installs Git and OpenSSH. Line four creates the SSH directory and puts the existing known key in the known_hosts file inside the container. In my case, BitBucket.

Line five mounts the SSH-Agent and makes it available at build time. An important part to note here is the ID.

Make sure your key is added to the SSH-Agent on your system, then pass the key with the corresponding ID to the build command:

docker build --ssh id_rsa="/home/<username>/.ssh/id_rsa" -t <container-tag> .

This works fine, but it doesn't provide the SSH key when running the container.

Second approach

Try as I might, I just could not get SSH_SOCK forwarding to work. I found I was forced to copy my SSH key in to the container at build time. I know this is less than secure. I know there is a risk of leaving it in the directory, and even committing it to the repository. But this is a rare occurrence. Very rarely will I need to git push inside a container.

With that said, I created a build script that copies the private key to the current directory, is ADDED in the build-script, then deleted. We have to do this because Docker cannot ADD a file from outside the working directory. I tried to do it with symlinks, but it kept failing.

#!/bin/bash

cp /home/<username>/.ssh/id_rsa ./
docker build --no-cache -t <container-name> .
rm ./id_rsa

With that in place, the Dockerfile looks like this:

FROM node:12-alpine
RUN apk update && apk add --no-cache git openssh-client
WORKDIR /app
RUN git config --global user.email "<email address>"
RUN git config --global user.name "<user name>"
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan bitbucket.org >> ~/.ssh/known_hosts
ADD ./id_rsa /root/.ssh/id_rsa
RUN git clone [email protected]:<workspace>/<repository>.git /app
RUN echo "StrictHostKeyChecking no " > /root/.ssh/config
RUN npm i
CMD npm start

I'm sure there must be a better way to do this, and if you feel inclined please get in touch and point me in the right direction. This is a rare need I had to have automated git push inside a container. In most cases having the ability to clone during build is sufficient, in which case the first approach described above should work.