Chiffrer son GNU/Linux, avec LVM et LUKS

lock

Vous avez vu un peu toutes les déclarations sur l’espionnage de masse toussa toussa ? (Non, ce n’est pas Lord cette fois :D) Hé bien nous allons apprendre à chiffrer vos partitions, pour y stocker votre système d’exploitation et vos données.

Euh, mais attends, “pourquoi ?” d’abord s’il te plaît

Petit rappel : le chiffrement de vos données permet de “protéger” vos données sur deux types d’action :

  • la confidentialité, puisqu’un autre utilisateur ne saura pas à quoi correspond ces données chiffrées,
  • l’intégrité, puisqu’un fichier ne pourra pas être modifié discrètement sans casser le chiffrement entièrement.

Remarque ÜBER importante : tel que démontré par de nombreux papiers, l’intégrité (“malleability” en anglais) n’est pas assurée avec certains méthodes d’enchaînements, comme CBC (Cipher-Block Chaining). N’utilisez plus CBC, utilisez à la place XTS (ciphertext stealing), tel que disponible avec cryptsetup (méthode qui est devenue par défaut), car même sans être infaillible, il permet de “mitiger” le problème (une véritable solution serait de signer les données, mais ceci est beaucoup plus gourmand en ressources mémoire et calculs).

Enfin, comme vous l’avez peut-être remarqué, j’ai mis entre guillemets le mot “protéger”, car il faut être conscient qu’avec les moyens à disposition de nos jours (puissance de calcul, failles et erreurs humaines), aucun chiffrement n’est inviolable (à deux-trois détails près). Et souvent, la faille, c’est vous.

Bien, maintenant que nous savons pourquoi, entrons dans les détails.

 

Let’s go!

Dans ce tutoriel, le but est de pouvoir chiffrer le contenu de notre disque dur ; le plus simple est d’utiliser un conteneur LVM pour les données et de chiffrer le tout avec un conteneur LUKS.
Par conséquent, il n’y a qu’un seul chiffrement/déchiffrement, ce qui est bien plus simple.

Au cours de ce tutoriel, nous allons voir plusieurs cas de figures concernant le déchiffrement de notre système, lors du démarrage :

  1. soit vous avez un accès physique à la machine avec clavier et vous pouvez y brancher un support externe, comme une clef USB
  2. soit vous n’avez qu’un accès distant (comme un serveur par exemple), et là la clef devra se trouver sur le même support mémoire que votre partition.

Tout de suite, on peut remarquer que le cas de figure 2 est beaucoup moins sécurisé que le cas de figure 1. Vu que nous n’avons pas d’accès physique, pour y brancher tout autre support mémoire et apporter notre clef secrète ainsi que notre noyau, il faut donc malheureusement créer une partition qui contiendra donc notre clef et notre noyau, et surtout qui ne sera pas chiffrée. Le seul avantage de ce cas de figure est de pouvoir démarrer le système automatiquement, et de garder en sécurité le système (relativement) en supprimant la clef.

Remarque : soyez-en bien conscient, la faille est béante ; ce système n’est pas là pour protéger vous et vos données, vu que mettre sur le même support les données chiffrées et la clef n’est pas très malin, mais il est pour unique but de faire perdre du temps, mais très peu.

Maintenant que nous avons compris quelques détails, commençons par préparer le support.

 

Préparation du support

Avec votre outil préféré, il faut « découper en partitions » votre support (le mot “partitionner” n’existe pas). Si vous êtes dans le cas de figure 1, faites une unique partition (ou selon vos souhaits).

Par contre, dans le cas de figure 2, il faudra créer au minimum une (petite) partition d’amorçage – /boot – et une autre partition pour les données chiffrées. Pour /boot, une taille de 64 Mio est ce qui est de plus correct, pas trop grande, et surtout, pas trop petite. Si vous faites une partition trop petite et venez à manquer de place dedans, vous devrez refaire un partitionnement, et donc déplacer vos fichiers temporairement, ce qui est long et fastidieux, alors évitez de vous faire perdre votre propre temps quitte à mettre 128 Mio. Ainsi, pour créer des partitions selon le cas de figure 2, vous pouvez suivre ces commandes :

gdisk /dev/sda

  • Entrez “o” pour créer une nouvelle table GPT,
  • ensuite “n” pour une nouvelle partition (en GPT, on peut avoir 128 partitions … là, on ne va en avoir besoin que de deux, pour le cas de figure n°2),
  • suivant notre exemple, pour la partition /boot, on rentre “64M” pour “Last Sector” (les autres par défaut),
  • pour le conteneur LVM, là, on va prendre toute la place, donc entrez tout par défaut,
  • avant de valider, vérifiez le résultat en entrant “p”, nombre de partition, et leur taille ; pour info, tant que vous n’avez pas entré “w” pour valider, les changements ne sont pas enregistrés, et vous pouvez en tout sécurité quitter avec “q” ou repartir de zéro avec “o”, aucune donnée ne sera écrite ou supprimée.
  • allez, pour nous, c’est bon, faisons “w” (confirmez si vous avez un message de confirmation).

Note : pour éviter les répétitions dans le tutoriel, et uniquement si vous suivez le cas de figure n°2, remplacez de votre propre initiative /dev/sda1 par /dev/sda2. Je rappelle que ce cas de figure est déconseillé, mais nécessaire dans certains cas.

Remarque : évitez au maximum de donner des infos sur le contenu de votre disque, donc, pas de label comme « cypher h4ck3r », et concernant le type, “Linux filesystem” par défaut fonctionne très bien.

Vérifions en lançant ls /dev/sd* pour voir si le nombre de partitions correspond (de la forme /dev/sdXY, avec X une lettre, et Y un chiffre). Si oui, continuons.

Voilà pour le partitionnement, comme sur n’importe quelle distribution GNU/Linux, continuons avec le vif du sujet !

 

Ze vif ofe ze sujet : le conteneur LUKS

Pour avoir un conteneur LUKS (Linux Unified Key Setup), il faut une passphrase, c’est comme un mot de passe, mais en plus long… Cette passphrase, nous allons la stocker dans un fichier chiffré pour la “protéger” (concernant le cas de figure n°2, je détaillerai plus bas le changement).

Avant de continuer, la question suivante pourrait se détacher : « pourquoi utiliser une clef chiffrée pour déchiffrer le conteneur LUKS, plutôt que d’utiliser la passphrase directement ? »

Voici quelques arguments ;

  • LUKS étant suffisamment robuste peut résister à de nombreuses attaques par brute-forces et d’attaques par “rainbow tables” (des listes de couples mots de passe/hash), avec une passphrase de 20 caractères. Mais avec une clef plus grande, nous augmentons significativement la difficulté.
  • Autre argument, c’est le fait que cette clef chiffrée peut être jetable, ou remplaçable. Une personne « malveillante » (par exemple, le chat qui voudrait se rincer la pupille en ton absence) peut déterminer le passphrase pour déchiffrer la clef, mais ce ne sera pas assez pour déchiffrer le conteneur LUKS, vu que c’est la passphrase intermédiaire. Aussi, il suffit de se débarrasser de cette clef, pour ne plus être capable de les déchiffrer… (je ne vais pas détailler plus en détails, car ce n’est pas sa place, mais il ne s’agit pas d’une 2FA).

Cette commande peut paraître assez longue lors de son exécution, car il faut avoir généré de l’entropie pour permet au « générateur de nombres pseudo-aléatoires » (PRNG) d’avoir les données les plus « aléatoires » possible (à ne pas confondre avec un « générateur de probabilités infinies », qui lui requiert des coordonnées).

Prenons en une quantité de 255 octets.
head -c 255 /dev/random | base64 -w0 -- | tr -d '\\n' | gpg --symmetric --armor -- > clef_luks.gpg

Remarque : pour éviter des soucis avec les pipes |, la commande tr -d '\\n' permet de supprimer les éventuels retours à la ligne qui pourraient nous poser problème.

Autre remarque : le choix entre /dev/random et son homologue /dev/urandom n’a ici que peu d’importance, suivant l’importance que vous lui accordez.

Créons maintenant notre conteneur LUKS, avec luksFormat
gpg --decrypt clef_luks.gpg | cryptsetup --key-file - --cipher serpent-xts-essiv:sha256 luksFormat /dev/sda1

Note : Si quelque chose n’a pas fonctionné, vous pouvez rajouter l’option --verbose, juste après cryptsetup.

Comme vous pouvez le remarquer, nous n’utilisons pas le chiffrement AES selon l’algorithme Rijndael, mais celui de l’algorithme Serpent, qui est aussi réputé car arrivé en deuxième place, après Rijndael.

Félicitations, vous venez de créer votre conteneur LUKS. 🙂

Montons le tout de suite dans le système, comme ceci :
gpg --decrypt clef_luks.gpg | cryptsetup --key-file - open --type luks /dev/sda1 vador

Remarque : en dessous de la version 1.6, si vous rencontrez l’erreur --type: unknown option, remplacez la nouvelle syntaxe open --type luks par luksOpen.

Nous voilà donc avec le périphérique LUKS monté dans /dev/mapper/vador
Note : le nom “vador” importe peu, vous pouvez lui donner le nom que vous souhaitez.
Note : je suis sûr que vous avez compris la référence “Luks/Vador”, non ? … Non ? Tant pis.

Considérez le comme une partition vide, donc actuellement nous ne pouvons pas y stocker de données directement ; et puis nous sommes limité à une seule partition, donc un seul système de fichiers, or nous en voulons plusieurs.

Voilà donc qu’entre en jeu le conteneur LVM (Logical Volume Manager).

 

Le conteneur LVM

Nous allons donc créer des “groupes de volumes” (VG), avec les différentes commandes que voici (adaptez bien sûr à vos besoins)

Initialiser et créer le groupe de volume (on peut se permettre de ne pas exécuter pvcreate, comme l’indique la documentation) :
vgcreate lvm-name /dev/mapper/vador

créer les “partitions” à l’intérieur du volume :
lvcreate --name root --size 20G lvm-name
lvcreate --name swap --size 2G lvm-name
lvcreate --name var --size 20G lvm-name
lvcreate --name home --extents 100%FREE lvm-name

Oui, pour la dernière, nous n’utilisons pas --size, mais --extends 100%FREE pour prendre toute la place restante.

Encore une fois, félicitations ! Pour faire un parallèle avec une installation sans chiffrement, c’est comme si vous venez à peine de créer les partitions. Mais vous voilà avec un système chiffré, ce n’est pas rien. Admirez le résultat (ainsi que la taille concrètement allouée à home) avec la commande lvdisplay lvm-name cette commande nous permet ainsi de savoir où nos volumes LVM sont situés, en suivant pour chacun des volumes la ligne “LV Path”, ce qui revient à /dev/<VG name>/<LV Name> (soit /dev/lvm-name/home pour notre volume home).

À cette étape, vous pouvez très bien reprendre le processus d’installation sans devoir refaire vos partitions (ce ne serait vraiment pas pratique autrement), après avoir déchiffré le conteneur LUKS, en exécutant vgchange --available y lvm-name ou si vous ne vous connaissez pas le nom vgscan && vgchange --available y ce qui vous permet de reprendre à cette étape et de poursuivre avec la création des systèmes de fichiers.

 

Les systèmes de fichiers

Après avoir créer nos “partitions” avec LVM, il est temps d’y mettre un système de fichiers, pour pouvoir y stocker les données ; par contre, ne vous trompez, comme déjà fait remarqué précédemment, nos “partitions” sont dans /dev/lvm-name/, et non pas /dev/sda comme l’indique de base le handbook Gentoo.

Commençons par créer la swap – car ça s’oublie facilement – et de l’activer si nos sommes sur un liveUSB/CD :

mkswap --label swap /dev/lvm-name/swap
# Si nous sommes dans un liveUSB/CD et que nous souhaitons entrer dans un “chroot”
swapon /dev/lvm-name/swap

Et puis, les systèmes de fichiers : me concernant, je vais utiliser le système de fichiers XFS.
mkfs.xfs -L root /dev/lvm-name/root
mkfs.xfs -L var /dev/lvm-name/var
mkfs.xfs -L home /dev/lvm-name/home

On oublie pas de monter les systèmes de fichiers (oui, car dans la RAM, ça ne sert à rien …)

mkdir /mnt/gentoo/
mount /dev/lvm-name/root /mnt/gentoo
mkdir /mnt/gentoo/{boot,home,var}
touch /mnt/gentoo/{boot,home,var}/.keep

Remarque : la commande touch est facultative, mais nous a permit de créer des fichiers vides dans les dossiers boot, home et var, tous nommés “.keep” (donc cachés) ; ces fichiers ont pour unique rôle d’éviter de voir les dossiers concernés supprimés de / et d’empêcher le montage des partitions à leur emplacement. Une petite sécurité pour valider le démarrage, surtout quand on ne peut pas voir la séquence de démarrage.

Note : nous nous occuperons de notre support de démarrage plus tard.

Nous y voilà, notre hiérarchie de fichiers est en place.

Et maintenant ?

Et maintenant, c’est “très simple”, vous pouvez reprendre normalement l’installation de votre système d’exploitation GNU/Linux préféré. Maintenant, suivez le handbook ou tout autre document et reprenez un peu plus tard.

Une fois le système de base installé et le noyau presque prêt, reprenons ensemble.
Nous allons nous créer un système de base, qui va pouvoir permettre de déchiffrer le conteneur LUKS et monter le système de fichiers, avec l’aide de genkernel.

 

J’ai deux amours, le noyau et Genkernel

Commencez par configurer votre kernel, avec “make menuconfig”. Importante chose, mettez en dur au moins les systèmes de fichiers de /boot et de / (respectivement ext2 et xfs dans notre exemple).

Maintenant, faisons appel à genkernel et voyons sa magie agir avec la commande suivante :

Remarque : faites attention à certaines options, comme --no-mrproper, qui empêche de supprimer les fichiers temporaires, et donc de perdre votre fichier .config :

genkernel --clean --no-mrproper --no-install --no-symlink --kernel-config=/usr/src/linux/.config --disklabel --lvm --luks --gpg --all-ramdisk-modules all

Comme vous pouvez le voir, nous rajoutons le support LVM, LUKS et optionnellement GPG, si votre clef est chiffré symétrique. Si vous utilisez du RAID, rajoutez --mdadm (voir genkernel(8)).

Une fois la compilation terminée, trois fichiers ont été créés dans /var/tmp/genkernel/, nommés “kernel-genkernel-x86_64-<version>-<name>”, “initramfs-<version>-<name>” et “System.map-genkernel-x86_64-<version>-<name>”. Mais avant de les copier dans /boot, reprenons ce dernier.

Nous avions mis de côté notre support de stockage, il est temps de le reprendre. Pour être démarrable, il faut par contre une simple partition et un simple système de fichiers. C’est là aussi que les deux cas de figure cités en début de tutoriel diffèrent :

  1. Dans le cas de figure n°1, il vous faut donc un support externe, telle qu’une clef USB, ou équivalent (les cartes SD fonctionnent aussi) :
    • déterminez après l’avoir branchée (*hmmhmm*) sa “position” dans /dev. Dans notre cas de figure, prenons /dev/sdb,
    • comme pour notre support mémoire système, utilisez fdisk /dev/sdb pour créer une partition dédiée à cette tâche ; dans notre exemple, ce sera /dev/sdb1,
    • ensuite, créons un système de fichiers de type ext2, avec la commande mkfs.ext2 -m0 -c /dev/sdb1 (Conseil, avec un FS en ext2, ça prendra du temps, du coup, prévoyez pas trop grand, comme indiqué en début d’article),
    • pour finir, montez ce nouveau système dans /boot comme ceci mount /dev/sdb1 /boot et voilà.
  2. Dans le cas de figure n°2, nous devons stocker la clef sur le même support ; or rappelez-vous, nous l’avons déjà fait (trop génial ce thican). On va donc simplement :
    • créer un système de fichiers mkfs.ext2 -m0 -c /dev/sda1
    • puis monter le nouveau système dans son emplacement mount /dev/sda1 /boot et c’est tout.

Remarque : ne copiez pas bêtement les commandes non plus, car si vous formattez avec un FS la partition qui sert de conteneur LUKS, “bravo”, vous gagnez le droit de tout recommencer.

Ensuite, copiez les fichiers créés par genkernel ainsi que votre fichier clef_luks.gpg dans la partition de démarrage (/boot), et maintenant, occupons nous du bootloader.

 

Le bootloader, pour un démarrage tout en douce(u)r

Le bootloader est un logiciel, de petite taille, qui se charge d’exécuter les scripts et programmes de base permettant au système de démarrer. D’expérience, je vous aurais conseillé extlinux, mais je n’ai pas réussi à le faire fonctionner à distance, donc nous allons prendre LILO, qui fonctionne.

Note : Si vous avez une idée du soucis, n’hésitez pas à m’écrire pour me donner quelques détails. 😉

Créez donc votre fichier /etc/lilo.conf avec ces informations, sans toutefois oublier d’adapter les noms de fichiers, bien sûr, mais aussi la variable “boot=” avec /dev/sdb (selon notre exemple) pour un support externe, ou /dev/sda pour un système distant :

# /etc/lilo.conf

# Should work for most systems, and do not have the sector limit:
lba32
# If lba32 do not work, use linear:
#linear

# MBR to install LILO to: (BE CAREFUL)
boot=/dev/sdb
map=/boot/.map
timeout=0
default=gentoo

image=/boot/kernel-genkernel-x86_64--
label=gentoo
read-only # read-only for checking
initrd=/boot/initramfs--
append="doluks crypt_root=/dev/sda1 root_keydev=/dev/sdb1 root_key=/boot/clef_luks.gpg dolvm lvmraid=lvm-name real_root=/dev/lvm-name/root rootfstype=xfs"

Remarque : Si vous êtes sur une machine distante, tel un serveur, il sera tout aussi difficile d’y taper un mot de passe au moment du démarrage comme le fait d’y brancher un support externe. Pour cela, et comme je l’avais mentionné, on va donc déchiffrer la clef, pour la stocker en clair avec le kernel et l’initramfs. Reprenons en premier notre clef, pour la déchiffrer et stocker ses données en clair dans un autre fichier :

# Plaçons-nous dans le dossier /boot du système de fichier, en dehors du chroot
cd /mnt/gentoo/boot
# Déchiffrons la clef et stockons les données dans un autre fichier. Le mot de passe vous sera demandé.
gpg --decrypt clef_luks.gpg > clef_luks.txt

Une fois ceci de fait, il faut remodifier le fichier /etc/lilo.conf pour indiquer d’utiliser ce nouveau fichier plutôt que la clef chiffrée. Pour celà, modifiez le contenu de la variable root_key=/boot/clef_luks.gpg en root_key=/boot/clef_luks.txt.

Comme vous pouvez le voir, rien de bien compliqué. Une fois le fichier /etc/lilo.conf correct et écrit, exécutez simplement la commande lilo et … et c’est tout !

Si si, pour de vrai, vous avez fini ! Redémarrez simplement, ou fermez vous-même les systèmes de fichiers (pour apprendre, par exemple) :
umount -l /mnt/gentoo/{home,boot,var,}
swapoff /dev/lvm-name/swap
vgchange -a n lvm-name
crytpsetup luksClose vador
reboot

Je vous souhaite bonne continuation pour le reste, et merci à vous d’avoir suivi ce tutoriel.

Vus : 498
Publié par Geekfault : 45