Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Hugo Prudente
Hugo Prudente

Posted on

     

Managing Secrets During Docker Build

Original post from:https://hugoprudente.github.io

What would be the best way to manage my secrets during a docker build?

Checking official and unofficial projects available inhub.docker.com,
I have collected the 4 (four) most common cases on how users are storing and managing their secrets.

There are cases that during the build you would use a token or secret file for fetch information from a
repo or other application to setup a configuration that will not be possible during runtime.

Some of those cases also doesn't fit the multistage building as fetching a package from pip.

Scenarios

I need to install apython using a private pip that I created for this lab.

To achieve that you only need to addpip.conf file as below to/root/.pip/pip.conf.

[global]index-url=https://hugo.prudente:My$3cr3tP4$$@private.pip/playlisttimeout=60extra-index-url=https://pypi.python.org/simple
Enter fullscreen modeExit fullscreen mode

Looks simple, let's see how we manage it.

Method 1

Here we copy thepip.conf to the container and don't remove it on the end.

FROM python:latestCOPY pip.conf /root/.pip/pip.confRUNpipinstallplaylist
Enter fullscreen modeExit fullscreen mode
on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker build-t secret:v1.on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ dockerhistorysecret:v1IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT0d6589d4b95f   About a minute ago   RUN /bin/sh-c pipinstallplaylist# bui…   14.1MB    buildkit.dockerfile.v0<missing>      4 minutes ago        COPY pip.conf /root/.pip/pip.conf# buildkit    200B      buildkit.dockerfile.v0<missing>      10 days ago          /bin/sh-c#(nop)  CMD ["python3"]              0B
Enter fullscreen modeExit fullscreen mode

Let's check if the file on the end of the build is present and it was leaked.

on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)❯ docker run -it secret:v1 cat /root/.pip/pip.conf[global]index-url = https://hugo.prudente:My$3cr3tP4$$@private.pip/playlisttimeout=60extra-index-url = https://pypi.python.org/simple
Enter fullscreen modeExit fullscreen mode

Method 2

Here we copy thepip.conf to the container and remove it with aRUN statement on the end.

FROM python:latestCOPY pip.conf /root/.pip/pip.confRUNpipinstallplaylistRUNrm /root/.pip/pip.conf
Enter fullscreen modeExit fullscreen mode
on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker build-t secret:v2.on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ dockerhistorysecret:v2IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT42f04cdc6577   6 seconds ago   RUN /bin/sh-crm /root/.pip/pip.conf# buil…   0B        buildkit.dockerfile.v0<missing>      4 minutes ago   RUN /bin/sh-c pipinstallplaylist# bui…   14.1MB    buildkit.dockerfile.v0<missing>      7 minutes ago   COPY pip.conf /root/.pip/pip.conf# buildkit    200B      buildkit.dockerfile.v0<missing>      10 days ago     /bin/sh-c#(nop)  CMD ["python3"]              0B
Enter fullscreen modeExit fullscreen mode

Let's check again if the file was present.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker run-it secret:v2cat /root/.pip/pip.confcat: /root/.pip/pip.conf: No such file or directory
Enter fullscreen modeExit fullscreen mode

Method 3

Here we copy thepip.conf to the container and remove it in the sameRUN statement as thepip install

FROM python:latestCOPY pip.conf /root/.pip/pip.confRUNpipinstallplaylist&&rm /root/.pip/pip.conf
Enter fullscreen modeExit fullscreen mode
on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker build-t secret:v3.on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ dockerhistorysecret:v3IMAGE          CREATED              CREATED BY                                      SIZE      COMMENTa2bf2672abaf   About a minute ago   RUN /bin/sh-c pipinstallplaylist&&rm…   14.1MB    buildkit.dockerfile.v0<missing>      17 minutes ago       COPY pip.conf /root/.pip/pip.conf# buildkit    200B      buildkit.dockerfile.v0<missing>      10 days ago          /bin/sh-c#(nop)  CMD ["python3"]              0B
Enter fullscreen modeExit fullscreen mode

Let's check once more if the file was present.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker run-it secret:v3cat /root/.pip/pip.confcat: /root/.pip/pip.conf: No such file or directory
Enter fullscreen modeExit fullscreen mode

Method 4

Here we create thepip.conf using thegenerate.sh script that receive the
SECRET asARG with the--build-arg options and we remove it on the sameRUN statement.

#!/bin/shSECRET=$1mkdir-p /root/.pipcat> /root/.pip/pip.conf<<EOF[global]index-url = https://hugo.prudente:${SECRET}@private.pip/playlisttimeout=60extra-index-url = https://pypi.python.org/simpleEOF
Enter fullscreen modeExit fullscreen mode
FROM python:latestARG SECRETCOPY generate.sh /generate.shRUN/generate.sh${SECRET}&& pipinstallplaylist&&rm /root/.pip/pip.conf
Enter fullscreen modeExit fullscreen mode
on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker build-t secret:v4--progress plain--build-argSECRET=My$3cr3tP4$$.➜ on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ dockerhistory-H secret:v4IMAGE          CREATED         CREATED BY                                      SIZE      COMMENTd2ca3623139f   2 minutes ago   RUN |1SECRET=My$3cr3tP4$$ /b…   14.1MB    buildkit.dockerfile.v0<missing>      2 minutes ago   COPY generate.sh /generate.sh# buildkit        261B      buildkit.dockerfile.v0<missing>      2 minutes ago   ARG SECRET                                      0B        buildkit.dockerfile.v0<missing>      10 days ago     /bin/sh-c#(nop)  CMD ["python3"]              0B
Enter fullscreen modeExit fullscreen mode

Last but not least, let's check the presece of the file.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)❯ docker run-it secret:v4cat /root/.pip/pip.confcat: /root/.pip/pip.conf: No such file or directory
Enter fullscreen modeExit fullscreen mode

The credential is not on thepip.conf file but it's visible during thedocker history.

Preliminary Results

Here is a matrix on where our secrets have been leaked.

MethodRuntimeInspection
secret:v1YesNo
secret:v2NoNo
secret:v3NoNo
secret:v4NoYes

With the preliminary results we can already exclude methods 1 and 4 as we can
consider them insecure due to the credentials being visible at some point.

The method 4, I also used--build-args SECRET=${SECRET} and the secrets
leaks on the same way.

Dive Deep

OverlayFS

Is the kernel implementation for a union-filesystem, an overlay-filesystem tries
to present a filesystem which is the result over overlaying one filesystem on
top of the other.

overlayfs.png

In short taking the example of the image above imagine you have 2 directories
thelower andupper where thelower is a read-only directory for the
consumer, but they are still read-write from the Linux Operational system.

When a file is modified on theupper the change happens normally but if a
change is made on a file of thelower directory a copy of it is created on the
upper to become accessible, once the modification is complete another
process is responsible to fetch the modification and write on thelower
directory.

So this union of directories merged together as one unique block limited by the
cgroups is what docker and it's storage driver uses on our example.

The AUFS that's also a union-filesystem also presents the same behaviour,
although some of locations may be slightly different.

Checking the Filesystem

Now that I know how OverlayFS works let's isolate the directories (layers) used
by thepython:latest so we can filter out only the ones that we are interested
on, the ones that havepip.conf file.

Inspect the containers I can find the directories used on the OverlayFS.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker inspect python:latest |grepDir |grep-Eo"([a-z0-9]{25,64})"9e7c768dda91c4fa7ed6a57c7cb784834033bff92bd11ff6d062d4de11c0f89817bf53c98685ae36487eb55f0d2256d168f210a688ef51deef760de1a699cbdf4cb1dbbf58a2b1ca8df6d9d977a66fe918aee21434fcd656f1a68f1f412d75ff358dd0944f115e2a273c5259dd1432b44e36908cf223f8ce0d9f74550430f577c034592b1a26552525742ed81e7fbce2139817b634d48db8349dbebf15a4591419d471e0407c0f1ca14eb1cb8c46aaef9357037cad5dc170cb6a4af3c1feab40e005796f193e62e9db78de1df20999daca1a96a0bebed19c1dd906b1b4da8542badc6aa65b2d3f10b0cdff3fc04bf3a64b551af1dd9e01b6ecd38ed71abdc3da8d6ff96b718838005288a94cdc9fd408d1f70d7e9cbab678ebeb4521d11b366don ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker inspect python:latest |grepDir |grep-Eo"([a-z0-9]{25,64})"> layers.python
Enter fullscreen modeExit fullscreen mode

With the layers saved in the filelayers.python I can use a similar command to
exclude the know python layers and get only the ones added by our build.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker inspect secret:v1 |grepDir |grep-Eo"([a-z0-9]{25,64})" |grep-v-f layers.python  |uniqe1kq2j71b7clcwtn0lbmqa1g9v6zy2xgzrow2mgpyq9d0vch6l➜ docker inspect secret:v2 |grepDir |grep-Eo"([a-z0-9]{25,64})" |grep-v-f layers.python  |uniqv6zy2xgzrow2mgpyq9d0vch6le1kq2j71b7clcwtn0lbmqa1g9oudofb9c0iaqog9sff81f8053on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker inspect secret:v3 |grepDir |grep-Eo"([a-z0-9]{25,64})" |grep-v-f layers.python  |uniqe1kq2j71b7clcwtn0lbmqa1g9hqsrze873a2uz7tjsgbqdo3sdon ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker inspect secret:v4 |grepDir |grep-Eo"([a-z0-9]{25,64})" |grep-v-f layers.python  |uniq78gz619okusgrq4jo4ek863ugoq22x88p26hzqmr1w1qpf8mm4
Enter fullscreen modeExit fullscreen mode

Now that we have the layers I have created a list just to make it simpler to
find it when we check the directory.

Accessing the OverlayFS directories

Using one of the commands below as root you will find the entry point where
Docker Storage driver creates the file-system hierarchy used by the whole
eco-system.

Linux

cd /var/lib/docker/
Enter fullscreen modeExit fullscreen mode

MacOS

docker run-it--rm--privileged--pid=host justincormack/nsenter1cd /var/lib/docker/
Enter fullscreen modeExit fullscreen mode

Once in the/var/lib/docker directory using a simplels and filtering the
layers previous stored in the temporary file and expanding it I could find the
specific layers that have thepip.conf

/var/lib/docker/overlay2# ls | grep -f /tmp/layers | xargs find | grep pip.confe1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.confhqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.confoudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf
Enter fullscreen modeExit fullscreen mode

So I accessed each of of those to confirm if the file was present or if was just
its shadow left by the directory union.

/var/lib/docker/overlay2# cat e1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.conf[global]index-url= https://hugo.prudente:My$3cr3tP4$$@private.pip/playlisttimeout=60extra-index-url= https://pypi.python.org/simpl/var/lib/docker/overlay2# cat hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.confcat: can\'t open'hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf': No such device or address/var/lib/docker/overlay2# cat oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.confcat: can\'t open'oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf': No such device or address
Enter fullscreen modeExit fullscreen mode

So 1 of 3 layers have the file present so I have checked from which container
that layer belong to and here's the surprise.

That 1 layer is shared with 3 of 4 builds that we have created, meaning that
during adocker pull three diferente containers could leak mypip.conf
secret.

Results

The updated matrix consolidating the results on where our secrets have leaked.

MethodRuntimeInspectionOverlayFS
secret:v1YesNoYes
secret:v2NoNoYes
secret:v3NoNoYes
secret:v4NOYesNo

So even knowing that the file is not acessible from the container directly if
you have access to pull the container on a full read-write system you would be
able to retreive the secrets.

But now what's the best way to build the container and do not have such issue?

Solution

From 18.09 or newer Docker have introduced the Docker BuildKit that brings some
extra funcionality to the Docker builds.

The builds using BuildKit different from the legacy allows the usage of the
--secret that allows the capacity of binding a file during build runtime
similar to the tradicional runtime that we achieve with-v option.

It's usage is quite simple let's build a container and run our tests again.

FROM python:latestRUN--mount=type=secret,id=pip.conf,dst=/root/.pip/pip.conf\      pipinstallplaylist
Enter fullscreen modeExit fullscreen mode
on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ docker build--file Dockerfile--secretid=pip.conf,src=pip.conf-t secret:v5.
Enter fullscreen modeExit fullscreen mode

Now that we have thesecret:v5 build lets confirm.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)➜ dockerhistorysecret:v5IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT266a21bb36ae   36 seconds ago   RUN /bin/sh-c pipinstallplaylist# bui…   14.1MB    buildkit.dockerfile.v0<missing>      11 days ago      /bin/sh-c#(nop)  CMD ["python3"]              0Bi
Enter fullscreen modeExit fullscreen mode

The history in this case is clean, not even mention the mount forpip.conf

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)❯ docker run-it secret:v5cat /root/.pip/pip.confcat: /root/.pip/pip.conf: No such file or directory
Enter fullscreen modeExit fullscreen mode

The file is also not present on the system.

on ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)❯ docker inspect secret:v5 |grepDir |grep-Eo"([a-z0-9]{25,64})" |grep-v-f layers.python  |uniqcnpw0dw9o05lmdz3j9j62jzpton ⛵ k3s(nerdweek) ~/post via 🐍 v3.9.1(osx)/var/lib/docker/overlay2#lscnpw0dw9o05lmdz3j9j62jzpt | xargs find |greppip.conffind: committed: No such file or directoryfind: diff: No such file or directoryfind:link: No such file or directoryfind: lower: No such file or directoryfind: work: No such file or directory
Enter fullscreen modeExit fullscreen mode

And the most important one the file doesn't exist on the layer/directory that we just
created meaning that if we use in a base image our scretes are safe.

References

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Joined

Trending onDEV CommunityHot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp