Skip to main content

Les formats d’image de conteneur sous le capot

Écrit par
Headshot of Agata Krajewska

Agata Krajewska

18 novembre 2020

0 minutes de lecture

Depuis la sortie de Docker, ces dernières années, les conteneurs sont devenus un mécanisme de plus en plus standard pour la distribution des logiciels.

Nous voyons apparaître un nombre croissant de solutions basées sur les conteneurs. Si l’innovation dans ce domaine est évidemment bienvenue, il est nécessaire d’établir certaines normes concernant le format et le runtime.

Compte tenu de la croissance rapide du projet Docker, les images Docker sont devenues une norme pour de nombreux usages. Mais il existe un intérêt largement partagé pour une spécification de conteneur unique et ouverte, qui soit :

  • indépendante des constructions de plus haut niveau, comme un client ou une pile d’orchestration spécifique,

  • sans lien étroit avec un fournisseur commercial ou un projet en particulier,

  • portable sur un large éventail de systèmes d’exploitation, de matériels, d’architectures de processeur, de clouds publics, etc.

OCI (Open Container Initiative) est un projet de la Linux Foundation visant à concevoir des normes ouvertes pour la virtualisation au niveau du système d’exploitation, et en particulier pour les conteneurs Linux.

Docker a fait don à l’OCI d’un projet de spécification du format de base et du runtime, ainsi que du code associé à une implémentation de référence de cette spécification.

Divers outils de compilation open source prennent désormais en charge le format d’image OCI, notamment :

  • BuildKit : une réécriture optimisée du moteur de compilation de Docker

  • Podman : une implémentation alternative de l’outil en ligne de commande de Docker

  • Buildah : une alternative en ligne de commande à l’écriture de Dockerfiles

Snyk prend en charge tous les formats d’image de conteneur courants. Nous pouvons ainsi nous intégrer à un large éventail d’outils de l’écosystème. Dans cet article, nous allons en présenter quelques-uns et vous montrer comment Snyk fonctionne avec eux !

Archive Docker

Commençons par examiner le format d’archive Docker. Il fait partie de la version 1, désormais obsolète, de la spécification des images Docker. Vous trouverez la spécification complète ici. Lorsque vous exécutez docker save, il s’agit du format par défaut généré par le client Docker, utilisé pour assurer la rétrocompatibilité avec d’autres outils. Les registres de conteneurs utilisent un format plus récent pour stocker les images.

Si vous utilisez Docker en local avec l’interface de ligne de commande Snyk ou l’une de nos intégrations de conteneurs, nous utiliserons votre instance Docker locale pour extraire l’image, l’enregistrer sur le système de fichiers et lancer l’analyse.

Voyons à quoi ressemble cette archive et ce qu’elle contient d’intéressant pour Snyk !

Dans cet article, j’utiliserai l’image ubuntu:bionic. Commençons par enregistrer l’archive dans votre système de fichiers local :

docker save --output ubuntu-docker.tar ubuntu:bionic

La distribution bionic comporte trois couches d’image, que nous allons examiner de plus près.

Après avoir décompressé l’archive tar, nous pouvons en examiner le contenu :

[ubuntu-docker] ll                                                                                                               
total 24
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 46076a325f0de3f745254638b8b0f0de343685b34e7ca6ec5cd0b6b7930eb7fa
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 468327b5cd7ce539db695bd0ef05dae8a4ff77b02870a8e823ed74dedad4bd55
-rw-r--r--  1 agatakrajewska  staff   3.3K 25 Sep 23:33 56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json
drwxr-xr-x  5 agatakrajewska  staff   160B 25 Sep 23:33 8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3
-rw-r--r--  1 agatakrajewska  staff   356B  1 Jan  1970 manifest.json
-rw-r--r--  1 agatakrajewska  staff    89B  1 Jan  1970 repositories

Examinons le fichier manifest.json :

[ubuntu-docker] cat manifest.json | jq                                                                                           
[
  {
    "Config": "56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json",
    "RepoTags": [
      "ubuntu:bionic"
    ],
    "Layers": [
      "8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3/layer.tar",
      "468327b5cd7ce539db695bd0ef05dae8a4ff77b02870a8e823ed74dedad4bd55/layer.tar",
      "46076a325f0de3f745254638b8b0f0de343685b34e7ca6ec5cd0b6b7930eb7fa/layer.tar"
    ]
  }
]

Ce fichier nous renvoie à la configuration du conteneur, où nous trouvons des informations très utiles, comme l’architecture, la configuration, les couches du système de fichiers racine, etc. Du point de vue de Snyk, l’élément le plus intéressant est sans doute la propriété Layers : elle pointe vers les couches réelles de l’image, qui ne sont que des répertoires dans l’archive. Si nous accédons à l’un de ces répertoires, nous pouvons voir ce qu’il contient :

[8bf067b107a6f7444876e33c6ed85652355f679ac98ebab97ab3ebad63f0dff3] ll                                                            
total 128160
-rw-r--r--  1 agatakrajewska  staff     3B 25 Sep 23:33 VERSION
-rw-r--r--  1 agatakrajewska  staff   401B 25 Sep 23:33 json
-rw-r--r--  1 agatakrajewska  staff    63M 25 Sep 23:33 layer.tar

Allons un peu plus loin et décompressons le fichier layer.tar :

[layer] ll                                                                                                                       
total 0
drwxr-xr-x  87 agatakrajewska  staff   2.7K 21 Sep 18:17 bin
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 boot
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:17 dev
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 etc
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 home
drwxr-xr-x   8 agatakrajewska  staff   256B 23 May  2017 lib
drwxr-xr-x   3 agatakrajewska  staff    96B 21 Sep 18:16 lib64
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 media
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 mnt
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 opt
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 proc
drwx------   4 agatakrajewska  staff   128B 21 Sep 18:17 root
drwxr-xr-x   5 agatakrajewska  staff   160B 21 Sep 18:14 run
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 sbin
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 srv
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 sys
drwxrwxrwt   2 agatakrajewska  staff    64B 21 Sep 18:17 tmp
drwxr-xr-x  10 agatakrajewska  staff   320B 21 Sep 18:14 usr
drwxr-xr-x  13 agatakrajewska  staff   416B 21 Sep 18:17 var

Comme vous pouvez le voir ci-dessus, le fichier layer.tar contient simplement les modifications apportées au système de fichiers pour la couche d’image, avec toutes les dépendances et autres binaires, selon le contenu de l’image. Après avoir extrait et analysé le contenu

de ces couches, nous pouvons vous présenter la liste des chemins vulnérables.

Testons maintenant l’image d’archive Docker enregistrée avec snyk test. Pour tester une archive locale, indiquez le préfixe docker-archive: :

snyk container test docker-archive:ubuntu-docker.tar ou analysez l’image depuis votre dépôt Docker local :

snyk container test ubuntu:bionic

Les deux commandes produiront exactement le même résultat :

Testing ubuntu:bionic...

✗ Low severity vulnerability found in tar
  Description: Loop with Unreachable Exit Condition ('Infinite Loop')
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-312298>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

✗ Low severity vulnerability found in tar
  Description: NULL Pointer Dereference
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-559435>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

# vuln list continues in here
...

Organization:      example-org
Package manager:   deb
Project name:      docker-image|ubuntu
Docker image:      ubuntu:bionic
Licenses:          enabled

Tested 90 dependencies for known issues, found 31 issues.

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

Image OCI

Au cours des derniers mois, nous avons également ajouté l’analyse des archives OCI aux fonctionnalités de l’interface de ligne de commande Snyk.

Examinons maintenant la spécification pour voir ce que contient l’archive ! Pour cette section, j’utiliserai également ubuntu:bionic.

Si vous souhaitez examiner vous-même une archive OCI au format tar, vous pouvez exécuter la commande suivante :

skopeo copy --override-os linux docker://ubuntu:bionic oci-archive:ubuntu.tar

J’utilise Skopeo ici pour enregistrer l’image. Cet outil cli facilite grandement diverses opérations sur les images.

Pour enregistrer une image ubuntu au format OCI, il suffit d’indiquer le préfixe oci-archive: pour la sortie tar.

Remarquez que j’utilise également l’option --override-os : c’est parce que je suis sur macOS et que les images Ubuntu de Docker Hub officielles ne sont disponibles que pour Linux. Nous parlerons également plus bas des images compatibles avec plusieurs architectures.

Une fois l’archive tar décompressée, nous pouvons en examiner le contenu :

[ubuntu] ll                                                                                                                      
total 16
drwxr-xr-x  3 agatakrajewska  staff    96B 28 Oct 11:18 blobs
-rw-r--r--  1 agatakrajewska  staff   186B 28 Oct 11:18 index.json
-rw-r--r--  1 agatakrajewska  staff    31B 28 Oct 11:18 oci-layout

Un fichier index.json intéressant se trouve à la racine. Examinons son contenu :

[ubuntu] cat index.json | jq                                                                                                     
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "digest": "sha256:afa93a8ce255ca452ca8c88f4b5c821a466cf0a3e0148a31d0d97dfdb91d9aef",
      "size": 658
    }
  ]
}

Il s’agit de notre manifeste de plus haut niveau (facultatif), qui nous oriente vers des manifestes d’image spécifiques. Il contient des informations sur un ensemble d’images pouvant couvrir plusieurs architectures et systèmes d’exploitation. Pour en savoir plus, consultez la spécification complète.

Examinons maintenant le manifeste propre à notre image, vers lequel index.json nous a orientés, dans le répertoire blobs/sha256 :

[sha256] cat afa93a8ce255ca452ca8c88f4b5c821a466cf0a3e0148a31d0d97dfdb91d9aef | jq                                               
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:33a51d09088285451e7a7525d4bd64fc15563264afe5a91ef84a8b3042018899",
    "size": 2426
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:171857c49d0f5e2ebf623e6cb36a8bcad585ed0c2aa99c87a055df034c1e5848",
      "size": 26701612
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:419640447d267f068d2f84a093cb13a56ce77e130877f5b8bdb4294f4a90a84f",
      "size": 852
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:61e52f862619ab016d3bcfbd78e5c7aaaa1989b4c295e6dbcacddd2d7b93e1f5",
      "size": 162
    }
  ]
}

Nous commençons à voir des éléments intéressants, comme la configuration du conteneur, qui contient de nombreuses informations utiles sur l’architecture de l’image, les couches de l’image de base, la configuration, etc. Nous disposons également des informations sur les couches de l’image, qui pointent vers les couches disponibles dans notre image ubuntu:bionic.

Comme vous pouvez le voir ci-dessous, le contenu de la première couche est exactement le même que celui de l’archive Docker présentée plus haut.

[171857c49d0f5e2ebf623e6cb36a8bcad585ed0c2aa99c87a055df034c1e5848] ll                                                            
total 0
drwxr-xr-x  87 agatakrajewska  staff   2.7K 21 Sep 18:17 bin
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 boot
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:17 dev
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 etc
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 home
drwxr-xr-x   8 agatakrajewska  staff   256B 23 May  2017 lib
drwxr-xr-x   3 agatakrajewska  staff    96B 21 Sep 18:16 lib64
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 media
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 mnt
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 opt
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 proc
drwx------   4 agatakrajewska  staff   128B 21 Sep 18:17 root
drwxr-xr-x   5 agatakrajewska  staff   160B 21 Sep 18:14 run
drwxr-xr-x  68 agatakrajewska  staff   2.1K 21 Sep 18:17 sbin
drwxr-xr-x   2 agatakrajewska  staff    64B 21 Sep 18:14 srv
drwxr-xr-x   2 agatakrajewska  staff    64B 24 Apr  2018 sys
drwxrwxrwt   2 agatakrajewska  staff    64B 21 Sep 18:17 tmp
drwxr-xr-x  10 agatakrajewska  staff   320B 21 Sep 18:14 usr
drwxr-xr-x  13 agatakrajewska  staff   416B 21 Sep 18:17 var

Vous pouvez analyser votre archive OCI locale en exécutant :

snyk container test oci-archive:ubuntu.tar

Différentes plateformes et architectures

En examinant les manifestes d’archive Docker et d’image OCI, nous avons remarqué qu’ils contenaient tous deux des informations sur l’architecture. Revenons au contenu du manifeste de configuration de notre conteneur, dans le répertoire ubuntu-docker, où nous avons enregistré notre docker archive :

[sha256] cat 56def654ec22f857f480cdcc640c474e2f84d4be2e549a9d16eaba3f397596e9.json | jq
{
  ...
  "architecture": "amd64",
  "os": "linux",
  ...
}

Parmi les autres informations, nous voyons l’architecture et le système d’exploitation, qui sont dans mon cas amd64 et linux. Qu’en est-il si notre image repose sur arm64 ou si nous utilisons la plateforme windows ?

Bonne nouvelle : Snyk prend également en charge d’autres plateformes. Il suffit de transmettre l’option -—platform à l’interface de ligne de commande Snyk.

Nous avons parlé du format d’archive docker save. Examinons maintenant le format v2 dans lequel les images sont stockées sur Docker Hub, qui recèle des aspects très intéressants. Il s’agit de la deuxième version du schéma v2, créée pour répondre à deux objectifs principaux. Selon la documentation Docker :

Le premier objectif est de permettre les images multi-architectures grâce à un « manifeste universel » qui référence les manifestes d’image des versions conçues pour des plateformes spécifiques. Le second est de faire évoluer le moteur Docker vers des images adressables par leur contenu, en prenant en charge un modèle d’image où la configuration de l’image peut être hachée pour générer un identifiant.

Avec notre instance Docker locale, nous pouvons d’abord examiner le « manifeste universel » de l’image qui nous intéresse afin de vérifier quelles variantes de plateforme sont disponibles. Comme expliqué ci-dessus, cela signifie qu’un même dépôt peut héberger plusieurs images pour différentes architectures.

docker manifest inspect ubuntu:bionic

Nous obtenons ainsi un tableau de manifestes :

{
   ...
  "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:45c6f8f1b2fe15adaa72305616d69a6cd641169bc8b16886756919e7c01fa48b",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:e80b8affb2361dc632c1fa8fcbf6b6514f750eb6ef99b7e7f825a55f849bfd89",
         "platform": {
            "architecture": "arm",
            "os": "linux",
            "variant": "v7"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 943,
         "digest": "sha256:01a2038b20d165ab7df81934f9849bdfbc59bd6f6322c5d11e341504f66ec266",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "v8"
         }
      },
...

Maintenant que nous avons établi que l’image ubuntu existe pour différentes plateformes, voyons comment analyser l’image ubuntu pour l’architecture de plateforme arm64.

Assurez-vous que les fonctionnalités expérimentales sont activées dans Docker sur votre hôte, puis exécutez :

snyk container test --platform=linux/arm64 ubuntu:bionic

Examinons le résultat :

Testing ubuntu:bionic...

✗ Low severity vulnerability found in tar
  Description: Loop with Unreachable Exit Condition ('Infinite Loop')
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-312298>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

✗ Low severity vulnerability found in tar
  Description: NULL Pointer Dereference
  Info: <https://snyk.io/vuln/SNYK-UBUNTU1804-TAR-559435>
  Introduced through: tar@1.29b-2ubuntu0.1, pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2
  From: tar@1.29b-2ubuntu0.1
  From: pam/libpam-runtime@1.1.8-3.6ubuntu2.18.04.2 > debconf@1.5.66ubuntu1 > perl/perl-base@5.26.1-6ubuntu0.3 > dpkg@1.19.0.5ubuntu2.3 > tar@1.29b-2ubuntu0.1

# vuln list continues in here
...

Organization:      example-org
Package manager:   apk
Project name:      docker-image|ubuntu:bionic
Docker image:      alpine:3.12
Platform:          linux/arm64
Licenses:          enabled

Tested 90 dependencies for known issues, found 31 issues.

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

Nous pouvons voir tous les chemins vulnérables détectés, ainsi que les informations de « Platform » dans le résultat de notre analyse.

Dans cet article, nous avons exploré les deux principaux formats d’image de conteneur, Docker et OCI, et montré comment Snyk peut interagir avec les deux, y compris sur plusieurs architectures.

Pour conclure, découvrez les bonnes pratiques de sécurité pour créer des images Docker optimales destinées aux applications Node.js et Java :

  1. 10 bonnes pratiques de sécurité pour Docker — découvrez les pratiques de sécurité à suivre lors de la création d’images Docker de base et de leur téléchargement, ainsi que la confiance dans le contenu Docker.

  2. Vous développez en Java ? Cette ressource vous sera utile : Docker pour les développeurs Java : 5 choses à savoir pour ne pas compromettre votre sécurité

  3. 10 bonnes pratiques pour conteneuriser des applications web Node.js avec Docker - Si vous développez en Node.js, vous allez adorer ce guide détaillé qui vous montre comment créer des images Docker de base sécurisées et performantes pour vos applications Node.js.

Toutes ces fonctionnalités sont disponibles gratuitement dans Snyk — inscrivez-vous pour créer un compte.

Lancez-vous dans les compétitions Capture The Flag

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

Publié dans:

Lire la suite

Blog

Stadium Summer : la tournée Fan Zone de Snyk Connect

La tournée Fan Zone de Snyk a proposé des ateliers sur la sécurité de l’IA, des occasions de réseauter et une compétition amicale dans 8 villes et lors de 3 sessions virtuelles. Les participants ont développé leurs compétences, échangé des idées et progressé ensemble.

Blog

Snyk VulnBench JS 1.0 : les LLM peuvent-ils détecter deux fois les mêmes bugs ?

Snyk VulnBench JS 1.0 : 300 analyses répétées montrent que les résultats de sécurité des LLM varient d’une exécution à l’autre, tandis que SAST et modèles détectent différentes vulnérabilités.

blog feature toolkit
Blog

Publication malveillante du paquet elementary-data sur PyPI : des identifiants cloud dérobés à des ingénieurs data

Des attaquants ont exploité une vulnérabilité d’injection de script dans GitHub Actions pour publier une version malveillante de l’interface de ligne de commande Python elementary-data (v0.23.3). Elle intégrait une porte dérobée volant des identifiants, qui ciblait les profils dbt, les clés des fournisseurs cloud et les secrets SSH dans les environnements d’ingénierie des données.