Archives pour l'étiquette ovh

Faire tourner QEMU en rescue sur un serveur Kimsufi KS-2

Il s’agit ici de faire tourner QEMU dans le mode rescue d’un serveur dédié Kimsufi, en l’occurrence un KS-2, et démarrer sur le disque dur du serveur.

Utiliser QEMU sur un Kimsufi peut avoir plusieurs intérêts, par exemple installer soi-même un OS via QEMU directement sur le disque dur, plutôt que d’utiliser les installations fournies par OVH. Sur les forums Kimsufi, on trouve un exemple avec une installation de Windows.

Une autre utilisation de QEMU est de démarrer depuis le disque dur pour débuguer certains problèmes dans la séquence de démarrage du serveur avec une connexion VNC,  vu qu’on n’a pas de clavier+écran. C’est ce cas de figure qui m’a intéressé. Mon problème était que, suite à l’installation maison d’une distribution sur mon Kimsufi, la machine était bloquée quelque part dans le boot, avant que je puisse avoir une connexion SSH, sans que je puisse savoir où.

Le KS-2 tourne sur un CPU Atom N2800 qui ne supporte pas la virtualisation hardware, on ne pourra donc pas utiliser QEMU avec KVM. En conséquence, ça va être lent : j’ai mis environs 1/2 heure pour démarrer une Ubuntu Server.

Pour commencer :

  • désactiver le monitoring dans le manager Kimsufi (pour éviter les mails d’alerte)
  • sélectionner le démarrage en rescue-pro
  • redémarrer le serveur
  • se connecter en SSH avec le mot de passe reçu par mail

Le rescue est un système Linux complet qui tourne sur le serveur sans toucher au disque dur. Certains répertoires sont montés depuis le réseau et sont en lecture seule, ce qui ne permet pas d’installer des programmes. La première étape est donc de s’en débarrasser en déplaçant tout ça dans un tmpfs en RAM que l’on montera à la place des répertoires réseau :

# mount -t tmpfs -o size=2000m tmpfs /mnt
# mkdir /mnt/var
# mkdir /mnt/var/cache
# mkdir /mnt/var/lib
# mkdir /mnt/var/run
# mkdir /mnt/usr
# mkdir /mnt/lib
# rsync -a /var/cache/ /mnt/var/cache/
# rsync -a /var/lib/ /mnt/var/lib/
# rsync -a /var/run/ /mnt/var/run/
# rsync -a /usr/ /mnt/usr/
# rsync -a /lib/ /mnt/lib/
# mount -B /mnt/var/cache /var/cache
# mount -B /mnt/var/lib /var/lib
# mount -B /mnt/var/run /var/run
# mount -B /mnt/usr /usr
# mount -B /mnt/lib /lib

Note : le KS-2 dispose de 4Go de RAM. Le tmpfs fait 2Go pour garder une bonne marge en particulier pour QEMU.

On peut ensuite mettre à jour et installer QEMU :

# apt-get -y update
# apt-get -y --force-yes upgrade
# apt-get -y install qemu

Tout est prêt ; on peut lancer QEMU :

# qemu-system-x86_64 -net nic -net user,hostfwd=tcp::60022-:22 -m 1024M -alt-grab -localtime -cpu core2duo -smp 2 -usbdevice tablet -hda /dev/sda -vnc :0

Note : l’option hostfwd=tcp::60022-:22 permet de rediriger le port 60022 de l’hôte (le serveur en rescue) vers le port 22 de l’invité (la VM), ce qui peut être utile pour se connecter en SSH sur la VM.

Sur un poste client (Ubuntu Desktop par exemple), on va créer un tunnel SSH entre le port local 5901 et le port 5900 du serveur en rescue. Ce tunnel va permettre de faire transiter la connexion VNC de manière sécurisée entre le client VNC et le serveur.

$ ssh -L 5901:localhost:5900 -XC root@[server-IP]

Dans un autre terminal toujours sur le poste client, on peut lancer le client VNC (Vinagre par exemple sur Ubuntu) :

$ vinagre localhost:5901

Plus le temps est court entre le lancement de QEMU sur le serveur et le lancement du client VNC, plus on arrive tôt dans la séquence de boot.

Pour arrêter la machine virtuelle, un simple CTRL+C sur le terminal où tourne QEMU suffit, mais si c’est possible, arrêter proprement la machine sera plus sain.

Sources :

Installer Ubuntu dans une partition chiffrée sur un serveur Kimsufi

Pour pouvoir faire de l’autohébergement pour pas trop cher, j’ai un serveur Kimsufi chez OVH. Seulement, qui dit autohébergement dit données personnelles et donc nécessité de les protéger. Or le serveur, dont je ne suis que locataire, est situé dans un datacenter que je ne verrai probablement jamais, et je ne maîtrise pas qui a un accès physique à la machine.

D’où l’idée de vouloir faire un chiffrement complet du disque dur, pour que même si quelqu’un avait l’idée d’aller fouiller dedans, il ne puisse pas y lire quoi que ce soit.

L’OS que je veux utiliser est une distribution Ubuntu Server 14.04 LTS avec le chiffrement proposé à base de dm-crypt/cryptsetup/LUKS. Tout le root file system (y compris le /home) et la partition swap sont chiffrés. Seule la partition de boot qui contient l’initramfs et le kernel n’est pas chiffrée, pour permettre de démarrer le système et attendre que l’utilisateur entre la clé de déverrouillage.

Deux problèmes se posent :

  1. l’interface de gestion Kimsufi permet seulement d’installer un nombre limité de distributions Linux, avec une configuration prédéfinie et pas de chiffrement de partitions
  2. le système repose sur le fait d’entrer la clé au démarrage pour déverrouiller la partition, ce qui n’est pas possible sur un serveur « headless », sans clavier et écran et à l’autre bout du monde

Pour le premier point, il va donc falloir faire une installation custom du serveur, à distance et sans clavier+écran.

Pour le second point, l’astuce consiste à installer dans l’initramfs un serveur SSH sur lequel on va pouvoir se connecter à distance pour entrer la clé de déverrouillage et débloquer la suite du démarrage.

Préparation :

La technique que j’ai choisie va consister à faire une installation dans une machine virtuelle sur un PC de bureau avec le chiffrement, puis de copier par le réseau le root file system généré et le contenu de la partition de boot vers le serveur Kimsufi démarré en mode rescue.

Les étapes :

  1. Créer une VM avec VirtualBox et y installer une distribution Ubuntu Server 14.04 LTS 64bits
  2. Configurer l’initramfs pour permettre le remote unlock par SSH ; vérifier le bon fonctionnement en testant le déverrouillage par SSH depuis l’hôte de la VM par exemple
  3. Redémarrer la VM sur un LiveCD Ubuntu Desktop par exemple et monter un emplacement réseau qui peut recevoir une copie du système dans un fichier (montage NFS dans « /mnt/nfs »)
  4. Depuis le LiveCD, déverrouiller la partition chiffrée :
# cryptsetup luksOpen /dev/sda5 sda5_crypt
# vgscan --mknodes
# vgchange -ay

Réaliser une copie compressée du root file system :

# dd if=/dev/mapper/ubuntu--vg-root conv=sync,noerror bs=64K | gzip -c  > /mnt/nfs/rootfs.img.gz

Copier le contenu de la partiton de boot :

# mkdir /mnt/boot
# mount /dev/sda1 /mnt/boot/
# cp -ar /mnt/boot /mnt/nfs/

Note : la destination de la copie doit être accessible depuis le serveur Kimsufi (IP publique).

Tout est maintenant prêt pour passer sur le Kimsufi.

 Sur le serveur Kimsufi :

Je pars d’un Kimsufi tout neuf avec dessus une distribution Ubuntu Server 14.04 LTS 64bits fraîchement installée par OVH/Kimsufi. Mais peut importe le choix de la distribution via le manager Kimsufi, puisqu’on va tout écraser.

Tout d’abord :

  • désactiver le monitoring dans le manager Kimsufi (pour éviter les mails d’alerte)
  • sélectionner le démarrage en rescue-pro
  • redémarrer le serveur
  • se connecter en SSH avec le mot de passe reçu par mail

La commande parted devrait donner quelque chose dans ce genre :

# parted -l
Model: ATA HGST HUS724020AL (scsi)
Disk /dev/sda: 2000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system     Name     Flags
1      20.5kB  1049kB  1029kB                  primary  bios_grub
2      2097kB  1996GB  1996GB  ext4            primary
3      1996GB  2000GB  4294MB  linux-swap(v1)  primary

On va modifier les partitions avec cgdisk (warning : toutes les données seront perdues).

# cgdisk
  • laisser la « BIOS boot partition » telle quelle
  • supprimer les partitions « Linux filesystem » et « Linux swap »
  • créer une partition « Linux filesystem » de 250 Mo
  • créer une partition « Linux filesystem » sur le reste de l’espace libre

On redémarre la machine (toujours en rescue) pour que les changement soient bien pris en compte :

# reboot

La commande parted doit maintenant donner ceci :

# parted -l
Error: Can't have overlapping partitions.
Model: ATA HGST HUS724020AL (scsi)
Disk /dev/sda: 2000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
1      20.5kB  1049kB  1029kB               primary  bios_grub
2      1053kB  263MB   262MB
3      263MB   2000GB  2000GB

Note : je n’ai pas compris l’erreur « can’t have overlapping partitions », mais ça ne semble pas avoir d’incidence sur la suite.

Créer un système de fichiers EXT2 pour la partition de boot :

# mkfs.ext2 /dev/sda2

Créer le conteneur chiffré avec cryptsetup :

# cryptsetup -y -s 512 -c serpent-xts-plain64 luksFormat /dev/sda3
# cryptsetup luksOpen /dev/sda3 sda3_crypt

Note : le choix de l’algo de chiffrement peut se faire en fonction des perfs de la machine. Le CPU Atom N2800 du KS-2 ne disposant pas des instructions AES-NI, AES ne sera pas forcément le choix le plus performant. Un bench peut être réalisé sur la machine (y compris depuis le rescue) avec la commande « cryptsetup benchmark ».

Créer les volumes pour le root FS et la swap avec LVM dans la partition chiffrée :

# pvcreate /dev/mapper/sda3_crypt
# vgcreate ubuntu-vg /dev/mapper/sda3_crypt
# lvcreate -L 4G -n swap_1 ubuntu-vg
# lvcreate -l 100%FREE -n root ubuntu-vg

Créer la partition swap :

# mkswap /dev/mapper/ubuntu--vg-swap_1

Monter l’emplacement NFS où l’on a mis la copie du système installé dans la machine virtuelle. Décompresser l’image de la partition root dans son volume logique :

# mkdir /mnt/nfs
# mount -t nfs [...] /mnt/nfs
# gunzip -c /mnt/nfs/rootfs.img.gz | dd of=/dev/mapper/ubuntu--vg-root

Redimensionner le file system pour occuper tout le volume logique :

# resize2fs /dev/mapper/ubuntu--vg-root

Monter maintenant le nouveau root FS ainsi que la partition de boot (pour l’instant toujours vide) :

# mkdir /mnt/ubuntu
# mount /dev/mapper/ubuntu--vg-root /mnt/ubuntu/
# mkdir /mnt/boot
# mount /dev/sda2 /mnt/boot/

Copier les fichiers de la partition de boot depuis le montage NFS :

# cp -ar /mnt/nfs/boot/* /mnt/boot/
# umount /mnt/boot

Noter la configuration réseau de l’interface eth0 (le rescue est probablement en DHCP, mais pour la prod on préférera une config en IP fixe) :

# ifconfig eth0
# route -n
# cat /etc/resolv.conf

L’étape suivante est de chrooter dans le root FS Ubuntu pour compléter la configuration :

# swapon /dev/mapper/scoresby2--vg-swap_1
# mount -o bind /dev /mnt/ubuntu/dev
# mount -t proc proc /mnt/ubuntu/proc
# mount -t sysfs sys /mnt/ubuntu/sys
# XTERM=xterm-color LANG=C.UTF-8 chroot /mnt/ubuntu /bin/bash
# mount /dev/sda2 /boot/

A l’aide de la commande blkid, on trouve les UUID des différentes partitions et volumes logiques :

# blkid

Editer le fichier /etc/crypttab avec l’UUID de la partition /dev/sda3 :

# vi /etc/crypttab
sda3_crypt UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx none luks,discard

Editer le fichier /etc/fstab avec l’UUID de la partition /dev/sda2 :

# vi /etc/fstab
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /boot           ext2    defaults        0       2

Créer un lien symbolique pour /etc/mtab :

# ln -sf /proc/mounts /etc/mtab

Côté réseau, il faut configurer l’interface eth0 avec l’IP fixe que l’on a notée dans un coin, ainsi que le serveur DNS.

 # vi /etc/network/interfaces
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address [...]
    netmask [...]
    network [...]
    broadcast [...]
    gateway [...]
    pre-up /sbin/ip addr flush dev eth0 || true

[+ config IPv6]

Note : la commande de pre-up sert à se débarrasser de la config réseau utilisée pendant l’initramfs (pour déverrouiller le volume chiffré en remote).

# vi /etc/resolv.conf
nameserver 127.0.0.1
nameserver [...]
search ovh.net

Il faut maintenant configurer GRUB2 pour le démarrage du serveur. On doit s’assurer que le système démarrera bien automatiquement sur Ubuntu, et définir la configuration réseau pour le kernel.

# vi /etc/default/grub
GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=2
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=0
GRUB_RECORDFAIL_TIMEOUT=$GRUB_HIDDEN_TIMEOUT
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="ip=[ip-address]::[default-gateway]:[netmask]::eth0:none net.ifnames=0 biosdevname=0"
GRUB_TERMINAL=console
GRUB_DISABLE_RECOVERY="true"

Notes :

  • l’option « GRUB_RECORDFAIL_TIMEOUT » permet de ne pas bloquer dans le menu de GRUB après un échec au démarrage et de s’assurer qu’on démarrera sur l’option par défaut
  • les paramètres « net.ifnames=0 » et « biosdevname=0 » permettent d’éviter que l’interface réseau eth0 ne soit renommée en autre chose (force le nommage legacy)

Pour le principe, je dégage l’option memory test du menu de GRUB :

# chmod -x /etc/grub.d/20_memtest86+

On installe GRUB sur le disque /dev/sda :

# grub-install /dev/sda
# update-grub

On met à jour l’initramfs :

# update-initramfs -u

On peut vérifier le contenu de l’initramfs :

# zcat /boot/initrd.img-* | cpio -t conf/conf.d/cryptroot etc/lvm/lvm.conf etc/dropbear/\* root/.ssh/authorized_keys sbin/dropbear

Vérifier que la sortie contient bien :

root/.ssh/authorized_keys
sbin/dropbear
conf/conf.d/cryptroot
etc/lvm/lvm.conf
etc/dropbear/dropbear_dss_host_key
etc/dropbear/dropbear_rsa_host_key
# zcat /boot/initrd.img-* | cpio -i --to-stdout conf/conf.d/cryptroot

Vérifier que la sortie est de la forme :

target=sda3_crypt,source=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx,key=none,rootdev,lvm=ubuntu--vg-root

La configuration est terminée. On sort maintenant du chroot, on démonte les partitions et on ferme le volume chiffré :

# umount /boot
# exit
# umount /mnt/ubuntu/{dev,proc,sys}
# umount /mnt/ubuntu
# swapoff -a
# lvchange -an /dev/mapper/ubuntu--vg-*
# cryptsetup luksClose sda3_crypt

Retourner dans le manager Kimsufi et sélectionner le démarrage sur le disque dur. On serre les fesses et

# reboot

Si tout va bien, la machine démarre et attend dans l’initramfs que le volume chiffrée soit déverrouillé. On se connecte en SSH (sur Dropbear) et la commande « unlock » perment d’entrer la passphrase. On peut ensuite se déconnecter et se reconnecter une fois que le serveur a fini de démarrer (sur OpenSSH cette fois-ci).

Si ça ne marche pas, on peut redémarrer en rescue et vérifier la configuration. On peut aussi démarrer sur le disque dur via QEMU dans le rescue pour débuguer la séquence de boot avec une connexion VNC.

Sources :