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?
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.
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/
docker build
a --secret
option pour l'API version 1.39 + .
Dans les notes de mise à jour, sous la section "Nouvelles fonctionnalités de Docker Engine EE et CE" à 18.09. dit:
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é.
J'ai ensuite trouvé la page suivante.
docker build --secret
Voici les étapes à suivre.
$ 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
DOCKER_BUILDKIT
variable d'environnement à 1
$ export DOCKER_BUILDKIT=1
$ echo "It's a secret" > mysecret.txt
$ 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.
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
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é.