Gerenciando Segredos Durante Docker Build
Qual seria a melhor maneira de gerenciar meus segredos durante uma compilação do docker?
Verificando projetos oficiais e não oficiais disponíveis em [hub.docker.com] (https://hub.docker.com), Reuni os 4 (quatro) casos mais comuns de como os usuários armazenam e gerenciam seus segredos.
Há casos em que durante a construção você usaria um token ou arquivo secreto para buscar informações de um repositório ou outro aplicativo para definir uma configuração que não será possível durante o tempo de execução.
Alguns desses casos também não se encaixam na compilação multistage como obter um pacote do pip.
Cenários
Preciso instalar um python
usando um pip privado que criei para este laboratório.
Para conseguir isso, você só precisa adicionar o arquivo pip.conf
conforme abaixo em / root / .pip / pip.conf
.
[global]
index-url = https: //hugo.prudente: My$3cr3tP4$$@private.pip/playlist
tempo limite = 60
extra-index-url = https://pypi.python.org/simple
Parece simples, vamos ver como o administramos.
Método 1
Aqui, copiamos o pip.conf
para o contêiner e não o removemos no final.
FROM python:latest
COPY pip.conf /root/.pip/pip.conf
RUN pip install playlist
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker build -t secret:v1 .
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker history secret:v1
IMAGE CREATED CREATED BY SIZE COMMENT
0d6589d4b95f About a minute ago RUN /bin/sh -c pip install playlist # 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
Vamos verificar se o arquivo no final da compilação está presente e não.
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/playlist
timeout=60
extra-index-url = https://pypi.python.org/simple
Método 2
Aqui, copiamos o pip.conf
para o contêiner e o removemos com uma instrução RUN
no final.
FROM python:latest
COPY pip.conf /root/.pip/pip.conf
RUN pip install playlist
RUN rm /root/.pip/pip.conf
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker build -t secret:v2 .
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker history secret:v2
IMAGE CREATED CREATED BY SIZE COMMENT
42f04cdc6577 6 seconds ago RUN /bin/sh -c rm /root/.pip/pip.conf # buil… 0B buildkit.dockerfile.v0
<missing> 4 minutes ago RUN /bin/sh -c pip install playlist # 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
Vamos verificar novamente se o arquivo estava presente.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker run -it secret:v2 cat /root/.pip/pip.conf
cat: /root/.pip/pip.conf: No such file or directory
Método 3
Aqui, copiamos o pip.conf
para o contêiner e o removemos na mesma instrução RUN
do pip install
FROM python:latest
COPY pip.conf /root/.pip/pip.conf
RUN pip install playlist && rm /root/.pip/pip.conf
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker build -t secret:v3 .
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker history secret:v3
IMAGE CREATED CREATED BY SIZE COMMENT
a2bf2672abaf About a minute ago RUN /bin/sh -c pip install playlist && 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
Vamos verificar mais uma vez se o arquivo estava presente.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker run -it secret:v3 cat /root/.pip/pip.conf
cat: /root/.pip/pip.conf: No such file or directory
Método 4
Aqui criamos o pip.conf
usando o script generate.sh
que recebe o
SECRET
como ARG
com as opções --build-arg
e o removemos na mesma instrução RUN
.
#!/bin/sh
SECRET=$1
mkdir -p /root/.pip
cat > /root/.pip/pip.conf << EOF
[global]
index-url = https://hugo.prudente:${SECRET}@private.pip/playlist
timeout=60
extra-index-url = https://pypi.python.org/simple
EOF
FROM python:latest
ARG SECRET
COPY generate.sh /generate.sh
RUN /generate.sh ${SECRET} && pip install playlist && rm /root/.pip/pip.conf
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-arg SECRET=My$3cr3tP4$$ .
➜ on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker history -H secret:v4
IMAGE CREATED CREATED BY SIZE COMMENT
d2ca3623139f 2 minutes ago RUN |1 SECRET=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
Por último, mas não menos importante, vamos verificar a presença do arquivo.
no ⛵ k3s (nerdweek) ~ / postar via 🐍 v3.9.1 (osx)
❯ docker run -it secret: v4 cat /root/.pip/pip.conf
cat: /root/.pip/pip.conf: Esse arquivo ou diretório não existe
A credencial não está no arquivo pip.conf
, mas é visível durante o histórico do docker
.
Resultados preliminares
Aqui está uma matriz de onde nossos segredos foram revelados.
Método | Tempo de execução | Inspeção |
---|---|---|
secret:v1 | Yes | No |
secret:v2 | No | No |
secret:v3 | No | No |
secret:v4 | No | Yes |
Com os resultados preliminares, já podemos excluir os métodos 1 e 4, pois podemos considerá-los inseguros devido às credenciais serem visíveis em algum ponto.
O método 4, eu também usei --build-args SECRET=${SECRET}
e os segredos foram revelados da mesma maneira.
Mergulho profundo
OverlayFS
É a implementação do kernel para um sistema de arquivos de união, um sistema de arquivos de sobreposição tenta para apresentar um sistema de arquivos que é o resultado sobrepondo um sistema de arquivos em em cima do outro.
Resumindo, pegando o exemplo da imagem acima, imagine que você tem 2 diretórios o inferior e superior, onde o ** inferior ** é um diretório somente leitura para o consumidor, mas eles ainda são de leitura e gravação do sistema operacional Linux.
Quando um arquivo é modificado no superior, a mudança acontece normalmente, mas se um a mudança é feita em um arquivo do diretório inferior, uma cópia dele é criada no superior para se tornar acessível, uma vez que a modificação seja concluída em outro processo é responsável por buscar a modificação e escrever no ** inferior ** diretório.
Portanto, esta união de diretórios fundidos em um único bloco limitado pelo cgroups é o que o docker e seu driver de armazenamento usa em nosso exemplo.
O AUFS que também é um sistema de arquivos de união também apresenta o mesmo comportamento, embora alguns dos locais possam ser ligeiramente diferentes.
Verificando o sistema de arquivos
Agora que sei como o OverlayFS funciona, vamos isolar os diretórios (camadas) usados
pelo python: latest
para que possamos filtrar apenas aqueles que estamos interessados
on, aqueles que possuem o arquivo pip.conf
.
Inspecione os contêineres, posso encontrar os diretórios usados no OverlayFS.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker inspect python:latest | grep Dir | grep -Eo "([a-z0-9]{25,64})"
9e7c768dda91c4fa7ed6a57c7cb784834033bff92bd11ff6d062d4de11c0f898
17bf53c98685ae36487eb55f0d2256d168f210a688ef51deef760de1a699cbdf
4cb1dbbf58a2b1ca8df6d9d977a66fe918aee21434fcd656f1a68f1f412d75ff
358dd0944f115e2a273c5259dd1432b44e36908cf223f8ce0d9f74550430f577
c034592b1a26552525742ed81e7fbce2139817b634d48db8349dbebf15a45914
19d471e0407c0f1ca14eb1cb8c46aaef9357037cad5dc170cb6a4af3c1feab40
e005796f193e62e9db78de1df20999daca1a96a0bebed19c1dd906b1b4da8542
badc6aa65b2d3f10b0cdff3fc04bf3a64b551af1dd9e01b6ecd38ed71abdc3da
8d6ff96b718838005288a94cdc9fd408d1f70d7e9cbab678ebeb4521d11b366d
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker inspect python:latest | grep Dir | grep -Eo "([a-z0-9]{25,64})" > layers.python
Com as camadas salvas no arquivo layers.python
, posso usar um comando semelhante para
exclua as camadas conhecidas do python e obtenha apenas aquelas adicionadas por nossa construção.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker inspect secret:v1 | grep Dir | grep -Eo "([a-z0-9]{25,64})" | grep -v -f layers.python | uniq
e1kq2j71b7clcwtn0lbmqa1g9
v6zy2xgzrow2mgpyq9d0vch6l
➜ docker inspect secret:v2 | grep Dir | grep -Eo "([a-z0-9]{25,64})" | grep -v -f layers.python | uniq
v6zy2xgzrow2mgpyq9d0vch6l
e1kq2j71b7clcwtn0lbmqa1g9
oudofb9c0iaqog9sff81f8053
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker inspect secret:v3 | grep Dir | grep -Eo "([a-z0-9]{25,64})" | grep -v -f layers.python | uniq
e1kq2j71b7clcwtn0lbmqa1g9
hqsrze873a2uz7tjsgbqdo3sd
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker inspect secret:v4 | grep Dir | grep -Eo "([a-z0-9]{25,64})" | grep -v -f layers.python | uniq
78gz619okusgrq4jo4ek863ug
oq22x88p26hzqmr1w1qpf8mm4
Agora que temos as camadas, criei uma lista apenas para torná-la mais simples encontrá-lo quando verificarmos o diretório.
Acessando os diretórios OverlayFS
Usando um dos comandos abaixo como root, você encontrará o ponto de entrada onde O driver Docker Storage cria a hierarquia do sistema de arquivos usada por todo eco-sistema.
Linux
cd /var/lib/docker/
MacOS
docker run -it --rm --privileged --pid=host justincormack/nsenter1
cd /var/lib/docker/
Uma vez no diretório /var/lib/docker
usando um simples ls
e filtrando o
camadas anteriores armazenadas no arquivo temporário e expandindo-o eu poderia encontrar o
camadas específicas que têm o pip.conf
/var/lib/docker/overlay2
# ls | grep -f /tmp/layers | xargs find | grep pip.conf
e1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.conf
hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf
oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf
Então, acessei cada um deles para confirmar se o arquivo estava presente ou apenas sua sombra deixada pela união do diretório.
/var/lib/docker/overlay2
# cat e1kq2j71b7clcwtn0lbmqa1g9/diff/root/.pip/pip.conf
[global]
index-url = https://hugo.prudente:My$3cr3tP4$$@private.pip/playlist
timeout=60
extra-index-url = https://pypi.python.org/simpl
/var/lib/docker/overlay2
# cat hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf
cat: can\'t open 'hqsrze873a2uz7tjsgbqdo3sd/diff/root/.pip/pip.conf': No such device or address
/var/lib/docker/overlay2
# cat oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf
cat: can\'t open 'oudofb9c0iaqog9sff81f8053/diff/root/.pip/pip.conf': No such device or address
Portanto, 1 de 3 camadas tem o arquivo presente, então verifiquei de qual contêiner essa camada pertence e aqui está a surpresa.
Essa 1 camada é compartilhada com 3 de 4 construções que criamos, o que significa que
durante um docker pull
, três recipientes diferentes podem vazar meu pip.conf
segredo.
Resultados
A matriz atualizada consolidando os resultados sobre onde nossos segredos vazaram.
Método | Tempo de execução | Inspeção | OverlayFS |
---|---|---|---|
secret:v1 | Yes | No | Yes |
secret:v2 | No | No | Yes |
secret:v3 | No | No | Yes |
secret:v4 | NO | Yes | No |
Portanto, mesmo sabendo que o arquivo não pode ser acessado diretamente do contêiner se você tem acesso para puxar o contêiner em um sistema completo de leitura e gravação, você seria capaz de recuperar os segredos.
Mas agora qual é a melhor maneira de construir o container e não ter esse problema?
Solução
A partir de 18.09 ou mais recente, o Docker introduziu o Docker BuildKit que traz alguns funcionalidade extra para as compilações do Docker.
As compilações usando BuildKit diferentes do legado permitem o uso do
--secret
que permite a capacidade de vincular um arquivo durante o tempo de execução de compilação
semelhante ao tempo de execução tradicional que alcançamos com a opção -v
.
Seu uso é bastante simples, vamos construir um contêiner e executar nossos testes novamente.
FROM python:latest
RUN --mount=type=secret,id=pip.conf,dst=/root/.pip/pip.conf \
pip install playlist
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker build --file Dockerfile --secret id=pip.conf,src=pip.conf -t secret:v5 .
Agora que temos a compilação secret:v5
, vamos confirmar.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
➜ docker history secret:v5
IMAGE CREATED CREATED BY SIZE COMMENT
266a21bb36ae 36 seconds ago RUN /bin/sh -c pip install playlist # bui… 14.1MB buildkit.dockerfile.v0
<missing> 11 days ago /bin/sh -c #(nop) CMD ["python3"] 0Bi
O histórico neste caso é limpo, sem nem mencionar a montagem para pip.conf
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
❯ docker run -it secret:v5 cat /root/.pip/pip.conf
cat: /root/.pip/pip.conf: No such file or directory
O arquivo também não está presente no sistema.
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
❯ docker inspect secret:v5 | grep Dir | grep -Eo "([a-z0-9]{25,64})" | grep -v -f layers.python | uniq
cnpw0dw9o05lmdz3j9j62jzpt
on ⛵ k3s (nerdweek) ~/post via 🐍 v3.9.1 (osx)
/var/lib/docker/overlay2 #
ls cnpw0dw9o05lmdz3j9j62jzpt | xargs find | grep pip.conf
find: committed: No such file or directory
find: diff: No such file or directory
find: link: No such file or directory
find: lower: No such file or directory
find: work: No such file or directory
E o mais importante, o arquivo não existe na camada / diretório que acabamos de criado, o que significa que se usarmos em uma imagem de base, nossos scretes estão seguros.
Referências
Containers Security Docker Credentials Secrets Vault
Cloud Computing & Virtualization Essentials
1875 Palavras
2021-03-04T31:46+0000