Installation de Void Linux chiffré sur un serveur distant

L'écriture de ce guide est motivé par les mêmes raisons que celui-ci : Installation d'Alpine Linux chiffré sur un serveur distant.

Une méthode est présentée sur le wiki officiel, mais celle-ci utilise dracut-crypt-ssh, qui dépend de Dropbear et qui ne sait pas gérer ed25519 contrairement à TinySSH.

Je fabriquerais à la fin mon initramfs et j'y intégrerais tinysshd afin de déchiffrer par le réseau ma partition '/' au démarrage.

Je prendrais ici pour exemple le cas d'un serveur dédié Kimsufi mais la manipulation devrait être globalement la même partout à partir du moment où le fournisseur permet de démarrer sur un linux rescue.

Vous pouvez aussi dans un premier temps, tester la manipulation chez vous dans une VM et utiliser SystemRescueCd (choisir rescue64 au boot) afin de tester cette procédure.

  1. Pré-requis
  2. Création de la configuration réseau
  3. Partitionnement
  4. Chiffrement et formatage
  5. Installation
  6. Ajout du module dracut intégrant TinySSH
  7. Redémarrage
  8. Conclusion
  9. Références et remerciements

1. Pré-requis

  • Avoir la possibilité de booter sur un linux rescue.

2. Création de la configuration réseau

Connectez-vous en SSH sur votre serveur démarré en mode rescue, puis récupérez les informations utiles :

root@rescue:~# udevadm test-builtin net_id /sys/class/net/eth0 2> /dev/null
    [...]
    ID_NET_NAME_PATH=enp1s0
root@rescue:~# ip a
    [...]
    6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
        link/ether f6:c5:fb:7e:d9:8f brd ff:ff:ff:ff:ff:ff
        inet 5.XXX.XXX.85/24 brd 5.XXX.XXX.255 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 2001:XXX:X:XX55::1/128 scope global
           valid_lft forever preferred_lft forever
        inet6 fe80::222:4dff:fea1:a419/64 scope link
           valid_lft forever preferred_lft forever
    [...]
root@rescue:~# ip r
    default via 5.XXX.XXX.254 dev eth0
    5.XXX.XXX.0/24 dev eth0  proto kernel  scope link  src 5.XXX.XXX.85
root@rescue:~# ip -6 r
    2001:XXX:X:XX55::1 dev eth0  proto kernel  metric 256
    2001:XXX:X:XXff:ff:ff:ff:ff dev eth0  metric 1024
    fe80::/64 dev eth0  proto kernel  metric 256
    default via 2001:XXX:X:XXff:ff:ff:ff:ff dev eth0  metric 1
root@rescue:~# cat /etc/resolv.conf
    search ovh.net
    nameserver 213.186.33.99

De là, on va pouvoir définir les lignes de commandes suivantes qui nous serviront par la suite :

ip link set dev enp1s0 up
ip addr add 5.XXX.XXX.85/24 brd + dev enp1s0
ip route add default via 5.XXX.XXX.254
ip -6 addr add 2001:XXX:X:XX55::1/128 dev enp1s0
ip -6 route add 2001:XXX:X:XXff:ff:ff:ff:ff dev enp1s0
ip -6 route add default via 2001:XXX:X:XXff:ff:ff:ff:ff

Mon serveur étant hébergé chez OVH, le serveur DNS 213.186.33.99 leur appartient et je peux donc l'utiliser.

3. Partitionnement

Notez l'utilisation de GPT et prenez l'habitude à ne plus utiliser MBR. J'utilise parted, mais vous pouvez utiliser cfdisk si vous préférez :

[user@local ~]$ ssh-keygen -R ip
[user@local ~]$ ssh root@ip
[root@serveur ~]# sgdisk -Z /dev/sda
[root@serveur ~]# parted /dev/sda
(parted) mklabel gpt
(parted) mkpart primary 2048s 4095s
(parted) mkpart primary ext2 4096s 200MB
(parted) mkpart primary xfs 200MB -500MB
(parted) mkpart primary linux-swap -500MB 100%
(parted) set 1 bios_grub on

Vous pouvez adapter votre partitionnement comme bon vous semble, mais vous devrez adapter la suite.

Voici mon partitionnement :

[root@serveur ~]# parted /dev/sda print
    Model: ATA HGST HUS726020AL (scsi)
    Disk /dev/sda: 2000GB
    Sector size (logical/physical): 512B/512B
    Partition Table: gpt
    Disk Flags:
    Number  Start   End     Size    File system     Flags
     1      1049kB  2097kB  1049kB                  bios_grub
     2      2097kB  200MB   198MB   ext2
     3      200MB   2000GB  2000GB  xfs
     4      2000GB  2000GB  500MB   linux-swap(v1)

Ce qui me donnera approximativement :

/boot : 200MB (ext2)
/     : 2TB (xfs chiffré)
swap  : 2GB (swap)

4. Chiffrement et formatage

Il va falloir utiliser une méthode efficace et performante et afin de déterminer ça, nous utiliserons la commande suivante :

[root@serveur ~]# cryptsetup benchmark
    # Tests are approximate using memory only (no storage IO).
    PBKDF2-sha1       218453 iterations per second for 256-bit key
    PBKDF2-sha256     301661 iterations per second for 256-bit key
    PBKDF2-sha512     168907 iterations per second for 256-bit key
    PBKDF2-ripemd160  208050 iterations per second for 256-bit key
    PBKDF2-whirlpool   87148 iterations per second for 256-bit key
    argon2i       4 iterations, 170831 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
    argon2id      4 iterations, 171825 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
    #     Algorithm |       Key |      Encryption |      Decryption
            aes-cbc        128b        40.2 MiB/s        42.6 MiB/s
        serpent-cbc        128b        32.3 MiB/s        72.9 MiB/s
        twofish-cbc        128b        33.8 MiB/s        35.0 MiB/s
            aes-cbc        256b        31.1 MiB/s        32.0 MiB/s
        serpent-cbc        256b        32.3 MiB/s        73.0 MiB/s
        twofish-cbc        256b        33.8 MiB/s        35.0 MiB/s
            aes-xts        256b        41.7 MiB/s        42.2 MiB/s
        serpent-xts        256b        68.1 MiB/s        68.5 MiB/s
        twofish-xts        256b        34.1 MiB/s        34.8 MiB/s
            aes-xts        512b        31.4 MiB/s        31.8 MiB/s
        serpent-xts        512b        68.0 MiB/s        68.4 MiB/s
        twofish-xts        512b        34.1 MiB/s        34.8 MiB/s

Vous ne rêvez pas, les performances sont désastreuses étant donné que mon microprocesseur est un Atom N2800 et qu'il ne possède pas le jeu d'instruction AES-NI.

Chiffrement de la partition /dev/sda3 :

[root@serveur ~]# cryptsetup -v -c serpent-xts-plain64 -s 256 --hash sha256 luksFormat /dev/sda3
[root@serveur ~]# cryptsetup luksOpen /dev/sda3 root

Si votre microprocesseur possède le jeu d'instruction AES-NI, il faudra généralement utiliser aes-xts et une clé 256 bit afin d'avoir d'excellentes performances :

[root@serveur ~]# cryptsetup -v -c aes-xts-plain64 -s 256 --hash sha256 luksFormat /dev/sda3
[root@serveur ~]# cryptsetup luksOpen /dev/sda3 root

On peut formater :

[root@serveur ~]# mkfs.ext2 -m0 /dev/sda2
[root@serveur ~]# mkfs.xfs /dev/mapper/root
[root@serveur ~]# mkswap /dev/sda4

Vous pouvez utiliser LVM, choisir EXT4, faire du thin provisioning, mais vous devrez adapter la suite.

5. Installation

On monte les partitions puis on rentre dans le chroot :

[root@serveur ~]# mkdir /mnt/void
[root@serveur ~]# mount -t xfs /dev/mapper/root /mnt/void
[root@serveur ~]# mkdir /mnt/void/boot
[root@serveur ~]# mount -t ext2 /dev/sda2 /mnt/void/boot
[root@serveur ~]# cd /mnt/void/
[root@serveur void]# curl -O https://alpha.de.repo.voidlinux.org/live/current/void-x86_64-ROOTFS-20181111.tar.xz
[root@serveur void]# tar xpf void-x86_64-ROOTFS-20181111.tar.xz --xattrs-include='*.*' --numeric-owner
[root@serveur void]# rm -f void-x86_64-ROOTFS-20181111.tar.xz
[root@serveur void]# echo 'nameserver 213.186.33.99' > etc/resolv.conf
[root@serveur void]# mount --types proc /proc proc
[root@serveur void]# mount --rbind /sys sys
[root@serveur void]# mount --rbind /dev dev
[root@serveur void]# mount --make-rslave sys
[root@serveur void]# mount --make-rslave dev
[root@serveur void]# cd
[root@serveur ~]# chroot /mnt/void /usr/bin/bash

Vous aurez bien sûr pensé à utiliser la dernière version du rootfs à cet emplacement et utiliser le bon serveur DNS.

Mise à jour du système :

[root@serveur /]# xbps-install -Su

Attention, si la commande précédente est immédiatement Killed (c'est le cas chez FirstHeberg), vous devrez utiliser un autre miroir comme expliqué ici.

Suite à l'installation, vous pourrez supprimer le fichier créé dans /etc/xbps.d/ ou utiliser le miroir de votre choix.

Création d'un utilisateur :

[root@serveur /]# useradd -m -G wheel,floppy,audio,video,cdrom,optical,kvm,xbuilder changeme
[root@serveur /]# passwd changeme

Ajout de l'utilisateur dans le fichier /etc/sudoers avec la commande visudo, à adapter comme bon vous semble, je décommente cette ligne :

%wheel ALL=(ALL) ALL

Définition du password root :

[root@serveur /]# passwd

Modification du fuseau horaire dans le fichier /etc/rc.conf :

TIMEZONE="Europe/Paris"

Récupération des UUID, vous devrez en toute logique adapter la suite avec les vôtres :

[root@serveur /]# blkid
/dev/sda1: PARTLABEL="primary" PARTUUID="87b05c8e-a339-4bf4-b92e-c626c7d27e34"
/dev/sda2: UUID="881aa33b-f170-4e65-bfd6-32e31d69f2f3" TYPE="ext2" PARTLABEL="primary" PARTUUID="b0bedbc1-f744-4fd0-bec7-fe0bdb705308"
/dev/sda3: UUID="a7768716-3cd2-4a79-ae9e-a0b829b3d0ac" TYPE="crypto_LUKS" PARTLABEL="primary" PARTUUID="73b47d6e-09ec-47ec-bb4e-36d6963ccdf8"
/dev/sda4: UUID="23a6c6d0-f048-49b5-b3e2-b7cedd8cc307" TYPE="swap" PARTLABEL="primary" PARTUUID="249421f4-13e6-4970-8dee-e09c075793dc"
/dev/mapper/root: UUID="86340405-9d6a-4727-8bcf-20da39806231" TYPE="xfs"

Modification de /etc/fstab :

UUID=86340405-9d6a-4727-8bcf-20da39806231 / xfs rw,noatime,nodiratime 0 1
UUID=881aa33b-f170-4e65-bfd6-32e31d69f2f3 /boot ext2 rw,noatime,nodiratime 0 2
UUID=23a6c6d0-f048-49b5-b3e2-b7cedd8cc307 none swap defaults 0 0
tmpfs /tmp tmpfs defaults,nosuid,nodev 0 0

Si vous utilisez un disque SSD, attention à bien ajouter le flag discard ou alors de créer une tâche cron pour la prise en compte du trim.

Définition du hostname :

[root@serveur /]# echo 'changeme' > /etc/hostname

Modification de /etc/locale.conf, vous pouvez utiliser la locale fr_FR.UTF-8 si vous préférez :

LANG=en_US.UTF-8
LC_COLLATE=C

Modification de /etc/default/libc-locales, décommentez la locale de votre choix :

[...]
#en_SG ISO-8859-1
en_US.UTF-8 UTF-8
#en_US ISO-8859-1
[...]

Pour prise en compte si vous avez effectué des modifications :

[root@serveur /]# xbps-reconfigure -f glibc-locales

Installation du kernel, vous pouvez choisir entre linux ou linux-lts :

[root@serveur /]# xbps-install linux

Pour la configuration réseau, vous ajouterez les lignes de commandes que vous avez préparez plus tôt dans le fichier /etc/rc.local :

ip link set dev enp1s0 up
ip addr add 5.XXX.XXX.85/24 brd + dev enp1s0
ip route add default via 5.XXX.XXX.254
ip -6 addr add 2001:XXX:X:XX55::1/128 dev enp1s0
ip -6 route add 2001:XXX:X:XXff:ff:ff:ff:ff dev enp1s0
ip -6 route add default via 2001:XXX:X:XXff:ff:ff:ff:ff

Installation du bootloader, je ne préconise généralement pas grub, mais pour Void Linux c'est bien trop contraignant d'en utiliser un autre :

[root@serveur /]# xbps-install grub
[root@serveur /]# grub-install /dev/sda

Modification du fichier /etc/default/grub, je me répète mais n'oubliez pas d'adapter :

[...]
GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR="Void"
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 slub_debug=P page_poison=1 ip=5.XXX.XXX.85::5.XXX.XXX.254:255.255.255.0::enp1s0:off rd.luks.uuid=a7768716-3cd2-4a79-ae9e-a0b829b3d0ac root=UUID=86340405-9d6a-4727-8bcf-20da39806231"
[...]

Si vous utilisez un disque SSD, n'oubliez pas d'activer le TRIM, dans ce cas la ligne GRUB_CMDLINE_LINUX_DEFAULT ressemblera à ceci :

[...]
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=4 slub_debug=P page_poison=1 ip=5.XXX.XXX.85::5.XXX.XXX.254:255.255.255.0::enp1s0:off rd.luks.uuid=a7768716-3cd2-4a79-ae9e-a0b829b3d0ac rd.luks.allow-discards=a7768716-3cd2-4a79-ae9e-a0b829b3d0ac root=UUID=86340405-9d6a-4727-8bcf-20da39806231"
[...]

Installation de cryptsetup, curl, dracut-network et tinyssh :

[root@serveur /]# xbps-install cryptsetup curl dracut-network tinyssh

Lancement automatique d'OpenSSH et suppression des agetty :

[root@serveur /]# cd /etc/runit/runsvdir/current/
[root@serveur current]# ln -s /etc/sv/sshd/
[root@serveur current]# rm -f agetty-tty*
[root@serveur current]# ls
sshd  udevd

Attention tout de même, vous n'aurez plus de TTY en accès physique, et c'est ce que je recherche dans un datacenter, par sécurité, vous pouvez laisser agetty-tty1.

A faire que si vous souhaitez conserver un TTY en accès physique :

[root@serveur /]# cd /etc/runit/runsvdir/current/
[root@serveur current]# ln -s /etc/sv/agetty-tty1/

Ajout de votre clé publique de type ED25519 qui servira à déverrouiller votre partition au boot grâce à TinySSH :

[root@serveur /]# echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDG6xoQpsNhFSsn9fIxP0bR3hTKYUYmh72d6fAS10khX doctor@who' > /etc/tinyssh/authorized_keys
[root@serveur /]# chmod 600 /etc/tinyssh/authorized_keys

Le module dracut va utiliser le fichier authorized_keys disponible dans /etc/tinyssh/ en priorité, s'il ne le trouve pas, il ira voir dans /root/.ssh/ et s'il n'en trouve aucune, il ne sera pas intégré dans l'initramfs.

Vous pouvez bien sûr en avoir plusieurs :

[root@serveur /]# cat /etc/tinyssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHXKA9rOvUQmKi1zz339YG8d+wPOL/pq9jmu1krtjujY doctor@who
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH6J/4X1NwxqtLIRZazdRvIvGrUJcIlaL4wPnOik/Q1E saitama@opm
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJl7F5YJ1NUiFW8e1XZXqOpIOAcJ4dEkv45bKVGzaq42 goku@god
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILpN34LuPCMsOUQXgMO5jqZJK+DXpp42a/cy4WGxd8az luffy@gomu

Ajoutez aussi votre clé au niveau de votre utilisateur afin de pouvoir vous connecter sur le système grâce à OpenSSH suite au déverrouillage :

[root@serveur /]# mkdir /home/changeme/.ssh/
[root@serveur /]# chmod 700 /home/changeme/.ssh/
[root@serveur /]# cp /etc/tinyssh/authorized_keys /home/changeme/.ssh/
[root@serveur /]# chown -R changeme: /home/changeme/.ssh/

6. Ajout du module dracut intégrant TinySSH

Récupération du module sur le dépot git :

[root@serveur /]# cd /usr/lib/dracut/modules.d/
[root@serveur modules.d]# curl -O https://gitea.tetsumaki.net/tetsumaki/dracut-crypt-tinyssh/archive/master.tar.gz
[root@serveur modules.d]# tar zxf master.tar.gz
[root@serveur modules.d]# rm -f master.tar.gz
[root@serveur modules.d]# mv dracut-crypt-tinyssh/ 60crypt-tinyssh

Attention, si le téléchargement avec la commande curl est immédiatement Killed (c'est le cas chez FirstHeberg), vous devrez récupérer l'archive sur votre PC puis l'envoyer par scp.

Prise en compte des modifications, remplacez linux4.19 par votre version xbps-query linux :

[root@serveur /]# xbps-reconfigure -f linux4.19

Nettoyage :

[root@serveur /]# xbps-remove -Oo

7. Redémarrage

Vous avez normalement tout configuré et pour résumer, vous devez contrôler que :

  • /etc/rc.local : votre configuration réseau
  • /etc/default/grub : vos paramètres réseaux, luks
  • /etc/tinyssh/authorized_keys : votre clé publique soit bien présente (pour TinySSH au niveau de l'initramfs)
  • /home/changeme/.ssh/authorized_keys : votre clé publique soit bien présente (pour OpenSSH au niveau du système)

Le moment de vérité, sortez du chroot et redémarrez :

[root@serveur ~]# exit
[root@serveur ~]# umount -R /mnt/void/
[root@serveur ~]# reboot

Si tout va bien, vous pouvez vous connecter et déchiffrer votre partition en tapant unlock.sh :

[user@local ~]$ ssh root@ip
[root@serveur ~]# unlock.sh
    Enter passphrase for /dev/sda3:

Vous perdez maintenant la connexion ssh, vous pouvez vous reconnecter et terminer la configuration du système :

[user@local ~]$ ssh user@ip
[user@serveur ~]$ sudo -i

8. Conclusion

Votre système est maintenant installé.

Si votre système ne répond plus ou que vous ne pouvez pas y accéder par SSH c'est que vous avez mal fais quelque chose.

Dans ce cas vous pouvez faire un reboot hardware en rescue et voir ce qui cloche en chrootant.

  • Il est fortement conseillé de configurer OpenSSH avec une authentification par clé uniquement
  • D'activer iptables et ip6tables : ln -s /etc/sv/iptables /var/service/ et ln -s /etc/sv/ip6tables /var/service/
  • D'installer un daemon syslog tel que socklog-void
  • D'utiliser tinyssh-convert afin d'utiliser la même clé privée qu'OpenSSH (/etc/ssh/ssh_host_ed25519_key --> /etc/tinyssh/sshkeydir/*)

9. Références et remerciements