Aller au contenu principal

Pour que vos secrets le restent

Une des premières règles de sécurité est de garder ses secrets bien au chaud et ne pas les divulguer. Parmi ces secrets, on trouve les certificats, les mots de passe, les tokens d'api, ...

Mais comment ne pas les embarquer dans un container?

Et bien docker, comme Podman, ont commencé à y répondre, mais partiellement. D'ailleurs pour les kubernetiens tout cela doit leur sembler bien naturel puisque pris en charge. Mais je suis sûr qu'ils se sont posé la question pour le build des images.

En aucun cas je ne dois trouver ces secrets dans l'image finale. Si quelqu'un récupérais l'image, il pourrait facilement retrouver ces infos. Je suis certain qu'en cherchant dans les images du dockerhub on peut trouver des infos de ce type. D'ailleurs, je suis certain que les hackers puisent dedans. Un billet intéressant sur le sujet

Construire des images ne contenant pas de secrets

Je vais prendre un des besoins que je rencontre souvent. Comment intégrer un fichier de config contenant des mots de passe d'accès à Nexus (là, on stocke le résultat des builds).

Les dernières versions de Docker mettent à disposition un nouveau système de build appelé BuildKit, qui inclut la gestion des secrets, ainsi que le transfert d'authentification de l'agent SSH. Voyons comment l'utiliser la partie des secrets.

Pour activer cette fonctionnalité 2 moyens :

  • export DOCKER_BUILDKIT=1 avant de lancer les commandes de build
  • ajouter au fichier /etc/docker/daemon.json la clé features buildkit. Il faudra redémarrer le service Docker.

Le fichier complet :

{
  "registry-mirrors": [],
  "insecure-registries": [],
  "debug": false,
  "experimental": false,
  "features": {
    "buildkit": true
  }
}

Passons à l'écriture du Dockerfile

FROM python:3.9-slim as builder
RUN useradd -ms /bin/bash user && apt update && apt install -y --no-install-recommends curl
USER user
RUN --mount=type=secret,id=pipconfig,dst=/home/user/.config/pip/pip.conf,uid=1000,gid=1000 pip install -r requirements

Les habitués auront vite remarqué la présence derrière le RUN la directive mount. La documentation se trouve ici

RUN --mount=type=secret,id=pipconfig,dst=/home/user/.config/pip/pip.conf,uid=1000,gid=1000 ....

On indique simplement que l'on veut monter un volume un fichier avec l'id sur le point de montage dst avec les champs uid et gid pour indiquer qui seront le owner et le groupe de ce fichier. Il existe d'autres options mode par exemple.

Pour lancer le build :

docker build --secret id=pipconfig,src=/etc/pip.conf . -t python:0.5 --progress=plain --no-cache
  • l'option --secret id=pipconfig,src=/etc/pip.conf permet d'indiquer comment créer les secrets. Ici je crée le secret avec l'id dont la source est le fichier src.
  • l'option --progress=plain permet d'avoir la sortie normale du build et non le condensé que propose buildkit
  • l'option --no-cache permet d'indiquer de ne pas utiliser le cache

Maintenant contrôlons que dans l'image ne se trouve pas les fameux secrets :

docker run -it python:0.5 bash

user@7fe851946d90:/$ ls -al /home/user/.config/pip/
total 0
drwxr-xr-x 2 root root  6 Oct 21 15:30 .
drwxr-xr-x 3 root root 17 Oct 21 15:30 ..

Super pas de fichier présent. Maintenant voyons comment lancer un container en intégrant des secrets.

Exécuter des containers en intégrant des secrets

Avec la commande docker

La commande docker possède aussi la directive --mount type=<type>,src=<source>,dst=<dest>[,readonly][,bind-propagation=<propagation] : type peut prendre la valeur bind, volume ou tmpfs.

Un exemple monte le fichier pip.conf dans le répertoire /etc

docker run --mount type=bind,src=/etc/pip.conf,dst=/etc/pip.conf -v $PWD:/src -it artefacts.robert.local/python:0.4 bash

Pour rappel, par défaut les volumes montés via l'option -v sont stocké dans /var/lib/docker/volumes. La directive mount vient en remplacer le contenu s'il existe déjà. Si le fichier ou le répertoire n'existe pas il est créé.

Les mounts de type tmpfs permettent de créer des espaces de travail temporaire en mémoire, qui seront donc détruit à l’arrêt du conteneur. Vous imaginez bien qu'ici les performances sont maximales. Bien sûr, dans ce type on ne spécifie pas de source.

Avec docker-compose

On peut également utiliser la commande docker-compose. Voici pour rappel les commandes pour l'installer.

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Il faut écrire un fichier docker-compose.yml dont voici le contenu :

version: "3.9"
services:
  python:
    image: python:0.4
    secrets:
      - source: pipconfig
        target: /etc/pip.conf
      - source: certificate
        target: /etc/pki/ca-trust/source/anchors/server.crt
    network_mode: bridge
secrets:
  pipconfig:
    file: /etc/pip.conf
  certificate:
    file: /etc/pki/ca-trust/source/anchors/server.crt

Il suffit comme vous pouvez le remarquer, de créer ses secrets dans la catégorie générale du même nom.

Comme pour le build d'indiquer dans le service ou l'utiliser. Si on n'indique pas de target, ils se retrouveront dans le répertoire /run/secrets.

Vous trouverez la documentation ici

Pour lancer le container, il suffit d'utiliser la commande :

docker-compose run --rm python bash

On indique le nom du service à lancer.

docker-compose run --rm python bash

Creating python_python_run ... done
user@ba46e44dc6d8:/$ cat /etc/pip.conf
[global]
index =  https://python:xxxxx@artefacts.robert.local/repository/pypi-all/pypi
index-url = https://python:xxxxx@artefacts.robert.local/repository/pypi-all/simple/
timeout = 10
trusted-host = artefacts.robert.local

Comment l'intégrer le build dans un pipeline Gitlab

C'est assez simple, il suffit de créer des variables dans les paramètres CI/CD sous la forme de fichier comme dans la copie d'écran ci-dessous :

Dans le script CI, il suffit de récupérer le fichier et de le stocker dans un fichier.

cat "$mon-secret" > "$(pwd)/mon-secret"
docker build --secret id=pipconfig,src=$(pwd)/mon-secret . -t python:0.5 --progress=plain --no-cache

Et voilà le tour est joué. Vos SECRETS sont bien au chaud.