Archives de catégorie : Machines virtuelles

Sécurité des conteneurs

Le technologie des conteneurs est en plein expansion ces jours-ci. Parmi les plus connus, il y a Docker qui est à la mode dans la mouvance DevOps, et qui permet de gains de productivité et d’agilité incontestables. Toutefois, la technologie n’est pas forcément mature pour les grosses entreprises et l’industrialisation à grande échelle, en termes de stabilité et de sécurité. Certains éditeurs comme aquasec proposent des solutions pour aider à la sécurisation de ce type d’architecture.

Fondements de l’architecture Docker

Docker est un système de conteneurs, qui permet de faire tourner sur un même OS plusieurs applications en parallèle et de façon relativement isolée.

Les conteneurs Docker s’appuient sur l’OS et la virtualisation (l’isolation) ne concerne que le système de fichiers, les configurations et les zones d’état du système (comme la base de registres sous Windows par exemple)1. Notez bien : il n’y a pas d’isolation au niveau de la mémoire de travail (RAM).

Si cela représente d’indéniables avantages sur l’urbanisation et l’optimisation des ressources, cela implique que sur une machine donnée, tous les conteneurs doivent être basés sur le même kernel. Ainsi, si on peut faire tourner différents Linux sur une machine hôte Linux, il est impossible de faire tourner un conteneur Windows sur un hôte Linux, et réciproquement : in fine, le conteneur appellera toujours le noyau de l’hôte.

Un exemple de solution : Aqua

Leur approche repose sur un outil, Aqua, qui met l’accent sur 5 axes2 permettant d’inclure la sécurité dans le cycle de vie des DevOps :

  • Gestion des vulnérabilités dans les images (des containers) ;
  • Réduction de la surface d’attaque du container ;
  • Renforcement du Contrôle d’Accès Utilisateur ;
  • Durcissement de la machine hôte ;
  • Automatisation de l’ensemble du process.

Gestion des vulnérabilités

Comme toute architecture fortement automatisée se basant sur des modèles, la sécurisation des modèles servant de base aux équipes de développement est une nécessité incontournable. Cependant, avec Docker, le rythme de modification des images de base augmente considérablement, ce qui va demander une validation quasi continue des images, et donc l’appréciation continue du niveau de sécurité des images utilisées.

Réduction de la surface d’attaque

Cette approche est classique pour tout environnement informatique, mais la sécurisation de conteneur est largement moins mature que celle de systèmes Linux, par exemple.

De plus, la communication entre conteneurs doit faire aussi l’objet de toutes les attentions : il est impératif de minimiser les communications entre conteneurs, en les rationalisant (pas d’ouverture de ports incontrôlés ni de droits trop ouverts sur l’OS), ce qui relève le plus souvent d’une démarche des développeurs et architectes.

Renforcement du contrôle d’accès des utilisateurs

Les premières versions de Docker imposaient l’utilisation du niveau root pour fonctionner, sans alternative possible. Cela ne pouvait pas perdurer si Docker voulait passer les portes des entreprises…

Les versions récentes sont beaucoup plus adaptées et permettent de gérer plus finement les droits, avec tous les gains associés en terme de ségrégation des rôles et de traçabilité. Il est incontournable de gérer alors ces droits de façon centralisée.

Durcissement de la machine hôte

Il s’agit principalement de s’assurer de l’isolation effective des conteneurs, ce qui est réalisé au niveau de l’OS. Or il faut que cette étape soit outillée et vérifiable, car comme plusieurs conteneurs peuvent s’exécuter sur le même OS, toute erreur de configuration peut conduire à des débordements !

Automatisation

L’automatisation est la raison d’être du DevOps, permettant l’agilité et la souplesse. L’automatisation de l’ensemble des processus doit inclure la sécurité, laquelle doit donc elle aussi être outillée et automatisée, afin que les DevOps et que les professionnels de la sécurité y trouvent leur compte.

Au niveau de Docker

Du fait que Docker s’appuie sur le système lui-même, les risques de débordement de ressources d’un conteneur à l’autre sont présents. Docker en a bien conscience, et ajoute des briques partout où c’est possible, mais le choix de la technologie LXC de Linux puis libcontainer3 laissera toujours planer le risque de lecture et/ou d’écriture par un conteneur cannibale (concurrent)4. Même avec des environnements de virtualisation ultra-rodés côté sécurité, le risque d’évasion de machine virtuelle est à considérer ; alors avec les conteneurs de type Docker, dont l’isolation est moins forte, ce risque est donc important et à prendre en compte lors que l’utilisation de ce type de technologie. 

Les risques

Image compromise

Le premier risque est d’utiliser une image contenant des composantes malveillantes. Avec l’explosion du minage sauvage de cryptomonnaies depuis 2017, toutes les techniques sont bonnes pour faire de l’argent, et incorporer un logiciel de minage dans des images couramment utilisées peut se révéler très lucratif5 pour les petits malins.

Autres sources

OpenVZ (Sauvegarde)

La sauvegarde d’une machine virtuelle OpenVZ se fait très simplement, en étant administrateur (root) de la machine.

Préparation de la partition

Pour minimiser le downtime (le temps où le serveur est indisponible), il faut utiliser un paramètre spécial de sauvegarde (cf script ci-dessous) lequel nécessite un partionnement particulier.

En effet, il faut que les machines virtuelles soient créées sur une partition de type LVM mais également il est nécessaire qu’une partie de cette partition reste disponible pour le processus de sauvegarde. Si le partionnement est généralement réalisé à l’installation, il faudra quelques commandes pour libérer de la place sur la partition LVM (par défaut, elle est montée sur /var/lib/vz.

Paramétrage du partitionnement pour une distribution de type PROXMOX

root@host:~# /etc/init.d/vz stop (arrêt du service de virtualisation) 
root@host:~# df -h (pour voir l'allocation de la partition LVM)
Sys. de fich. Tail. Occ. Disp. %Occ. Monté sur
/dev/sda1 12G 886M 11G 8% /
tmpfs 1000M 0 1000M 0% /lib/init/rw
udev 10M 2,6M 7,5M 26% /dev
tmpfs 1000M 0 1000M 0% /dev/shm
/dev/mapper/pve-lv1 74G 952M 69G 2% /var/lib/vz (on voit que la partition fait 74 Go).

root@host:~# umount /var/lib/vz (on démonte la partition)
root@host:~# e2fsck -f /dev/mapper/pve-lv1 (on vérifie son état, pour ne rien abimer)

root@host:~# resize2fs /dev/mapper/pve-lv1 73G (on diminue la taille de 1 ou 2 Go, ce qui est généralement suffisant)
root@host:~# lvresize --size -2G /dev/mapper/pve-lv1 (on libère 2 Go qui pourra être utilisé par le processus snapshop)

root@host:~# e2fsck -f /dev/mapper/pve-lv1 (on re-vérifie la partition, après ces manipulations)
root@host:~# resize2fs /dev/mapper/pve-lv1 (on met en conformité la partition et le système de fichiers)
root@host:~# mount /dev/mapper/pve-lv1 /var/lib/vz (on remonte la partition)
root@host:~# /etc/init.d/vz start (on redémarre le service de virtualisation)

Script de sauvegarde

Le seul paramètre important est le numéro de machine virtuelle. La première porte souvent l’identifiant 101, c’est ce que j’ai utilisé dans mon exemple.

L’option --snapshot permet de minimiser le temps d’indisponibilité de la machine virtuelle.

#!/bin/sh# 
#
# Svg machine virtuelle
nohup vzdump --snapshot --compress--dumpdir /svg --tmpdir /tmp 101
#
NOM_FICH1=vzdump-101.$(date +%Y%m%d)-$1.tgz
cp /svg/vzdump-101.tgz /répertoire_sauvegarde/$NOM_FICH1

Script ou commande de restauration

vzrestore <fichier_de_sauvegarde> 101