Conteneurs Linux vs VM: une comparaison de sécurité

Les développeurs adorent les conteneurs. Ils sont faciles à utiliser et rapides à démarrer. Vous pouvez en exécuter beaucoup sur du matériel même simple. La surcharge de démarrage a toujours été un fléau de développement et de test, et cette surcharge ne fait qu'augmenter avec les architectures de microservices. Si un développeur a besoin d'une demi-douzaine de services, il peut facilement perdre un jour ou deux avec la configuration - configuration du matériel, exécution des installateurs, lutte contre les incompatibilités.

Avec les conteneurs, cela se réduit à quelques minutes ou secondes et peut être exécuté sur un poste de travail de développement. Les référentiels facilement disponibles d'images de conteneurs utiles multiplient la productivité des développeurs, tout comme le fait l'open source, mais sans la peine de faire une construction. Les équipes opérationnelles ont été plus lentes à adopter les conteneurs. L'une des raisons est que de nombreuses applications qu'ils doivent prendre en charge ne sont pas encore conteneurisées. Une autre raison est la réticence à s'éloigner des VM.

Pour les opérations, le passage du bare metal aux machines virtuelles était naturel. Les VM individuelles ressemblent et peuvent être gérées comme des systèmes individuels, en utilisant les mêmes outils et processus. Les premières préoccupations concernant la sécurité des VM ont été apaisées par la longue histoire de production des VM dans le monde mainframe, par la possibilité d'appliquer les mêmes contrôles que ceux utilisés pour les systèmes bare-metal, par la prise en charge de la virtualisation matérielle et par la maturité évolutive des outils de gestion des VM.

De nombreux premiers soucis de sécurité se résumaient à une question: les machines virtuelles étaient-elles aussi sécurisées que le bare metal? Aujourd'hui, des questions similaires se posent à propos des conteneurs. Dans quelle mesure les conteneurs sont-ils sécurisés et comment se comparent-ils aux VM? Certes, si nous comparons les services exécutés dans des conteneurs aux mêmes services exécutés en tant que processus distincts sur le même système, la version du conteneur est plus sécurisée. La séparation fournie par les espaces de noms Linux et les groupes de contrôle fournit des barrières qui n'existent pas entre les processus simples. La comparaison avec les VM est moins claire. Jetons un coup d'œil aux VM et aux conteneurs du point de vue de la sécurité.

Dans cet article, je vais adopter deux approches différentes pour comparer la sécurité des VM et des conteneurs. La première approche sera plus structurelle, ou théorique, examinant les caractéristiques de chacun dans une perspective de sécurité. Ensuite, j'appliquerai une analyse plus pratique en examinant ce qui se passe dans une violation typique et comment elle peut être affectée par les architectures de conteneur et de VM.

Vue structurelle

Pour l'approche structurelle, je vais comparer la surface d'attaque des deux systèmes. Une surface d'attaque représente le nombre de points auxquels un système peut être attaqué. Il n'est pas défini avec précision (comme un nombre, par exemple) mais est utile pour les comparaisons. Pour un cambrioleur, une maison à 10 portes a une plus grande surface d'attaque qu'une maison à une porte, même si les portes sont identiques. Une porte peut être laissée déverrouillée; une serrure peut être défectueuse; des portes situées à différents endroits peuvent offrir à un intrus plus d'intimité, etc.

Dans les systèmes informatiques, la surface d'attaque comprend tout ce qui permet à l'attaquant (ou au logiciel agissant en son nom) de «toucher» le système cible. Les interfaces réseau, les connexions matérielles et les ressources partagées sont tous des points d'attaque possibles. Notez que la surface d'attaque n'implique pas qu'une vulnérabilité réelle existe. Les 10 portes peuvent être parfaitement sécurisées. Mais une plus grande surface d'attaque signifie plus d'endroits à protéger et plus il est probable que l'attaquant trouve une faiblesse dans au moins un.

La surface d'attaque totale dépend du nombre de points de contact différents et de la complexité de chacun. Regardons un exemple simple. Imaginez un système à l'ancienne qui sert des cotations boursières. Il a une seule interface, une simple ligne série. Le protocole sur cette ligne est également simple: un symbole boursier de longueur fixe, disons cinq caractères, est envoyé au serveur, qui répond par une cotation de prix de longueur fixe - disons 10 caractères. Il n'y a pas d'Ethernet, TCP / IP, HTTP, etc. (J'ai en fait travaillé sur de tels systèmes il y a longtemps dans une galaxie lointaine, très lointaine.)

La surface d'attaque de ce système est très petite. L'attaquant peut manipuler les caractéristiques électriques de la ligne série, envoyer des symboles incorrects, envoyer trop de données ou faire varier le protocole d'une autre manière. La protection du système impliquerait la mise en œuvre de contrôles appropriés contre ces attaques.

Imaginez maintenant le même service, mais dans une architecture moderne. Le service est disponible sur Internet et expose une API RESTful. Le côté électrique de l'attaque a disparu - tout ce qui va faire est de faire frire le routeur ou le commutateur de l'attaquant. Mais le protocole est énormément plus complexe. Il a des couches pour IP, TCP, éventuellement TLS et HTTP, chacune offrant la possibilité d'une vulnérabilité exploitable. Le système moderne a une surface d'attaque beaucoup plus grande, bien qu'il ressemble toujours à l'attaquant comme un point d'interface unique.

Surface d'attaque en métal nu

Pour un attaquant non physiquement présent dans le centre de données, la surface d'attaque initiale est le réseau dans le serveur. Cela a conduit à la «vue périmétrique» de la sécurité: protégez les points d'entrée dans le centre de données et rien n'y entre. Si l'attaquant ne peut pas entrer, peu importe ce qui se passe entre les systèmes à l'intérieur. Cela fonctionnait bien lorsque les interfaces de périmètre étaient simples (pensez à accès commuté), mais encourageait des faiblesses sur les interfaces internes. Les attaquants qui trouvaient un trou dans le périmètre découvraient souvent que la surface d'attaque interne de la batterie de serveurs était beaucoup plus grande que celle externe et qu'ils pouvaient faire des dégâts considérables une fois à l'intérieur.

Cette surface d'attaque interne comprenait des connexions réseau entre les serveurs, mais aussi des interactions de processus à processus au sein d'un seul serveur. Pire encore, étant donné que de nombreux services fonctionnent avec des privilèges élevés (utilisateur «root»), réussir à en entrer un signifierait effectivement un accès sans entrave à tout autre élément de ce système, sans avoir à rechercher des vulnérabilités supplémentaires. Toute une industrie s'est développée autour de la protection des serveurs - pare-feu, anti-programme malveillant, détection d'intrusion, etc. - avec des résultats loin d'être parfaits.

Il existe également d'intéressantes attaques «side channel» contre les serveurs. Les chercheurs ont montré des exemples d'utilisation de la consommation d'énergie, du bruit ou du rayonnement électromagnétique des ordinateurs pour extraire des informations, parfois des données très sensibles telles que des clés cryptographiques. D'autres attaques ont exploité des interfaces exposées comme les protocoles de clavier sans fil. En général, cependant, ces attaques sont plus difficiles - elles peuvent nécessiter la proximité du serveur, par exemple - de sorte que le chemin principal pour «descendre le fil» est plus courant.

Surface d'attaque VM

Lorsque les VM sont utilisées de la même manière que le bare metal, sans aucune différence dans l'architecture de l'application (comme elles le sont souvent), elles partagent la plupart des mêmes points d'attaque. Une surface d'attaque supplémentaire est l'échec potentiel de l'hyperviseur, du système d'exploitation ou du matériel pour isoler correctement les ressources entre les machines virtuelles, permettant à une machine virtuelle de lire d'une manière ou d'une autre la mémoire d'une autre machine virtuelle. L'interface entre la VM et l'hyperviseur représente également un point d'attaque. Si une machine virtuelle peut percer et exécuter du code arbitraire dans l'hyperviseur, elle peut accéder à d'autres machines virtuelles sur le même système. L'hyperviseur lui-même représente un point d'attaque car il expose les interfaces de gestion.

Il existe des points d'attaque supplémentaires en fonction du type de système VM. Les systèmes VM de type 2 utilisent un hyperviseur s'exécutant en tant que processus sur un système d'exploitation hôte sous-jacent. Ces systèmes peuvent être attaqués en attaquant le système d'exploitation hôte. Si l'attaquant peut faire exécuter du code sur le système hôte, il peut potentiellement affecter l'hyperviseur et les VM, en particulier s'il peut y accéder en tant qu'utilisateur privilégié. La présence d'un système d'exploitation entier, y compris des utilitaires, des outils de gestion et éventuellement d'autres services et points d'entrée (tels que SSH), fournit un certain nombre de points d'attaque possibles. Les systèmes VM de type 1, où l'hyperviseur s'exécute directement sur le matériel sous-jacent, éliminent ces points d'entrée et ont donc une surface d'attaque plus petite.

Surface d'attaque du conteneur

Comme pour les machines virtuelles, les conteneurs partagent les points d'attaque d'entrée de réseau fondamentaux des systèmes bare-metal. De plus, comme les machines virtuelles de type 2, les systèmes de conteneurs qui utilisent un système d'exploitation hôte «entièrement chargé» sont soumis à toutes les mêmes attaques disponibles contre les utilitaires et services de ce système d'exploitation hôte. Si l'attaquant peut accéder à cet hôte, il peut essayer d'accéder ou affecter d'une autre manière les conteneurs en cours d'exécution. S'il obtient un accès privilégié («root»), l'attaquant pourra accéder ou contrôler n'importe quel conteneur. Un système d'exploitation «minimaliste» (tel que KurmaOS d'Apcera) peut aider à réduire cette surface d'attaque mais ne peut pas l'éliminer entièrement, car un certain accès au système d'exploitation hôte est nécessaire pour la gestion des conteneurs.

Les mécanismes de base de séparation des conteneurs (espaces de noms) offrent également des points d'attaque potentiels. De plus, tous les aspects des processus sur les systèmes Linux n'ont pas d'espace de noms, donc certains éléments sont partagés entre les conteneurs. Ce sont des zones naturelles que les attaquants doivent sonder. Enfin, le processus vers l'interface du noyau (pour les appels système) est volumineux et exposé dans chaque conteneur, par opposition à l'interface beaucoup plus petite entre une VM et l'hyperviseur. Les vulnérabilités dans les appels système peuvent offrir un accès potentiel au noyau. Un exemple de ceci est la vulnérabilité récemment signalée dans le trousseau de clés Linux.

Considérations architecturales

Pour les machines virtuelles et les conteneurs, la taille de la surface d'attaque peut être affectée par l'architecture de l'application et la manière dont la technologie est utilisée.

De nombreuses applications de VM héritées traitent les VM comme du bare metal. En d'autres termes, ils n'ont pas adapté leurs architectures spécifiquement pour les machines virtuelles ou pour les modèles de sécurité non basés sur la sécurité périmétrique. Ils peuvent installer de nombreux services sur la même machine virtuelle, exécuter les services avec des privilèges root et avoir peu ou pas de contrôles de sécurité entre les services. La réarchitecture de ces applications (ou plus probablement leur remplacement par de nouvelles) pourrait utiliser des machines virtuelles pour fournir une séparation de sécurité entre les unités fonctionnelles, plutôt que simplement comme un moyen de gérer un plus grand nombre de machines.

Les conteneurs sont bien adaptés aux architectures de microservices qui «enchaînent» un grand nombre de petits services (généralement) à l'aide d'API standardisées. Ces services ont souvent une durée de vie très courte, lorsqu'un service conteneurisé est démarré à la demande, répond à une demande et est détruit, ou lorsque les services augmentent et diminuent rapidement en fonction de la demande. Ce modèle d'utilisation dépend de l'instanciation rapide prise en charge par les conteneurs. Du point de vue de la sécurité, il présente à la fois des avantages et des inconvénients.

Le plus grand nombre de services signifie un plus grand nombre d'interfaces réseau et donc une plus grande surface d'attaque. Cependant, il permet également davantage de contrôles au niveau de la couche réseau. Par exemple, dans la plate-forme Apcera, tout le trafic de conteneur à conteneur doit être explicitement autorisé. Un conteneur non autorisé ne peut pas atteindre de manière arbitraire un point de terminaison du réseau.

La courte durée de vie du conteneur signifie que si un attaquant entre, le temps dont il dispose pour faire quelque chose est limité, par opposition à la fenêtre d'opportunité offerte par un service de longue durée. L'inconvénient est que la criminalistique est plus difficile. Une fois que le conteneur est parti, il ne peut pas être sondé et examiné pour trouver le malware. Ces architectures rendent également plus difficile pour un attaquant d'installer des logiciels malveillants qui survivent après la destruction du conteneur, comme il le ferait sur du métal nu en installant un pilote qui se charge au démarrage. Les conteneurs sont généralement chargés à partir d'un référentiel de confiance en lecture seule, et ils peuvent être davantage sécurisés par des contrôles cryptographiques.

Voyons maintenant ce qui se passe lors d'une violation.

Protection contre les violations

Les attaquants ont généralement un ou deux objectifs en pénétrant dans un système serveur. Ils veulent obtenir des données ou faire des dégâts.

S'ils recherchent des données, ils veulent infiltrer autant de systèmes que possible, avec les privilèges les plus élevés possible, et maintenir cet accès aussi longtemps que possible. Cela leur donne le temps de trouver les données, qui peuvent déjà être là - une base de données mal sécurisée, par exemple - ou qui peuvent nécessiter une collecte lente au fil du temps au fur et à mesure qu'elles pénètrent, comme la collecte de transactions à mesure qu'elles arrivent des utilisateurs. Maintenir l'accès pendant une longue période nécessite la discrétion. L'attaque nécessite également un moyen de récupérer les données.

Si l'attaquant tente simplement de faire des dégâts, le but est à nouveau d'accéder à autant de systèmes et de privilèges que possible. Mais il y a un équilibre: une fois que les dommages commencent, ils seront vraisemblablement remarqués, mais plus l'attaquant attend longtemps pour démarrer (pendant que le malware filtre d'un système à l'autre), plus il a de chances d'être détecté. La sortie des données est moins importante que le contrôle coordonné du malware. L'idée est d'infecter autant de systèmes que possible, puis de les endommager en un point synchronisé, soit à l'avance, soit sur commande.

Les violations impliquent un certain nombre d'éléments. Examinons chacun d'eux et voyons si les machines virtuelles et les architectures conteneurisées peuvent affecter la surface d'attaque de chacun.