Docker - Using ssh in builds

Intro

Docker Image를 build 할 때 private github repository를 pull하는 등 ssh 인증이 필요한 상황이 있습니다.

ARG SSH_PRIVATE_KEY
RUN mkdir /root/.ssh/
RUN echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa
# something works
RUN rm /root/.ssh/id_rsa

혹시 위와 같은 단순한 방법을 사용하고 있었다면 당장 해당 key를 expire 시켜야 할 수도 있습니다.

필요한 작업을 마친 후 rm 명령어로 해당 ssh key를 지웠다 하더라도 다른 이전 레이어들에 파일의 정보가 그대로 남아있을 수 있고, 무엇보다 ARG 로 전달된 값들은 docker history 명령어 만으로도 그 기록을 모두 확인할 수 있기 때문입니다.

해당 image를 아무곳에도 공개하지 않고 혼자만 쓸 경우에는 괜찮을 수 있지만, 대부분의 경우 그렇지 않고 언제든 실수가 발생할 수 있기 때문에 docker build 에서 안전하고 효율적으로 ssh key를 사용하는 방법을 소개합니다.

  1. Multi-stage build
  2. Using ssh with buildkit (recommended)
  3. Using secret with buildkit

Multi-stage build

Multi-stage build는 하나의 docker file에서 여러개의 docker image를 build 하는 방법입니다.

최종적으로 생성되는 이미지는 그 전에 존재하던 이미지들에 대한 어떠한 정보도 남아있지 않기 때문에, 이 점을 이용하여 안전하게 ssh key를 사용할 수 있습니다.

전체적인 flow와 예시 코드는 아래와 같습니다.

  • Image1: Create or copy SSH key → Use SSH key (git clone)
  • Image2: Copy cloned repository files from Image1 → do your stuff
# Example dockerfile
## Image1: image for clone private repository
FROM ubuntu as intermediate

# 1-1. install git
RUN apt update && apt install git-all -y

# 1-2. add credentials on build (You can pass to ARG or copy from local)
ARG SSH_PRIVATE_KEY
RUN mkdir /root/.ssh/
RUN echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa

# 1-3. add github domain to known hosts
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts

# 1-4. 
RUN git clone git@github.com:<your/private-repo>

## Image2: main image
FROM edvice/torch:v1.12.0-py3.8-cuda11.4-cudnn8-runtime

# 2-1. copy the repository form the previous image name <intermediate>
COPY --from=intermediate </path/of/repo> </dest/of/repo>

# 2~
Run <do your stuff> ...

Multi-stage build를 잘 활용하면 위와 같이 민감한 정보를 숨기거나 Docker image의 크기를 획기적으로 줄이는 등의 효과를 볼 수 있습니다.

Multi-stage build에 대한 자세한 정보는 여기 을 참조해주세요.


What is Docker Buildkit?

Docker buildkit을 사용하면 Multi-stage build보다 더 간단하고 쉽게 ssh key를 사용할 수 있습니다.

Docker buildkit은 docker 18.09 버전 이상에서 사용할 수 있으며 성능, 저장소 관리, 보안 면에서 기존의 builder보다 더욱 우수하고 여러가지 편리하고 강력한 기능들을 추가로 사용할 수 있습니다.

또한 Buildkit으로 만들어진 docker image는 기존의 builder 로 만들어진 image와 완전히 동일하게 사용이 가능하며, docker hub등의 repository에서도 완벽히 호환됩니다.

Docker buildkit을 이용하기 위해서는 docker build 명령어 앞에 DOCKER_BUILDKIT env를 설정해주면 됩니다.

# ex
$ DOCKER_BUILDKIT=1 docker build .

혹은 /etc/docker/daemon.json 파일을 수정하여 기본값으로 설정할 수 있습니다.

# /etc/docker/daemon.json
{ "features": { "buildkit": true } }
# restart daemon
$ sudo systemctl restart docker

Using ssh with Buildkit

Docker 18.09부터 지원하는 Buildkit의 SSH forwarding 기능을 사용하면 기존의 SSH 인증 정보를 손쉽게 도커 빌드중에도 사용할 수 있습니다.

우선 빌드전에 사용할 SSH key를 SSH-agent에 등록해주는 과정이 필요합니다.

  • Run ssh-agent
$ eval $(ssh-agent)
> Agent pid 28899
# Check 
$ echo $SSH_AUTH_SOCK
> /tmp/ssh-gE3XpbSmJuAc/agent.28898
  • Add ssh key to ssh-agent
$ ssh-add /home/junpyo/.ssh/pyojuncode_ssh/pyojuncode_ssh

이후 SSH forwarding을 사용하기 위해 dockerfile을 구성합니다.

아래 예시의 3번처럼 기존의 명령어 앞에 --mount=type=ssh 를 붙여주시면 됩니다.

# syntax=docker/dockerfile:1
FROM ubuntu:18.04

# 1. Install ssh client and git
RUN apk add --no-cache openssh-client git

# 2. Download public key for github.com
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts

# 3. Clone private repository
RUN --mount=type=ssh git clone git@github.com:<your/private-repo>

마지막으로 Docker builldkit을 이용하여 Image를 build합니다.

forwarding을 위해 build할 때 --ssh default argument를 추가해야합니다.

$ DOCKER_BUILDKIT=1 docker build --ssh default -t <Image tag> .

해당 이미지에서 Forwarding 된 SSH key를 사용하여 private repository에 성공적으로 접근하고, build 완료 이후에는 SSH key에 대한 그 어떠한 정보도 남아있지 않은것을 확인할 수 있습니다.

추가적으로 id 를 이용하여 여러개의 SSH key를 사용할 수 있습니다.

#In docker file
RUN --mount=type=ssh, id=firstkey git clone ...

RUN --mount=type=ssh, id=secondkey git clone ...

When you run docker build

$ DOCKER_BUILDKIT=1 docker build --ssh firstkey=/path/to/ssh-key --ssh secondkey=/path/to/ssh-key ...

Using secrets with Buildkit

Docker buildkit에 추가된 secret은 dockerfile 내에서 해당 secret을 mount 한 single-command 에서만 노출되고, 다른 레이어들에서는 전혀 노출되지 않는 특성을 가지고 있습니다.

secret을 mount하면 기본적으로 /run/secrets 폴더에 위치하며 build 시 target key를 통해 그 위치를 바꿀 수 있습니다.

이 방식을 사용해 기존에 사용하던 SSH-key를 secret으로 마운트하고 사용하는 방법이 있습니다.

# Example
FROM ubuntu:18.04

...

RUN --mount=type=secret, id=githubkey ssh-add /path/to/mounted/key && git clone ...

...

When you build dockerfile

$ DOCKER_BUILDKIT=1 docker build --secret id=githubkey, src=/path/of/local/key ...  .   

Refs

[https://docs.docker.com/develop/develop-images/build_enhancements/](