web-dev-qa-db-fra.com

Passage sécurisé des clés secrètes aux conteneurs Docker

Je souhaite transmettre une valeur secrète requise par une application qui s'exécute dans un conteneur Docker. Ce conteneur particulier est de courte durée - il démarre, exécute une commande, puis se termine.

Méthode 1: passez la valeur en tant que variable d'environnement via la ligne de commande lors du démarrage du conteneur (Docker le prend en charge comme argument de ligne de commande pour démarrer un conteneur). J'ai l'impression que c'est mauvais car la commande apparaîtra dans les listes de processus (avec la clé et tout) sur la machine hôte qui a démarré le conteneur Docker.

Méthode 2: passez la valeur en tant que variable env via un fichier de variables env. Cela résout le problème de liste de processus, mais en exécutant docker info sur le conteneur en cours d'exécution de l'hôte affiche une liste de toutes les variables d'environnement définies pour ce conteneur. Cela me fait croire que Docker les stocke quelque part sur le disque sur l'hôte qui n'est pas sûr (car l'ajout d'une nouvelle variable d'environnement dans le conteneur en cours d'exécution ne met pas à jour cette liste, il ne doit pas la lire directement en temps réel).

En général, je pense que les variables d'environnement ne sont pas adéquates pour stocker en toute sécurité des données secrètes (même si ce n'est que temporairement), mais je n'ai pas suffisamment de connaissances pour sauvegarder cette pensée.

Qu'est-ce qu'une méthode sécurisée pour transmettre des données secrètes à un conteneur?

34
Anthony Kraft

Les variables d'environnement sont le meilleur moyen de le faire, en particulier la méthode 2. Docker, par défaut, ne se laisse pas exécuter par des utilisateurs autres que root. L'accès à la prise est interdit. Je dirais que la méthode 2 est raisonnablement sûre, car prête à l'emploi si un attaquant a un accès root (et peut fouiller dans vos conteneurs Docker), vous êtes déjà en mauvais état.

Deux notes de sécurité Docker en général. Soyez extrêmement prudent avec l'activation de l'API, car par défaut, il n'y a pas de cryptage ou d'authentification. Ils ont un moyen d'utiliser les certificats et TLS qu'ils ont documentés, mais procédez avec prudence.

Aussi, si possible, activez SELinux sur votre serveur. Les nouvelles versions de celui-ci sont capables de voir les conteneurs Docker et de créer automatiquement des contextes de sécurité pour chacun. Cela empêche un compromis de conteneur de revenir facilement dans l'hôte. Par défaut, Docker s'exécute en tant qu'utilisateur root, et même avec la directive USER, il s'interface toujours directement avec le noyau contrairement à une machine virtuelle. Cela expose l'hôte à tout exploit de privilège local dès qu'un conteneur de docker est compromis.

10
theterribletrivium

Les gars de Docker ont récemment introduit leur solution native pour cela: https://blog.docker.com/2017/02/docker-secrets-management/

Le modèle d'utilisation est le suivant:

$ echo "This is a secret" | docker secret create my_secret_data -
$ docker service  create --name="redis" --secret="my_secret_data" redis:Alpine

Le secret non chiffré est ensuite monté dans le conteneur dans un système de fichiers en mémoire à /run/secrets/<secret_name>.

Bien que cela ne soit accessible que dans un swarm

Vous pouvez trouver la documentation complète ici: https://docs.docker.com/engine/swarm/secrets/

6
grandrew

Réponse courte

docker build a --secret option pour l'API version 1.39 + .

Longue réponse

API version 1.39+ signifie docker 18.09.0+

Dans les notes de mise à jour, sous la section "Nouvelles fonctionnalités de Docker Engine EE et CE" à 18.09. dit:

  • Version API mise à jour vers 1.39 moby/moby # 37640

La page "Build Enhancements for Docker" dans les guides a une explication un peu dépassée.

J'ai trouvé --secret option at New Docker Build secret information , mais l'explication ici s'est avérée obsolète. Ça dit

Ce Dockerfile sert uniquement à démontrer que le secret est accessible. Comme vous pouvez voir le secret imprimé dans la sortie de la construction. L'image finale construite n'aura pas le fichier secret

mais en fait, le secret n'est pas imprimé dans la sortie de génération. Je pense qu'il est gardé pour la sécurité.

La page "Dockerfile frontend experimental syntaxes" dans buildkit a une explication à jour.

J'ai ensuite trouvé la page suivante.

Comment utiliser docker build --secret

Voici les étapes à suivre.

  1. Assurez-vous d'utiliser la version requise de Docker.
$ docker version
Client: Docker Engine - Community
 Version:           19.03.2
 API version:       1.40
 Go version:        go1.12.8
 Git commit:        6a30dfc
 Built:             Thu Aug 29 05:29:11 2019
 OS/Arch:           linux/AMD64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.2
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.8
  Git commit:       6a30dfc
  Built:            Thu Aug 29 05:27:45 2019
  OS/Arch:          linux/AMD64
  Experimental:     false
 containerd:
  Version:          1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
 runc:
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
  1. Ensemble DOCKER_BUILDKIT variable d'environnement à 1
$ export DOCKER_BUILDKIT=1
  1. Créez un fichier secret.
$ echo "It's a secret" > mysecret.txt
  1. Créez un Dockerfile.
$ cat <<EOF > Dockerfile
# syntax = docker/dockerfile:experimental
FROM Alpine
RUN --mount=type=secret,id=mysecret,target=/foobar cat /foobar | tee /output
EOF

Assurez-vous que vous avez # syntax = docker/dockerfile:experimental à la première ligne dans Dockerfile. Notez que l'exemple ci-dessus est juste pour la démonstration. Vous ne devez pas enregistrer le contenu du secret en utilisation réelle.

  1. Courir docker build avec --secret option.
$ docker build -t secret-example --secret id=mysecret,src=mysecret.txt .
[+] Building 2.3s (8/8) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 176B
 => [internal] load .dockerignore
 => => transferring context: 2B
 => resolve image config for docker.io/docker/dockerfile:experimental
 => CACHED docker-image://docker.io/docker/dockerfile:experimental@sha256:888f21826273409b5ef5ff9ceb90c64a8f8ec7760da30d1ffbe6c3e2d323a7bd
 => [internal] load metadata for docker.io/library/Alpine:latest
 => CACHED [1/2] FROM docker.io/library/Alpine
 => [2/2] RUN --mount=type=secret,id=mysecret,target=/foobar cat /foobar | tee /output
 => exporting to image
 => => exporting layers
 => => writing image sha256:22c44473107b6d1f92095c6400613a7e82c9835f5baaa85853a114e4bb5d8744
 => => naming to docker.io/library/secret-example

Notez le contenu de mysecret.txt n'est PAS imprimé, même dans la sortie de génération.

Vérifiez que le secret est correctement utilisé. Encore une fois, c'est juste à des fins de démonstration.

$ docker run -t secret-example cat /output
It's a secret

J'ai remarqué le contenu de /foobar n'est pas enregistré, mais le fichier vide reste dans l'image créée.

$ docker run -t secret-example ls -l /foobar
-rwxr-xr-x    1 root     root             0 Sep 16 19:16 /foobar
2
hnakamur

docker secret ne fonctionne qu'en mode swarm. Pour le mode local, pour passer un secret simple, comme le mot de passe, nous pouvons lire le mot de passe dans une variable de stdin. La difficulté vient du mode detach, qui se bloque lors de la lecture du tuyau dans le conteneur. Voici une astuce pour contourner:

cid=$(docker run -d -i Alpine sh -c 'read A; echo "[$A]"; exec some-server')
docker exec -i $cid sh -c 'cat > /proc/1/fd/0' <<< _a_secret_

Créez d'abord le démon docker avec -i option, la commande read A suspendra l'attente de l'entrée de /proc/1/fd/0; Exécutez ensuite la deuxième commande docker, en lisant le secret de stdin et redirigez-le vers le dernier processus suspendu. Le secret ne sera lu qu'une seule fois et ne sera pas inspecté.

1
James Z.M. Gao