Docker et sécurisation des mots de passe

162

J'ai récemment expérimenté Docker sur la création de services avec lesquels jouer et une chose qui me harcèle toujours est de mettre des mots de passe dans un Dockerfile. Je suis un développeur, donc stocker les mots de passe dans la source ressemble à un coup de poing dans le visage. Cela devrait-il même être un problème? Existe-t-il de bonnes conventions sur la façon de gérer les mots de passe dans Dockerfiles?

anthonateur
la source
7
Il y a un problème ouvert sur Github demandant les meilleures pratiques concernant Docker et les secrets, le problème est ici: github.com/docker/docker/issues/13490
Luís Bianchin

Réponses:

93

C'est certainement une préoccupation. Les fichiers Docker sont généralement archivés dans des référentiels et partagés avec d'autres personnes. Une alternative consiste à fournir des informations d'identification (noms d'utilisateur, mots de passe, jetons, tout ce qui est sensible) en tant que variables d'environnement lors de l'exécution . Cela est possible via l' -eargument (pour les variables individuelles sur la CLI) ou l' --env-fileargument (pour plusieurs variables dans un fichier) à docker run. Lisez ceci pour utiliser l'environnement avec docker-compose.

L'utilisation --env-fileest certainement une option plus sûre car elle protège contre les secrets apparaissant dans psou dans les journaux si l'on utilise set -x.

Cependant, les variables d'environnement ne sont pas non plus particulièrement sécurisées. Ils sont visibles via docker inspect, et sont donc disponibles pour tout utilisateur pouvant exécuter des dockercommandes. (Bien sûr, tout utilisateur qui a accès à dockersur l'hôte a également la racine de toute façon.)

Mon modèle préféré est d'utiliser un script wrapper comme ENTRYPOINTou CMD. Le script wrapper peut d'abord importer des secrets d'un emplacement extérieur dans le conteneur au moment de l'exécution, puis exécuter l'application en fournissant les secrets. Les mécanismes exacts de cela varient en fonction de votre environnement d'exécution. Dans AWS, vous pouvez utiliser une combinaison de rôles IAM, du service de gestion de clés et de S3 pour stocker des secrets chiffrés dans un compartiment S3. Quelque chose comme HashiCorp Vault ou credstash est une autre option.

AFAIK, il n'y a pas de modèle optimal pour l'utilisation de données sensibles dans le cadre du processus de construction. En fait, j'ai une question SO sur ce sujet. Vous pouvez utiliser docker-squash pour supprimer des calques d'une image. Mais il n'y a pas de fonctionnalité native dans Docker à cet effet.

Vous pouvez trouver des commentaires timides sur la configuration dans des conteneurs utiles.

Ben Whaley
la source
Comme indiqué dans d'autres commentaires, il y aura 2 couches (après ADD et après le premier RUN) qui contiennent le .configfichier.
Petr Gladkikh
1
Oui, les variables env semblent être la meilleure solution. J'ai examiné cela dans le contexte du développement de TDDing Dockerfile.
gnoll110
5
Je crains que si votre mot de passe est une variable env, il apparaisse dans docker inspect.
slim
Une installation par défaut de docker (sous Linux) nécessite des privilèges sudoer pour s'exécuter docker inspect. Si l'attaquant peut déjà sudo, arracher votre mot de passe de docker inspect est probablement assez bas sur votre liste de choses qui peuvent maintenant mal tourner. Ce détail particulier me semble être un risque acceptable.
GrandOpener
7
@GrandOpener Cela s'applique uniquement à la situation où un attaquant utilise votre système. Si je pousse une image docker vers un référentiel et qu'elle est tirée par quelqu'un d'autre, je m'en fiche de savoir s'ils ont sudo sur leur propre système, mais je me soucie vraiment de savoir s'ils voient des secrets dans env qui ne sont plus censés y être.
vee_ess
75

Notre équipe évite de mettre des informations d'identification dans des référentiels, ce qui signifie qu'elles ne sont pas autorisées Dockerfile. Notre meilleure pratique dans les applications consiste à utiliser des creds à partir de variables d'environnement.

Nous résolvons pour cela en utilisant docker-compose.

Dans docker-compose.yml, vous pouvez spécifier un fichier contenant les variables d'environnement du conteneur:

 env_file:
- .env

Assurez-vous d'ajouter .envà .gitignore, puis définissez les informations d'identification dans le .envfichier comme:

SOME_USERNAME=myUser
SOME_PWD_VAR=myPwd

Stockez le .envfichier localement ou dans un emplacement sécurisé où le reste de l'équipe peut le récupérer.

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

theUtherSide
la source
15
Vous pouvez également le faire sans fichier .env, si vous le souhaitez. Utilisez simplement la propriété d'environnement dans votre fichier docker-compose.yml. "Les variables d'environnement avec seulement une clé sont résolues en leurs valeurs sur la machine sur laquelle Compose est en cours d'exécution, ce qui peut être utile pour les valeurs secrètes ou spécifiques à l'hôte."
D.Visser
1
donne un cookie à cet homme! :) oui c'est vraiment une bonne pratique Je veux juste ajouter que docs.docker.com/compose/env-file cela devrait fonctionner automatiquement mais dans la version 2 de docker compose, il semble que vous deviez le déclarer comme décrit dans cette réponse.
équivalent8
5
L'utilisation de variables d'environnement est déconseillée par l'équipe Docker elle-même, car env var peut être vu via / proc / <pid> / environ et docker inspect. Cela ne fait qu'obscurcir la manière d'obtenir les informations d'identification d'un attaquant qui a obtenu un accès root. Bien sûr, les informations d'identification ne devraient jamais être suivies par le CVS. Je suppose que le seul moyen d'empêcher un utilisateur root d'obtenir des informations est de lire les informations d'identification depuis l'application Web (en espérant qu'il ne met pas à jour son fichier proc environ) à partir d'un fichier crypté, le processus de décryptage demandant un mot de passe en toute sécurité. Je pense que je vais essayer avec une tombe: github.com/dyne/Tomb
pawamoy
.gitignoreafin que le .envfichier contenant des informations sensibles ne soit pas archivé sur GitHub. Je suis sûr que cela ne fonctionnera pas si vous l'ajoutez.dockerignore
theUtherSide
Salut @theUtherSide, merci pour votre réponse, j'avais une question, lorsque je ne consens pas le .envfichier et que je fais un déploiement sur un serveur sur site, suggérez-vous de créer à .envnouveau le fichier sur le serveur manuellement?
opensource-developer
37

Docker maintenant (version 1.13 ou 17.06 et supérieure) prend en charge la gestion des informations secrètes. Voici un aperçu et une documentation plus détaillée

Une fonctionnalité similaire existe dans kubernetes et DCOS

Heather QC
la source
Quelques commandes utiles à partir des liens ci-dessus docker secret create:: créer un secret docker secret inspect: afficher des informations détaillées sur un secret docker secret ls: afficher tous les secrets docker secret rm: supprimer un --secretindicateur de secret spécifique pour docker service create: créer un secret lors de la création du service --secret-addet des --secret-rmindicateurs pour docker service update: mettre à jour la valeur d'un secret ou supprimer un secret pendant la tâche de mise à jour du service. Les secrets Docker sont protégés au repos sur les nœuds de gestionnaire et fournis aux nœuds de travail lors du démarrage du conteneur.
PJ
7
Oui, vous devez configurer un essaim pour utiliser les secrets de Docker
Heather QC
11
Ceci est un bon début sur une réponse, mais a besoin de beaucoup plus d'informations de ce qui est lié pour apparaître dans la réponse elle-même.
Jeff Lambert
7
Je ne suis pas sûr que cela puisse être la réponse acceptée si cela ne fonctionne qu'avec des essaims. Beaucoup de gens n'utilisent pas d'essaims, mais ont encore besoin de passer des secrets.
John Y
9

Vous ne devez jamais ajouter d'informations d'identification à un conteneur à moins que vous ne souhaitiez diffuser les informations à quiconque peut télécharger l'image. En particulier, faire et ADD credset plus RUN rm credsn'est pas sécurisé car le fichier creds reste dans l'image finale dans une couche intermédiaire du système de fichiers. Il est facile pour quiconque ayant accès à l'image de l'extraire.

La solution typique que j'ai vue lorsque vous avez besoin de creds pour extraire des dépendances, c'est d'utiliser un conteneur pour en créer un autre. Par exemple, vous avez généralement un environnement de construction dans votre conteneur de base et vous devez l'appeler pour créer votre conteneur d'application. La solution simple consiste donc à ajouter la source de votre application, puis RUNles commandes de construction. Ce n'est pas sûr si vous avez besoin de crédits pour cela RUN. Au lieu de cela, vous placez votre source dans un répertoire local, exécutez (comme dans docker run) le conteneur pour effectuer l'étape de construction avec le répertoire source local monté en tant que volume et les creds injectés ou montés en tant qu'un autre volume. Une fois l'étape de construction terminée, vous construisez votre conteneur final en insérant simplement ADDle répertoire source local qui contient maintenant les artefacts générés.

J'espère que Docker ajoute quelques fonctionnalités pour simplifier tout cela!

Mise à jour: il semble que la méthode à venir sera d'avoir des versions imbriquées. En bref, le dockerfile décrirait un premier conteneur utilisé pour créer l'environnement d'exécution, puis une deuxième génération de conteneur imbriquée qui peut assembler toutes les pièces dans le conteneur final. De cette façon, les éléments de construction ne sont pas dans le deuxième conteneur. Il s'agit d'une application Java où vous avez besoin du JDK pour créer l'application, mais uniquement du JRE pour l'exécuter. Un certain nombre de propositions sont en cours de discussion, il est préférable de commencer à partir de https://github.com/docker/docker/issues/7115 et de suivre certains des liens pour des propositions alternatives.

TvE
la source
7

Une alternative à l'utilisation de variables d'environnement, qui peut devenir compliquée si vous en avez beaucoup, consiste à utiliser des volumes pour rendre un répertoire sur l'hôte accessible dans le conteneur.

Si vous placez toutes vos informations d'identification sous forme de fichiers dans ce dossier, le conteneur peut lire les fichiers et les utiliser à sa guise.

Par exemple:

$ 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

De nombreux programmes peuvent lire leurs informations d'identification à partir d'un fichier séparé, vous pouvez donc simplement pointer le programme vers l'un des fichiers.

Malvineous
la source
5

solution d'exécution uniquement

docker-compose fournit également une solution en mode non-swarm (depuis v1.11: Secrets utilisant des montages de liaison ).

Les secrets sont montés sous forme de fichiers ci /run/secrets/- dessous par docker-compose. Cela résout le problème au moment de l'exécution (exécution du conteneur), mais pas au moment de la construction (création de l'image), car il /run/secrets/n'est pas monté au moment de la construction. De plus, ce comportement dépend de l'exécution du conteneur avec docker-compose.


Exemple:

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

Pour construire, exécutez:

docker-compose up -d

Lectures complémentaires:

Murmel
la source
2

Avec Docker v1.9, vous pouvez utiliser l' instruction ARG pour récupérer les arguments passés par ligne de commande à l'image lors de l' action de construction . Utilisez simplement l' indicateur --build-arg . Ainsi, vous pouvez éviter de conserver un mot de passe explicite (ou d'autres informations sensibles) sur le Dockerfile et de les transmettre à la volée.

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

Exemple:

Dockerfile

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

commande de construction d'image

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

pendant la construction, il imprime

$ 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

J'espère que ça aide! Au revoir.

NickGnd
la source
26
Selon la documentation ARG de Docker : "Il n'est pas recommandé d'utiliser des variables de construction pour transmettre des secrets tels que les clés github, les informations d'identification de l'utilisateur, etc."
Lie Ryan
3
Je me demande simplement pourquoi Docker déconseille d'utiliser --build-arg var=secretpour passer une clé privée SSH dans une image, il n'y a aucune justification documentée. Quelqu'un peut-il l'expliquer?
Henk Wiersema
2
@HenkWiersema Les informations de processus, les journaux et l'historique des commandes ne sont pas sécurisés. Les informations de processus sont disponibles publiquement et incluent tous les paramètres de ligne de commande. Souvent, ces appels aboutissent dans des journaux qui peuvent également être publics. Il n'est pas rare qu'un attaquant inspecte les informations sur les processus en cours d'exécution et recherche des secrets dans les fichiers journaux publics. Même s'il n'est pas public, il pourrait être stocké dans l'historique de vos commandes, ce qui permettrait à quelqu'un d'obtenir facilement des secrets via un compte non administratif.
tu-Reinstate Monica-dor duh
2
Quelle est la méthode recommandée pour fournir les informations d'identification nécessaires au moment de la construction? Par exemple, une image qui a besoin d'un accès aws s3 pour récupérer un grand ensemble de données qui résidera à l'intérieur de l'image?
ely
4
J'imagine que la raison pour laquelle il n'est pas recommandé est parce que docker historyexpose build-arg/ ARGvariables. On peut extraire n'importe quelle image, l'inspecter et voir tous les secrets passés lors de la construction en tant que paramètre build-arg/ ARG.
vee_ess
2

Mon approche semble fonctionner, mais elle est probablement naïve. Dites-moi pourquoi c'est faux.

Les ARG définis lors de la construction du docker sont exposés par la sous-commande history, donc n'allez pas là-bas. Cependant, lors de l'exécution d'un conteneur, les variables d'environnement données dans la commande d'exécution sont disponibles pour le conteneur, mais ne font pas partie de l'image.

Donc, dans le Dockerfile, effectuez une configuration qui n'implique pas de données secrètes. Définissez un CMD de quelque chose comme /root/finish.sh. Dans la commande d'exécution, utilisez des variables d'environnement pour envoyer des données secrètes dans le conteneur. finish.shutilise les variables essentiellement pour terminer les tâches de construction.

Pour faciliter la gestion des données secrètes, placez-les dans un fichier chargé par le docker exécuté avec le --env-filecommutateur. Bien sûr, gardez le fichier secret. .gitignoreet autres choses de ce genre.

Pour moi, finish.shexécute un programme Python. Il vérifie qu'il n'a pas été exécuté avant, puis termine l'installation (par exemple, copie le nom de la base de données dans Django settings.py).

Kieran Mathieson
la source
2

Il existe une nouvelle commande docker pour la gestion des "secrets". Mais cela ne fonctionne que pour les amas d'essaims.

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
la source
1

La méthodologie de l'application 12-Factor indique que toute configuration doit être stockée dans des variables d'environnement.

Docker compose pourrait effectuer une substitution de variable dans la configuration, ce qui pourrait être utilisé pour passer des mots de passe d'hôte à docker.

Bunyk
la source
J'apprécie la référence à la Bible.
JakeCowton
-2

Bien que je sois totalement d'accord, il n'y a pas de solution simple. Il y a toujours un point de défaillance unique. Soit le fichier dockerfile, etcd, etc. Apcera a un plan qui ressemble à un sidekick - une double authentification. En d'autres termes, deux conteneurs ne peuvent pas parler sauf s'il existe une règle de configuration Apcera. Dans leur démo, l'uid / pwd était en clair et ne pouvait pas être réutilisé jusqu'à ce que l'administrateur ait configuré le lien. Pour que cela fonctionne, cependant, cela impliquait probablement de patcher Docker ou au moins le plugin réseau (s'il y a une telle chose).

Richard
la source
2
Y a-t-il une réponse quelque part à la question posée?
Abhijit Sarkar