Skip to main content

Outils en ligne de commande pour les conteneurs : utiliser Snyk avec Buildah, Podman et Skopeo

Écrit par

9 décembre 2020

0 minutes de lecture

À mesure que l’écosystème des conteneurs a mûri, les options ne manquent pas, tant pour les logiciels que nous utilisons que pour la façon dont nous les assemblons.

Parmi ces options, on trouve l’association de Buildah, Podman et Skopeo, trois outils en ligne de commande open source issus de l’écosystème RedHat. Comme son nom l’indique, Buildah offre un large éventail de fonctionnalités pour créer des conteneurs et des images conformes à OCI. Podman permet de gérer et d’exécuter des conteneurs OCI, tandis que Skopeo est un outil conçu pour interagir avec les registres de conteneurs.

Dans cet article, nous verrons comment les normes ouvertes permettent à Snyk d’analyser les conteneurs avec toutes ces technologies.

Commençons par Skopeo. Cet outil en ligne de commande permet de transférer des images de conteneurs entre différents dépôts et peut également les enregistrer localement. Skopeo peut générer des images au format d’archive Docker ou OCI, deux formats pris en charge par Snyk pour l’analyse depuis le système de fichiers. Utilisons-le pour télécharger une image depuis Quay.io et l’analyser localement depuis notre système de fichiers :

$ skopeo copy docker://quay.io/tutum/hello-world oci-archive:hello-world.tar
$ snyk container test oci-archive:hello-world.tar 

Testing oci-archive:hello-world.tar…

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in openssl
  Description: Out-of-Bounds
  Info: https://snyk.io/vuln/SNYK-UBUNTU1210-OPENSSL-374571
  Introduced through: openssl@1.0.1c-3ubuntu2.6, ca-certificates@20120623, ssl-cert@1.0.32, meta-common-packages@meta
  From: openssl@1.0.1c-3ubuntu2.6
  From: ca-certificates@20120623 > openssl@1.0.1c-3ubuntu2.6
  From: ssl-cert@1.0.32 > openssl@1.0.1c-3ubuntu2.6
  and 1 more...
  Fixed in: 1.0.1c-3ubuntu2.7

Organization:      matt-jarvis-snyk
Package manager:   deb
Project name:      docker-image|hello-world.tar
Docker image:      oci-archive:hello-world.tar
Licenses:          enabled

Tested 237 dependencies for known issues, found 25 issues.

Ubuntu 12.10 is no longer supported by the Ubuntu maintainers. Vulnerability detection may be affected by a lack of security updates.

Comme nous pouvons le constater, cette image utilise une version d’Ubuntu en fin de vie et contient donc plusieurs vulnérabilités qui ne seront pas corrigées. Skopeo nous permet ainsi de télécharger des images depuis n’importe quel registre de conteneurs conforme aux normes, dans des formats pris en charge par Snyk, puis de les analyser localement.

Buildah

Passons maintenant à Buildah. Buildah est un outil en ligne de commande qui crée des images, mais ne les exécute pas : il est donc destiné à être utilisé avec Podman. Il est également conçu pour fonctionner sans accès root, en tirant parti des espaces de noms utilisateur du noyau Linux pour isoler un shell similaire à root, mais disposant uniquement de privilèges utilisateur. Les administrateurs peuvent ainsi permettre aux développeurs de créer et de gérer des images de conteneurs, tout en respectant le principe du moindre privilège dans l’environnement de compilation.

Buildah peut utiliser des Dockerfiles comme Docker, mais permet aussi de développer des conteneurs et des images avec d’autres méthodes, par exemple Bash ou Python. Cela offre davantage de flexibilité et peut faciliter le débogage des compilations de conteneurs. Buildah permet notamment de monter le conteneur intermédiaire pendant la compilation : les scripts peuvent alors vérifier que tout s’est correctement exécuté avant de poursuivre, compiler des logiciels à l’extérieur du conteneur ou même demander des informations à l’utilisateur. Cette méthode offre une alternative aux processus de compilation en plusieurs étapes, parfois complexes, qui se sont imposés comme bonnes pratiques avec Docker. 

Voyons un exemple d’utilisation de Buildah.

À noter tout d’abord : avec Buildah, vous pouvez simplement utiliser des Dockerfiles pour créer vos conteneurs si cette méthode vous convient. Prenons ce Dockerfile comme exemple :

$ cat Dockerfile 
FROM centos:8
LABEL maintainer Matt Jarvis <matt@mattjarvis.org.uk>

RUN dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake && dnf clean all

RUN git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

WORKDIR /opt/hello

RUN ./bootstrap --skip-po && ./configure && make && make install

RUN ./hello
ENTRYPOINT "/usr/local/bin/hello"

Nous utilisons ici l’image CentOS 8 comme base, clonons le code source de GNU Hello, installons quelques prérequis, puis compilons et installons le binaire.

Nous pouvons automatiser cette compilation avec Buildah et l’option bud (build-using-dockerfile) :

$ buildah bud

Ce n’est clairement pas une bonne pratique, car l’image finale contient à la fois le code source et les prérequis de compilation, et ne respecte pas la plupart des bonnes pratiques pour créer des images sécurisées.  Avec Docker, la bonne pratique consisterait ici à utiliser une compilation en plusieurs étapes. Nous pourrions créer un Dockerfile multiétape avec Buildah, mais il est encore plus intéressant d’utiliser les commandes natives de Buildah et d’écrire des scripts. Pour reproduire exactement ce que nous avons fait avec le Dockerfile ci-dessus, nous exécuterions le script suivant :

$ cat single-stage.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)

# Labels are part of the "buildah config" command
buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Install pre-reqs
buildah run $container dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake
buildah run $container dnf clean all

# Clone the git repository
buildah run $container git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

# Workingdir is also a "buildah config" command
buildah config --workingdir /opt/hello $container

buildah run $container ./bootstrap --skip-po
buildah run $container ./configure
buildah run $container make
buildah run $container make install

# Entrypoint is also a “buildah config” command
buildah config --entrypoint /usr/local/bin/hello $container

# Finally save the running container to an image
buildah commit --format docker $container hello:latest

Buildah nous permet toutefois de monter le conteneur intermédiaire pendant la compilation. Nous pouvons donc compiler le code source sur l’hôte et simplement copier le binaire obtenu dans notre conteneur avant de créer l’image. Nous profitons ainsi des ressources de l’hôte pendant les compilations et pouvons, par exemple, utiliser le gestionnaire de paquets de l’hôte pour installer des paquets dans notre conteneur. Nous n’avons pas non plus besoin d’un accès root pour cela : Buildah prend en charge les espaces de noms utilisateur, qui permettent d’effectuer un montage sans privilèges avec la commande unshare :

$ buildah unshare
$ cat host-mount.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)
mountpoint=$(buildah mount $container)

buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Clone the git repository to the host
git clone https://git.savannah.gnu.org/git/hello.git hello

pushd hello
./bootstrap --skip-po
./configure
make
# Install to the container
make install DESTDIR=${mountpoint}
popd

chroot $mountpoint bash -c "/usr/local/bin/hello"

buildah config --entrypoint "/usr/local/bin/hello" $container
buildah commit --format docker $container hello
buildah unmount $container

Comme le montre l’exemple ci-dessus, après l’exécution de la commande unshare, nous disposons d’un shell root, mais dans un espace de noms utilisateur sans privilèges root complets. Nous pouvons ainsi monter le système de fichiers du conteneur pendant la compilation. Comme il s’agit d’un script Bash, nous pouvons également tirer parti de toutes les fonctionnalités offertes par un langage de programmation : utiliser des conditions, des boucles ou même demander des informations à l’utilisateur pendant le processus.

Podman

Une fois le script exécuté et notre image créée, nous pouvons aussi l’analyser avec Snyk. C’est là que Podman entre en jeu. Podman est un moteur de conteneurs sans démon, conçu pour développer, gérer et exécuter des conteneurs OCI. Buildah et Podman reposant sur des normes ouvertes, Snyk a toujours pu analyser les images créées ou téléchargées avec Podman : il suffit d’utiliser Podman pour enregistrer l’image sur le disque et de l’analyser depuis le système de fichiers. Podman permet d’enregistrer les images au format d’archive Docker standard ou au format d’archive OCI, tous deux pris en charge par Snyk.

[root@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  15 minutes ago  223 MB
[root@localhost buildah_test]# podman save 975f60fc1f19 -o hello.tar
[root@localhost buildah_test]# snyk container test docker-archive:hello.tar

Testing docker-archive:hello.tar...

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|hello.tar
Docker image:      docker-archive:hello.tar
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker docker-archive:hello.tar --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

Notre image de base étant une image publique disponible sur Dockerhub, nous pourrions aussi simplement lancer cette commande pour l’analyser avec Snyk CLI :

[root@localhost buildah_test]# snyk container test centos:8

Dans ce cas, Snyk communique directement avec Dockerhub et analyse l’image d’origine.

En revanche, si l’image existe uniquement dans Podman en local ou se trouve dans un dépôt privé sur Dockerhub qui nécessite une connexion, aucune de ces méthodes ne fonctionnera. Essayons, par exemple, d’analyser directement depuis Podman l’image que nous avons créée :

[matt@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  20 minutes ago  223 MB

[matt@localhost buildah_test]# snyk container test localhost/hello
connect ECONNREFUSED 127.0.0.1:443

Pour ces cas d’utilisation, Snyk se sert du client Docker local, soit via l’API Registry, soit en utilisant le socket Docker. Comme le système ne contient aucun binaire Docker, Snyk a ici tenté d’utiliser l’API Registry, sans parvenir à se connecter. Par défaut, Snyk ne sait pas que ces images sont présentes localement dans Podman ni comment utiliser Podman pour accéder aux images d’origine.

Heureusement, Podman offre des fonctionnalités de compatibilité simples grâce au paquet podman-docker. Celui-ci fournit un faux binaire Docker, qui est en fait un wrapper shell du binaire Podman, et crée un lien symbolique entre le socket de l’API Podman et l’emplacement par défaut du fichier socket Docker.  Par défaut, ce paquet crée un lien symbolique vers /var/run/podman/podman.sock, l’emplacement utilisé lorsque l’API Podman s’exécute avec les privilèges root.

[matt@localhost buildah_test]$ sudo dnf install podman-docker

Now let’s try and scan our local image again :

[matt@localhost buildah_test]$ snyk container test localhost/hello
connect EACCES /var/run/docker.sock

Cela ne fonctionne toujours pas, mais nous obtenons cette fois une erreur différente. Snyk a détecté un binaire nommé docker et tente donc de se connecter à l’emplacement par défaut du socket Docker privilégié. Comme l’API Podman n’est pas encore lancée, la connexion échoue à nouveau.

Comme nous l’avons vu plus tôt, le paquet podman-docker crée un lien symbolique entre /var/run/podman/podman.sock et /var/run/docker.sock, en supposant que l’API Podman sera exécutée avec les privilèges root.

[matt@localhost buildah_test]$ ls -l /var/run/docker.sock 
lrwxrwxrwx. 1 root root 23 Nov 25 07:51 /var/run/docker.sock -> /run/podman/podman.sock

L’un des principaux problèmes du socket Docker est qu’il s’exécute par défaut avec les privilèges root, ce qui représente un risque de sécurité dans certains environnements. Dans la plupart des environnements de développement locaux, nous n’avons pas besoin d’exécuter l’API Podman de cette manière : nous pouvons utiliser un socket sans privilèges à un autre emplacement. Nous pouvons également indiquer cet emplacement à Snyk à l’aide de la variable d’environnement DOCKER_HOST, qui prend en charge les formats d’URL tcp:// et unix://.

Dans un autre shell, lançons l’API Podman avec un socket sans privilèges :

[matt@localhost ~]$ podman system service --time=0 unix://home/matt/podman.sock

Le processus s’exécutera au premier plan. Vous devrez donc appuyer sur Ctrl-C une fois le test terminé. Définissons maintenant la variable d’environnement DOCKER_HOST :

[matt@localhost buildah_test]$ export DOCKER_HOST=unix:///home/matt/podman.sock

Enfin, essayons à nouveau d’analyser notre image locale avec Snyk :

[matt@localhost buildah_test]$ snyk container test localhost/hello

Testing localhost/hello...

{ ----SNIPPED OUTPUT----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|localhost/hello
Docker image:      localhost/hello
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker localhost/hello --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

Ça fonctionne ! Snyk communique maintenant correctement avec Podman via le socket sans privilèges.

Pour configurer DOCKER_HOST de façon permanente dans votre shell, vous pouvez ajouter la commande export à votre fichier .bashrc afin que le paramètre soit conservé.

Enfin, nous aimerions que toute cette configuration soit automatiquement disponible à chaque connexion. Pour cela, nous pouvons tirer parti des fonctionnalités d’activation par socket de systemd afin de lancer automatiquement notre socket lorsque nous nous connectons et essayons de l’utiliser.

Commençons par configurer le socket :

[matt@localhost ~]$ cat .config/systemd/user/podman.socket
[Unit]
Description=Podman API Socket
Documentation=man:podman-api(1)

[Socket]
ListenStream=/home/matt/podman.sock
SocketMode=0660

[Install]
WantedBy=sockets.target

Puis associons le socket à un service :

[matt@localhost ~]$ cat .config/systemd/user/podman.service
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-api(1)
StartLimitIntervalSec=0

[Service]
Type=oneshot
Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf
ExecStart=/usr/bin/podman system service unix:///home/matt/podman.sock
TimeoutStopSec=30
KillMode=process

[Install]
WantedBy=multi-user.target
Also=podman.socket

Pour finir, rechargeons la configuration de systemd et activons le service !

[matt@localhost ~]$ systemctl --user daemon-reload
[matt@localhost ~]$ systemctl --user enable --now podman.socket

Conclusion

Voilà : l’analyse d’images avec Snyk CLI fonctionne avec Podman exactement comme avec Docker. Les développeurs peuvent ainsi facilement analyser de façon approfondie les images Docker ou OCI locales dans le cadre de leur workflow, sans avoir besoin de privilèges élevés. Nous avons également vu comment utiliser Skopeo et Buildah avec Snyk, grâce aux normes ouvertes !

Vous n’avez pas encore de compte Snyk ? Inscrivez-vous gratuitement et utilisez Snyk pour analyser vos images de conteneurs et vos dépendances open source.

Lancez-vous dans les challenges Capture The Flag

Apprenez à résoudre des challenges Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.