Docker e proteção de senhas

162

Recentemente, experimentei o Docker na criação de alguns serviços para brincar e uma coisa que me incomoda é colocar senhas em um arquivo Docker. Como sou desenvolvedor, o armazenamento de senhas na fonte parece um soco na cara. Isso deveria ser uma preocupação? Existem boas convenções sobre como lidar com senhas no Dockerfiles?

anthonator
fonte
7
Há um problema em aberto no Github solicitando práticas recomendadas em relação ao Docker e segredos, o problema está aqui: github.com/docker/docker/issues/13490
Luís Bianchin

Respostas:

92

Definitivamente, é uma preocupação. Dockerfiles geralmente são registrados nos repositórios e compartilhados com outras pessoas. Uma alternativa é fornecer credenciais (nomes de usuário, senhas, tokens, qualquer coisa sensível) como variáveis ​​de ambiente em tempo de execução . Isso é possível através do -eargumento (para vars individuais na CLI) ou do --env-fileargumento (para várias variáveis ​​em um arquivo) para docker run. Leia isto para usar ambiental com docker-compose.

--env-fileDefinitivamente, usar é uma opção mais segura, pois ela protege contra os segredos que aparecem psnos logs ou nos logs, se usados set -x.

No entanto, os env vars também não são particularmente seguros. Eles são visíveis via docker inspecte, portanto, estão disponíveis para qualquer usuário que possa executar dockercomandos. (Obviamente, qualquer usuário que tenha acesso ao dockerhost também terá raiz, de qualquer maneira.)

Meu padrão preferido é usar um script de wrapper como o ENTRYPOINTou CMD. O script do wrapper pode primeiro importar segredos de um local externo para o contêiner no tempo de execução, depois executar o aplicativo, fornecendo os segredos. A mecânica exata disso varia com base no seu ambiente de tempo de execução. Na AWS, você pode usar uma combinação de funções do IAM, o Key Management Service e o S3 para armazenar segredos criptografados em um bucket do S3. Algo como o HashiCorp Vault ou credstash é outra opção.

AFAIK não existe um padrão ideal para o uso de dados confidenciais como parte do processo de construção. Na verdade, eu tenho uma pergunta SO sobre esse tópico. Você pode usar o docker-squash para remover as camadas de uma imagem. Mas não há funcionalidade nativa no Docker para esse fim.

Você pode encontrar comentários de shykes sobre configuração em contêineres úteis.

Ben Whaley
fonte
Conforme observado em outros comentários, haverá 2 camadas (após ADD e após a primeira EXECUÇÃO) que contêm o .configarquivo.
Petr Gladkikh
1
As variáveis ​​env parecem ser o melhor caminho a percorrer. Eu estive analisando isso no contexto do desenvolvimento do TDDing Dockerfile.
precisa saber é o seguinte
5
Estou preocupado que, se sua senha for uma variável env, ela aparecerá em docker inspect.
magro
Uma instalação padrão do docker (no linux) requer privilégios de sudoer para executar docker inspect. Se o invasor já puder executar o sudo, a retirada de sua senha da inspeção do docker provavelmente está muito baixa na sua lista de coisas que agora podem dar errado. Esse detalhe em particular parece um risco aceitável para mim.
GrandOpener
7
@GrandOpener Isso se aplica apenas à situação em que há um invasor usando seu sistema. Se eu enviar uma imagem do docker para um repositório, e ela for puxada por outra pessoa, não me importo se eles têm sudo em seu próprio sistema, mas definitivamente me importo se eles virem segredos no ambiente que não deveriam mais estar lá.
vee_ess
74

Nossa equipe evita colocar credenciais em repositórios, o que significa que eles não podem entrar Dockerfile. Nossa prática recomendada nos aplicativos é usar creds de variáveis ​​de ambiente.

Nós resolvemos isso usando docker-compose.

Dentro docker-compose.yml, você pode especificar um arquivo que contenha as variáveis ​​de ambiente para o contêiner:

 env_file:
- .env

Certifique-se de adicionar .enva e .gitignore, em seguida, defina as credenciais no .envarquivo como:

SOME_USERNAME=myUser
SOME_PWD_VAR=myPwd

Armazene o .envarquivo localmente ou em um local seguro, onde o resto da equipe possa pegá-lo.

Consulte: https://docs.docker.com/compose/environment-variables/#/the-env-file

theUtherSide
fonte
15
Você também pode fazer isso sem um arquivo .env, se desejar. Basta usar a propriedade environment em seu arquivo docker-compose.yml. "Variáveis ​​de ambiente com apenas uma chave são resolvidas com seus valores na máquina Compose em execução, o que pode ser útil para valores secretos ou específicos de host".
D. Visser
1
dê a este homem um biscoito! :) Sim, essa é realmente uma boa prática. Quero apenas acrescentar que docs.docker.com/compose/env-file deve funcionar automaticamente, mas no docker compor a versão 2, parece que você precisa declará-lo conforme descrito nesta resposta.
equivalent8
5
O uso de variáveis ​​de ambiente é desencorajado pela própria equipe do Docker, pois env var pode ser visto por meio de / proc / <pid> / environment e inspecionador do docker. Isso apenas ofusca a maneira de obter as credenciais para um invasor que obteve acesso root. Obviamente, as credenciais nunca devem ser rastreadas pelo CVS. Acho que a única maneira de impedir que um usuário root obtenha cred é ler as credenciais no aplicativo da web (esperando que ele não atualize seu arquivo proc environment) de um arquivo criptografado, o processo de descriptografia com segurança solicitando uma senha. Eu acho que vou tentar com uma tumba: github.com/dyne/Tomb
pawamoy
.gitignorepara que o .envarquivo com informações confidenciais não seja verificado no GitHub. Eu tenho certeza que isso não vai funcionar se você adicioná-lo.dockerignore
theUtherSide 22/02/19
oi @theUtherSide, obrigado pela sua resposta, eu tinha uma pergunta: quando não .envfaço o check-in do arquivo e faço uma implantação em um servidor no local, você sugere criar o .envarquivo novamente no servidor manualmente?
opensource-developer
37

O Docker agora (versão 1.13 ou 17.06 e superior) tem suporte para gerenciar informações secretas. Aqui está uma visão geral e uma documentação mais detalhada

Recurso semelhante existe nos kubernetes e no DCOS

Heather QC
fonte
Alguns comandos úteis dos links acima docker secret create:: criar um segredo docker secret inspect: exibir informações detalhadas sobre um segredo docker secret ls: exibir todos os segredos docker secret rm: remover um --secretsinalizador de segredo específico para docker service create: criar um segredo durante a criação do serviço --secret-adde --secret-rmsinalizadores para docker service update: atualizar o valor de um segredo ou remover um segredo durante a tarefa de atualização de serviço. Os segredos do Docker são protegidos em repouso nos nós do gerenciador e provisionados aos nós do trabalhador durante a inicialização do contêiner.
PJ
7
Sim, você precisa configurar um enxame para usar os segredos do Docker
Heather QC
11
Este é um bom começo para uma resposta, mas precisa de muito mais informações daquilo que está vinculado para aparecer na própria resposta.
Jeff Lambert
7
Não tenho certeza se essa pode ser a resposta aceita se funcionar apenas com enxames. Muitas pessoas não estão usando enxames, mas ainda precisam passar segredos.
John John
9

Você nunca deve adicionar credenciais a um contêiner, a menos que esteja bem transmitindo os cleds para quem puder fazer o download da imagem. Em particular, fazer e ADD credsmais tarde RUN rm credsnão é seguro porque o arquivo creds permanece na imagem final em uma camada intermediária do sistema de arquivos. É fácil para qualquer pessoa com acesso à imagem extraí-la.

A solução típica que eu vi quando você precisa de creds para fazer check-out de dependências é usar um contêiner para criar outro. Ou seja, normalmente você tem algum ambiente de construção em seu contêiner base e precisa invocá-lo para criar seu contêiner de aplicativo. Portanto, a solução simples é adicionar a fonte do aplicativo e RUNos comandos de compilação. Isso é inseguro se você precisar de ajuda RUN. Em vez disso, o que você faz é colocar sua origem em um diretório local, execute (como no docker run) o contêiner para executar a etapa de construção com o diretório de origem local montado como volume e os cleds injetados ou montados como outro volume. Após a conclusão da etapa de construção, você constrói seu contêiner final simplesmente ADDinserindo o diretório de origem local que agora contém os artefatos construídos.

Espero que o Docker adicione alguns recursos para simplificar tudo isso!

Atualização: parece que o método daqui para frente será ter builds aninhados. Em resumo, o dockerfile descreveria um primeiro contêiner usado para criar o ambiente de tempo de execução e, em seguida, um segundo contêiner aninhado que pode montar todas as peças no contêiner final. Dessa forma, o material em tempo de construção não está no segundo contêiner. Este é um aplicativo Java em que você precisa do JDK para criar o aplicativo, mas apenas do JRE para executá-lo. Há várias propostas em discussão, é melhor começar em https://github.com/docker/docker/issues/7115 e seguir alguns dos links para propostas alternativas.

TvE
fonte
7

Uma alternativa ao uso de variáveis ​​de ambiente, que podem ficar confusas se você tiver muitas delas, é usar volumes para tornar um diretório no host acessível no contêiner.

Se você colocar todas as suas credenciais como arquivos nessa pasta, o contêiner poderá ler os arquivos e usá-los como desejar.

Por exemplo:

$ echo "secret" > /root/configs/password.txt
$ docker run -v /root/configs:/cfg ...

In the Docker container:

# echo Password is `cat /cfg/password.txt`
Password is secret

Muitos programas podem ler suas credenciais a partir de um arquivo separado; portanto, você pode simplesmente apontar o programa para um dos arquivos.

Malvineous
fonte
5

solução somente em tempo de execução

docker-compose também fornece uma solução no modo não-enxame (desde a v1.11: Segredos usando montagens de ligação ).

Os segredos são montados como arquivos abaixo /run/secrets/pelo docker-compondo. Isso resolve o problema no tempo de execução (executando o contêiner), mas não no tempo de construção (construção da imagem), porque /run/secrets/não é montado no tempo de construção. Além disso, esse comportamento depende da execução do contêiner com docker-compose.


Exemplo:

Dockerfile

FROM alpine
RUN cat /run/secrets/password
CMD sleep inifinity

docker-compose.yml

version: '3.1'
services:
  app:
    build: .
    secrets:
      - password

secrets:
  password:
    file: password.txt

Para construir, execute:

docker-compose up -d

Leitura adicional:

Murmel
fonte
2

Com o Docker v1.9, é possível usar a instrução ARG para buscar argumentos passados ​​pela linha de comando para a imagem na ação de compilação . Simplesmente use o sinalizador --build-arg . Portanto, você pode evitar manter uma senha explícita (ou outras informações confidenciais) no arquivo Docker e passá-las rapidamente.

fonte: https://docs.docker.com/engine/reference/commandline/build/ http://docs.docker.com/engine/reference/builder/#arg

Exemplo:

Dockerfile

FROM busybox
ARG user
RUN echo "user is $user"

comando build image

docker build --build-arg user=capuccino -t test_arguments -f path/to/dockerfile .

durante a construção imprimir

$ docker build --build-arg user=capuccino -t test_arguments -f ./test_args.Dockerfile .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM busybox
 ---> c51f86c28340
Step 2 : ARG user
 ---> Running in 43a4aa0e421d
 ---> f0359070fc8f
Removing intermediate container 43a4aa0e421d
Step 3 : RUN echo "user is $user"
 ---> Running in 4360fb10d46a
**user is capuccino**
 ---> 1408147c1cb9
Removing intermediate container 4360fb10d46a
Successfully built 1408147c1cb9

Espero que ajude! Tchau.

NickGnd
fonte
26
De acordo com os documentos ARG do Docker : "Não é recomendado o uso de variáveis ​​em tempo de construção para passar segredos como chaves do github, credenciais do usuário etc."
Lie Ryan
3
Imaginando por que o Docker recomenda usar --build-arg var=secreta passagem de uma chave privada SSH para uma imagem, não há justificativa documentada. Alguém pode explicar isso?
Henk Wiersema
2
@HenkWiersema As informações do processo, os logs e o histórico de comandos são inseguros. As informações do processo estão disponíveis publicamente e incluem todos os parâmetros da linha de comandos. Freqüentemente, essas chamadas acabam em logs que também podem ser públicos. Não é incomum um invasor inspecionar informações sobre processos em execução e vasculhar arquivos de log públicos em busca de segredos. Mesmo quando não é público, ele pode ser armazenado no histórico de comandos, o que facilitaria a obtenção de segredos por meio de uma conta não administrativa.
tu restabelece Monica-dor duh
2
Qual é a maneira recomendada de fornecer credenciais necessárias no momento da criação? Por exemplo, uma imagem que precisa de acesso aws s3 para buscar um grande conjunto de dados que residirá dentro da imagem?
Ely
3
Eu imagino que o motivo não é recomendado é porque docker historyexpõe build-arg/ ARGvariáveis. Pode-se extrair qualquer imagem, inspecioná-la e ver quaisquer segredos passados ​​durante a compilação como um parâmetro build-arg/ ARG.
vee_ess
2

Minha abordagem parece funcionar, mas provavelmente é ingênua. Diga-me porque está errado.

Os ARGs configurados durante a construção do docker são expostos pelo subcomando history, portanto, não é necessário ir para lá. No entanto, ao executar um contêiner, as variáveis ​​de ambiente fornecidas no comando executar estão disponíveis para o contêiner, mas não fazem parte da imagem.

Portanto, no Dockerfile, faça a instalação que não envolve dados secretos. Defina um CMD de algo parecido /root/finish.sh. No comando run, use variáveis ​​de ambiente para enviar dados secretos para o contêiner. finish.shusa as variáveis ​​essencialmente para concluir as tarefas de construção.

Para facilitar o gerenciamento dos dados secretos, coloque-os em um arquivo carregado pela janela de encaixe executada com o --env-filecomutador. Obviamente, mantenha o arquivo em segredo. .gitignoree tal.

Para mim, finish.shexecuta um programa Python. Ele verifica se não foi executado antes e termina a instalação (por exemplo, copia o nome do banco de dados no Django settings.py).

Kieran Mathieson
fonte
2

Há um novo comando do docker para gerenciamento de "segredos". Mas isso só funciona para grupos de enxames.

docker service create
--name my-iis
--publish target=8000,port=8000
--secret src=homepage,target="\inetpub\wwwroot\index.html"
microsoft/iis:nanoserver 
José Ibañez
fonte
1

A metodologia do aplicativo de 12 fatores informa que qualquer configuração deve ser armazenada em variáveis ​​de ambiente.

A composição do Docker pode fazer a substituição de variáveis na configuração, para que possa ser usada para passar senhas do host para o docker.

Bunyk
fonte
Agradeço a referência à Bíblia.
JakeCowton 19/06
-2

Embora eu concorde totalmente, não há uma solução simples. Continua a haver um único ponto de falha. O dockerfile, etcd e assim por diante. O Apcera tem um plano que se parece com o ajudante - autenticação dupla. Em outras palavras, dois contêineres não podem falar, a menos que haja uma regra de configuração do Apcera. Na demonstração, o uid / pwd estava limpo e não pôde ser reutilizado até que o administrador configurasse a ligação. Para que isso funcione, no entanto, provavelmente significou corrigir o Docker ou, pelo menos, o plug-in de rede (se houver).

Richard
fonte
2
Existe uma resposta em algum lugar para a pergunta feita?
Abhijit Sarkar