Introduction aux risques et à la sécurisation de Docker (1e partie)
Le principe de conteneurisation n’est pas nouveau. Il est utilisé pour répondre à une forte demande de productivité et de réduction des coûts d’infrastructure en suivant le courant du « tout est service » : PaaS, IaaS, SaaS, et maintenant Container as a Service.
Dans le monde UNIX, le principe de conteneurisation n’est pas nouveau. Docker, ainsi que d’autres acteurs du marché, utilisent ce principe pour répondre à une forte demande de productivité et de réduction des coûts d’infrastructure en suivant le courant du « tout est service » : PaaS, IaaS, SaaS, et maintenant Container as a Service.
Docker fourni un service de virtualisation d’application basée sur des fonctionnalités noyau unitairement matures du système d’exploitation Linux, et qui, orchestrées et interfacées à d’autres composants, constitue la solution « Docker Engine ». Docker se distingue des solutions de virtualisation de système complet type Vmware ou HyperV et intervient uniquement sur la couche applicative en embarquant les librairies et les binaires nécessaires à l’application dans un conteneur. Le poids d’un conteneur est nettement inférieur à celui d’une machine virtuelle, ce qui incite énormément d’entreprises à réfléchir à la conteneurisation de leurs applications afin d’économiser principalement sur le coût de stockage, mais également pour améliorer le retour sur investissement (ROI), le rendement d’une machine physique, les évolutions de version (retour arrière/mise à jour) et réduire le temps de mise en production ou le « Time-To-Market ».
D’après un sondage de mi-2016, les équipes les plus motivées sur ce phénomène sont la direction générale et la direction informatique qui a elles deux représentent les 2/3 de la population sondée. 43% des entreprises ressentent un manque de maturité pour envisager à un réel changement immédiat, mais 71% y réfléchissent sérieusement pour les prochaines années. Les principales craintes sont la perte de gouvernance liée au changement des modes de travail pour s’orienter vers un rapprochement des équipes de développement et des opérationnels tout en conciliant les problématiques de sécurité (SecDevOps) et les problèmes liés à la protection des données hébergées par ce principe de conteneurisation.
Créé en 2013, Docker a développé la solution sans prioriser les contraintes de sécurité qui deviennent maintenant incontournables et intégrées dans le cycle de vie des projets. Docker se doit aujourd’hui de répondre à ces attentes de sécurité et cela conditionne également sa propre survie dans un monde « hyperconcurrentiel ».
Plusieurs bons points sont à noter depuis 2016. Docker répond aux exigences de ses utilisateurs et propose régulièrement des correctifs de sécurité. La communauté Docker reste ouverte, active et s’agrandit, ce qui permet de challenger le code source de l’application (sous licence apache2) et d’en sortir de nouvelles vulnérabilités et améliorations et, dernièrement les récentes fonctionnalités de gestion de conteneur à grande échelle (comme Swarm ou les réseaux Overlay) intègrent nativement ces aspects de sécurité.
Le système alternatif à Docker, Rocket (Rkt) par CoreOs, propose un durcissement natif du système ainsi que des fonctionnalités de signature et chiffrement des images et conteneurs embarqués dans la solution.
Plus de 125 fournisseurs de conteneur sont actuellement actifs, alors qu’ils étaient 30, il y a trois ans. Le phénomène de consolidation est également en cours. Apprenda a acquis Kismatic en mai 2016 et Cisco a acheté ContainerX en août de la même année.
Enfin tout récemment, en mars 2017, le cœur de Docker a été confié à la Cloud Native Computing Foundation (CNCF). Portée par la fondation Linux, la CNCF regroupe un peu plus de 70 membres parmi lesquels Cisco, CoreOs, Fujitsu, Google, Huawei, IBM, Intel, Joyent, Mesosphere, Red Hat, Samsung, NetApp, Apigee, Canonical, eBay, Juniper Network, Twitter, Nec ou encore VMware.
Selon sa charte, la CNCF assure l’intendance des projets, favorise la croissance et l’évolution de l’écosystème, assure la promotion des technologies et des applications (événements, marketing direct, formation, certifications…) et fait en sorte que la technologie soit accessible et fiable. Parmi les projets qu’elle soutient, on trouve bien entendu Kubernetes mais aussi CoreDNS et l’outil de monitoring du cloud Prometheus.
En termes d’usages, la technologie Docker est de plus en plus utilisée : développement, tests, déploiements en production, DevOps, assurance qualité, etc.
Comparaison avec la virtualisation
Automatiser les tâches de déploiement des infrastructures et des applications nécessite de comprendre la différence entre les deux sujets : infrastructure et application. L’infrastructure fournit les ressources matérielles et réseaux aux différents systèmes d’exploitation déployés sur des serveurs qui vont héberger des applications et des services nécessaires à l’entreprise : commerce, communication, marketing, recrutement. Notre travail est maintenant complètement dépendant de l’informatique et d’Internet. D’après L’INSEE, les ¾ de nos emplois appartiennent au secteur tertiaire.
Afin de répondre à une demande toujours plus croissante de services dans un contexte d’économie générale, le principe de virtualisation s’applique désormais à l’infrastructure (système d’exploitation) et aux applications et services. Certains spécialistes de l’hébergement de services proposent un modèle de licence à la « minute de marche » (uptime), à l’octet réseau, etc.
La virtualisation des composants matériels permet de créer plusieurs instances de système Windows ou Linux, appelées « machines virtuelles » sur la même machine hôte appelé hyperviseur, tout en conservant l’isolation du système par le biais d’un noyau unique à chaque machine virtuelle. La conteneurisation se distingue par son concept qui mutualise le noyau du système hôte aux différents conteneurs, vus comme des processus système gérés grâce aux mécanismes natifs de l’hôte. Ces conteneurs contiennent le minimum nécessaire pour que l’application fonctionne (binaires et librairies). Ce processus est appelé conteneur et celui qui le gère est appelé démon.
Il y a donc une différence technique majeure entre la virtualisation de système d’exploitation et l’isolation des processus applicatifs appelée conteneurisation. La virtualisation de système s’appuie sur un hyperviseur où chaque machine virtuelle doit posséder son propre système d’exploitation. Alors que la conteneurisation s’appuie sur un noyau commun pour l’ensemble des processus et chaque processus appelle ce noyau et utilise ses propres binaires et librairies.
Plusieurs paramètres de configuration viennent cloisonner les conteneurs entre eux, car les interrogations quant à la sécurité des conteneurs et du système hôte apparaissent assez naturellement.
Finalement, chaque application conteneurisée devient un service que l’on appelle aujourd’hui « microservice », lui-même hébergé sur des infrastructures de plus en plus automatisées telles que le PaaS ou le IaaS.
Fonctionnement de Docker
Docker s’installe sur un serveur Linux ou Windows connecté au réseau et accessoirement au service de stockage externe de l’entreprise. Le démon Docker (dockerd) gère les conteneurs et est exécuté avec l’utilisateur « root » sur le système.
Un conteneur est un processus qui utilise un système de base appelé image, contenant un minimum de librairies et de binaires nécessaires à son fonctionnement. Celles-ci peuvent être téléchargées chez Docker sur sa plateforme Docker Hub ou depuis un emplacement interne.
Une image correspond à une application ou un système de base que l’administrateur peut adapter à son besoin. Par exemple, une nouvelle plateforme de cassage de mot de passe doit être disponible par un navigateur web, un ou plusieurs nouveaux conteneurs pourraient être déployés en s’appuyant sur des images du Docker Hub (Frontal web, application de crack et base de données). Le contenu d’une image peut être personnalisé pour satisfaire l’ensemble des besoins : interface Ldap, mise à jour, fichiers de configuration. Ces étapes peuvent être automatisées grâce à un simple fichier appelé le DockerFile ou grâce à des outils plus lourds d’implémentation.
Le démon Docker est accessible par une API qui permet de passer les commandes de création, de lancement et conteneur ou de téléchargement d’images, voire de montage de volume.
L’image initiale est ensuite instanciée à l’aide d’une commande passée au démon à travers l’API existante, ce qui crée un nouveau processus qui sera le conteneur. Les données modifiées dans ce conteneur sont stockées dans un tampon géré par un système de fichier de type Copy And Write comme UnionFs ou dans le cas de Docker AuFs. Les données ne sont donc pas modifiées dans l’image, mais dans la couche d’abstraction des données du conteneur. Donc, une image peut servir plusieurs conteneurs différents ayant leur base identique.
Le démon Docker embarque des mécanismes importants tels que le load-balancing, la gestion d’un DNS et de l’adressage IP.
Dans la plupart des cas, un conteneur devra être accessible depuis l’extérieur par le réseau. Docker offre la possibilité d’exposer un service vers l’extérieur grâce à un système de NAT automatiquement géré par le démon dans Iptables. Il existe aussi la fonctionnalité de réseau étendu MacVlan (avec support 802.1q) pour intégrer directement le conteneur dans l’infrastructure réseau extérieur.
L’isolation système entre les conteneurs s’appuie sur des principes de Namespace natifs au système d’exploitation et de filtrage des appels système au noyau.