⚠️ Traduction non officielle - Cette documentation est une traduction communautaire non officielle de Docker.

Isoler les conteneurs avec un namespace utilisateur

Les namespaces Linux fournissent une isolation pour les processus en cours d'exécution, limitant leur accès aux ressources système sans que le processus en cours d'exécution soit conscient des limitations. Pour plus d'informations sur les namespaces Linux, voir Namespaces Linux.

Le meilleur moyen de prévenir les attaques d'escalade de privilèges depuis l'intérieur d'un conteneur est de configurer les applications de votre conteneur pour qu'elles s'exécutent en tant qu'utilisateurs non privilégiés. Pour les conteneurs dont les processus doivent s'exécuter en tant qu'utilisateur root dans le conteneur, vous pouvez remapper cet utilisateur vers un utilisateur moins privilégié sur l'hôte Docker. L'utilisateur mappé se voit attribuer une plage d'UIDs qui fonctionnent dans le namespace comme des UIDs normaux de 0 à 65536, mais n'ont aucun privilège sur la machine hôte elle-même.

À propos du remappage et des IDs d'utilisateur et de groupe subordonnés

Le remappage lui-même est géré par deux fichiers : /etc/subuid et /etc/subgid. Chaque fichier fonctionne de la même manière, mais l'un concerne la plage d'ID utilisateur, et l'autre la plage d'ID de groupe. Considérez l'entrée suivante dans /etc/subuid :

testuser:231072:65536

Cela signifie que testuser se voit attribuer une plage d'ID utilisateur subordonné de 231072 et les 65536 entiers suivants en séquence. L'UID 231072 est mappé dans le namespace (dans le conteneur, dans ce cas) comme UID 0 (root). L'UID 231073 est mappé comme UID 1, et ainsi de suite. Si un processus tente d'escalader les privilèges en dehors du namespace, le processus s'exécute comme un UID de nombre élevé non privilégié sur l'hôte, qui ne mappe même pas vers un utilisateur réel. Cela signifie que le processus n'a aucun privilège sur le système hôte du tout.

Note

Il est possible d'attribuer plusieurs plages subordonnées pour un utilisateur ou groupe donné en ajoutant plusieurs mappages non chevauchants pour le même utilisateur ou groupe dans les fichiers /etc/subuid ou /etc/subgid. Dans ce cas, Docker n'utilise que les cinq premiers mappages, conformément à la limitation du noyau de seulement cinq entrées dans /proc/self/uid_map et /proc/self/gid_map.

Lorsque vous configurez Docker pour utiliser la fonctionnalité userns-remap, vous pouvez optionnellement spécifier un utilisateur et/ou groupe existant, ou vous pouvez spécifier default. Si vous spécifiez default, un utilisateur et groupe dockremap est créé et utilisé à cette fin.

Warning

Certaines distributions n'ajoutent pas automatiquement le nouveau groupe aux fichiers /etc/subuid et /etc/subgid. Si c'est le cas, vous devrez peut-être modifier manuellement ces fichiers et attribuer des plages non chevauchantes. Cette étape est couverte dans Prérequis.

Il est très important que les plages ne se chevauchent pas, afin qu'un processus ne puisse pas obtenir l'accès dans un namespace différent. Sur la plupart des distributions Linux, les utilitaires système gèrent les plages pour vous lorsque vous ajoutez ou supprimez des utilisateurs.

Ce remappage est transparent pour le conteneur, mais introduit une certaine complexité de configuration dans les situations où le conteneur a besoin d'accéder aux ressources sur l'hôte Docker, telles que les montages de liaison dans des zones du système de fichiers que l'utilisateur système ne peut pas écrire. Du point de vue de la sécurité, il est préférable d'éviter ces situations.

Prérequis

  1. Les plages UID et GID subordonnées doivent être associées à un utilisateur existant, même si l'association est un détail d'implémentation. L'utilisateur possède les répertoires de stockage namespacés sous /var/lib/docker/. Si vous ne voulez pas utiliser un utilisateur existant, Docker peut en créer un pour vous et l'utiliser. Si vous voulez utiliser un nom d'utilisateur ou ID utilisateur existant, il doit déjà exister. Typiquement, cela signifie que les entrées pertinentes doivent être dans /etc/passwd et /etc/group, mais si vous utilisez un back-end d'authentification différent, cette exigence peut se traduire différemment.

    Pour vérifier cela, utilisez la commande id :

    $ id testuser
    
    uid=1001(testuser) gid=1001(testuser) groups=1001(testuser)
    
  2. La façon dont le remappage de namespace est géré sur l'hôte utilise deux fichiers, /etc/subuid et /etc/subgid. Ces fichiers sont typiquement gérés automatiquement lorsque vous ajoutez ou supprimez des utilisateurs ou groupes, mais sur certaines distributions, vous devrez peut-être gérer ces fichiers manuellement.

    Chaque fichier contient trois champs : le nom d'utilisateur ou ID de l'utilisateur, suivi d'un UID ou GID de début (qui est traité comme UID ou GID 0 dans le namespace) et un nombre maximum d'UIDs ou GIDs disponibles pour l'utilisateur. Par exemple, étant donné l'entrée suivante :

    testuser:231072:65536

    Cela signifie que les processus namespacés utilisateur démarrés par testuser sont possédés par l'UID hôte 231072 (qui ressemble à l'UID 0 à l'intérieur du namespace) jusqu'à 296607 (231072 + 65536 - 1). Ces plages ne devraient pas se chevaucher, pour s'assurer que les processus namespacés ne peuvent pas accéder aux namespaces les uns des autres.

    Après avoir ajouté votre utilisateur, vérifiez /etc/subuid et /etc/subgid pour voir si votre utilisateur a une entrée dans chacun. Sinon, vous devez l'ajouter, en faisant attention à éviter le chevauchement.

    Si vous voulez utiliser l'utilisateur dockremap créé automatiquement par Docker, vérifiez l'entrée dockremap dans ces fichiers après avoir configuré et redémarré Docker.

  3. S'il y a des emplacements sur l'hôte Docker où l'utilisateur non privilégié doit écrire, ajustez les permissions de ces emplacements en conséquence. Ceci est également vrai si vous voulez utiliser l'utilisateur dockremap créé automatiquement par Docker, mais vous ne pouvez pas modifier les permissions jusqu'après avoir configuré et redémarré Docker.

  4. L'activation de userns-remap masque effectivement les couches d'image et de conteneur existantes, ainsi que d'autres objets Docker dans /var/lib/docker/. C'est parce que Docker doit ajuster la propriété de ces ressources et les stocke en fait dans un sous-répertoire dans /var/lib/docker/. Il est préférable d'activer cette fonctionnalité sur une nouvelle installation Docker plutôt que sur une existante.

    Dans la même ligne, si vous désactivez userns-remap vous ne pouvez pas accéder à aucune des ressources créées pendant qu'elle était activée.

  5. Vérifiez les limitations sur les namespaces utilisateur pour vous assurer que votre cas d'usage est possible.

Activer userns-remap sur le démon

Vous pouvez démarrer dockerd avec le flag --userns-remap ou suivre cette procédure pour configurer le démon en utilisant le fichier de configuration daemon.json. La méthode daemon.json est recommandée. Si vous utilisez le flag, utilisez la commande suivante comme modèle :

$ dockerd --userns-remap="testuser:testuser"
  1. Modifiez /etc/docker/daemon.json. En supposant que le fichier était précédemment vide, l' entrée suivante active userns-remap en utilisant un utilisateur et groupe appelé testuser. Vous pouvez adresser l'utilisateur et le groupe par ID ou nom. Vous n'avez besoin de spécifier le nom ou ID de groupe que s'il est différent du nom ou ID utilisateur. Si vous fournissez à la fois le nom ou ID utilisateur et groupe, séparez-les par un caractère deux-points (:). Les formats suivants fonctionnent tous pour la valeur, en supposant que l'UID et GID de testusersont1001` :

    • testuser
    • testuser:testuser
    • 1001
    • 1001:1001
    • testuser:1001
    • 1001:testuser
    {
      "userns-remap": "testuser"
    }
    Note

    Pour utiliser l'utilisateur dockremap et laisser Docker le créer pour vous, définissez la valeur à default plutôt que testuser.

    Sauvegardez le fichier et redémarrez Docker.

  2. Si vous utilisez l'utilisateur dockremap, vérifiez que Docker l'a créé en utilisant la commande id.

    $ id dockremap
    
    uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)
    

    Vérifiez que l'entrée a été ajoutée à /etc/subuid et /etc/subgid :

    $ grep dockremap /etc/subuid
    
    dockremap:231072:65536
    
    $ grep dockremap /etc/subgid
    
    dockremap:231072:65536
    

    Si ces entrées ne sont pas présentes, modifiez les fichiers en tant qu'utilisateur root et attribuez un UID et GID de départ qui est le plus élevé attribué plus le décalage (dans ce cas, 65536). Faites attention à ne pas permettre de chevauchement dans les plages.

  3. Vérifiez que les images précédentes ne sont pas disponibles en utilisant la commande docker image ls. La sortie devrait être vide.

  4. Démarrez un conteneur à partir de l'image hello-world.

    $ docker run hello-world
    
  5. Vérifiez qu'un répertoire namespacé existe dans /var/lib/docker/ nommé avec l'UID et GID de l'utilisateur namespacé, possédé par cet UID et GID, et non lisible par le groupe ou le monde. Certains des sous-répertoires sont encore possédés par root et ont des permissions différentes.

    $ sudo ls -ld /var/lib/docker/231072.231072/
    
    drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/
    
    $ sudo ls -l /var/lib/docker/231072.231072/
    
    total 14
    drwx------ 5 231072 231072 5 Jun 21 21:19 aufs
    drwx------ 3 231072 231072 3 Jun 21 21:21 containers
    drwx------ 3 root   root   3 Jun 21 21:19 image
    drwxr-x--- 3 root   root   3 Jun 21 21:19 network
    drwx------ 4 root   root   4 Jun 21 21:19 plugins
    drwx------ 2 root   root   2 Jun 21 21:19 swarm
    drwx------ 2 231072 231072 2 Jun 21 21:21 tmp
    drwx------ 2 root   root   2 Jun 21 21:19 trust
    drwx------ 2 231072 231072 3 Jun 21 21:19 volumes
    

    Votre liste de répertoires peut avoir quelques différences, surtout si vous utilisez un pilote de stockage de conteneur différent de aufs.

    Les répertoires qui sont possédés par l'utilisateur remappé sont utilisés à la place des mêmes répertoires directement sous /var/lib/docker/ et les versions inutilisées (comme /var/lib/docker/tmp/ dans l'exemple ici) peuvent être supprimées. Docker ne les utilise pas tant que userns-remap est activé.

Désactiver le remappage de namespace pour un conteneur

Si vous activez les namespaces utilisateur sur le démon, tous les conteneurs sont démarrés avec les namespaces utilisateur activés par défaut. Dans certaines situations, comme les conteneurs privilégiés, vous pourriez avoir besoin de désactiver les namespaces utilisateur pour un conteneur spécifique. Voir limitations connues des namespaces utilisateur pour certaines de ces limitations.

Pour désactiver les namespaces utilisateur pour un conteneur spécifique, ajoutez le flag --userns=host à la commande docker container create, docker container run, ou docker container exec.

Il y a un effet de bord lors de l'utilisation de ce flag : le remappage utilisateur ne sera pas activé pour ce conteneur mais, parce que les couches (image) en lecture seule sont partagées entre les conteneurs, la propriété du système de fichiers du conteneur sera toujours remappée.

Ce que cela signifie est que tout le système de fichiers du conteneur appartiendra à l'utilisateur spécifié dans la configuration de démon --userns-remap (231072 dans l'exemple ci-dessus). Cela peut conduire à un comportement inattendu des programmes à l'intérieur du conteneur. Par exemple sudo (qui vérifie que ses binaires appartiennent à l'utilisateur 0) ou les binaires avec un flag setuid.

Limitations connues des namespaces utilisateur

Les fonctionnalités Docker standard suivantes sont incompatibles avec l'exécution d'un démon Docker avec les namespaces utilisateur activés :

  • Partage des namespaces PID ou NET avec l'hôte (--pid=host ou --network=host).
  • Pilotes externes (volume ou stockage) qui ne sont pas conscients ou incapables d'utiliser les mappages utilisateur du démon.
  • Utilisation du flag de mode --privileged sur docker run sans aussi spécifier --userns=host.

Les namespaces utilisateur sont une fonctionnalité avancée et nécessitent une coordination avec d'autres capacités. Par exemple, si des volumes sont montés depuis l'hôte, la propriété des fichiers doit être pré-arrangée si vous avez besoin d'un accès en lecture ou écriture au contenu du volume.

Bien que l'utilisateur root à l'intérieur d'un processus de conteneur namespacé utilisateur ait beaucoup des privilèges attendus du superutilisateur dans le conteneur, le noyau Linux impose des restrictions basées sur la connaissance interne que c'est un processus namespacé utilisateur. Une restriction notable est l'incapacité d'utiliser la commande mknod. La permission est refusée pour la création de périphérique dans le conteneur lorsqu'il est exécuté par l'utilisateur root.