Using SSH private keys securely when building Docker images
Use case: SSH keys required to access private GitHub repository
Accessing private GitHub repositories is easy: all you need is to generate RSA key, and add its public part as
deploy key to GitHub repository of interest (see
Then on client side, you need to load private part of the key and…well, that’s it - you will be able to clone from the private repository.
The problem is that if you
ADD private key to your Docker image (in
Dockerfile), it will be persisted as a layer. And even if you then
remove the private key (once you are done pulling from the repository), your private key will still be available (that’s
how layers work - key will be missing from the latest layer, but still available in the original layer it was added to).
And that is a huge no-no!
There are number of solutions available:
- pull the code before building, so that you do now need to use SSH key at all
- squashing layers, so that temporary layer, which was created when SSH key was added, is merged (and thus is wiped out)
- forward SSH agent (sounds nice, but is hard to accomplish)
- go as far as to use Habitus
- make a note that Habitus solution is very nice, but a bit of an overkill, and proceed from there to..
Which is pretty simple: expose any required artifacts using local web-server, and fetch them into the image using
Without further ado, here is Dockerfile:
1FROM alpine:3.4 2 3RUN \ 4 apk add --update git openssh-client && \ 5 6 # add SSH key 7 wget -O /tmp/id_rsa http://192.168.99.1:8080/some_id_rsa && \ 8 chmod 600 /tmp/id_rsa && \ 9 eval $(ssh-agent) && \ 10 echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \ 11 ssh-add /tmp/id_rsa && \ 12 13 # clone some private repo 14 git clone --depth 1 --branch develop email@example.com:farazdagi/some-repo.git && \ 15 16 # install and cleanup 17 # ..some installation instructions 18 apk del git openssh-client && \ 19 rm /tmp/id_rsa 20 21EXPOSE 8545 22 23ENTRYPOINT ["/foo"] 24
- As you see, everything is within a single
RUNinstruction - which means single layer added for all the cloning, installing and cleaning up.
- Another point of interest is that I rely on
some_id_rsa(which is the private key). And IP I use is default VM IP address of Mac that runs
docker-machine(I am on OSX). On Linux box, it can be Docker network IP address of host machine.
Now, the last thing before we
docker build is to make sure that RSA keys are exposed indeed.
You can use Python’s SimpleHTTPServer or PHP’s bundled server, or whatever you have experience with.
I’ve used PHP’s internal server:
1cd ~/folder-that-has-your-deploy-id-rsa 2php -S 192.168.99.1:8080 # that's it, all files in the directory are exposed
The benefits are immense:
- solution is trivial to setup and easy to use (it will work with other “secrets” too)
- you do not need to post-process your images (to squash layers)
- you are still using conventional Dockerfile - no magic here, just
wgetcommand to obtain artifacts
- Sajadi, Khash Using SSH Private keys securely in Docker build