root on ZFS on LUKS (on arch)

Update 2018-05-22: Inzwischen gibt es über archzfs fertige Pakete und Tools, alles hier ist vermutlich obsolet.


Mir fiel kürzlich ein Thinkpad T430 in die Hände. Da inzwischen natürlich sämtliche verbaute Hardware in den gängigen Linuxen einfach[tm] funktioniert muss ich halt mit Software basteln. Ausserdem fasst der Rechner zusätzlich zur SSD noch große Mengen drehenden Rost, daher der Wunsch nach einem ordentlichen Dateisystem.

Damit ich mich nächstes Jahr noch erinnere was ich da verbastelt habe, hier also mein kleines Rezept:

/ auf ZFS auf LUKS

Im Grunde wird dieser Artikel ein Mix aus folgenden Ressourcen. Ein Blick lohnt sich, vermutlich sind die Informationen dort ab nächster Woche aktueller als hier:

Installationsmedium

Überraschungsfrei: Image herunterladen (https://www.archlinux.org/download/), auf USB-Stick schreiben, von diesem booten. Man könnte jetzt schon die benötigten ZFS-Packages in ein eigenes Image bauen, ich machte das aber nachher im gebooteten Live-System. NB: Ich musste im BIOS einstellen das EFI-Boot vorgezogen wird, sonst wurde das Live-System im legacy-modus gestartet.

Live-System starten

Nach dem Boot vom Stick geht es wie gewohnt weiter:

Keyboard-Layout:

# loadkeys de-latin1

WIFI per netctl (netctl – ArchWiki):

# cp /etc/netctl/examples/wireless-wpa /etc/netctl/wifi
# vim /etc/netctl/wifi  # Interface-Namen nicht vergessen!
# netctl start wifi

Ich machte die Installation von einem anderen Rechner aus per SSH, also sshd starten und ein Passwort für root setzen:

# systemctl start sshd.service
# passwd

Um den ganzen ZFS-Kram im Live-System installieren zu können braucht es etwas mehr "Platten"platz:

# mount -o remount,size=2G /run/archiso/cowspace

Partitionierung

Der Rechner kam mit Windows 7 auf legacy-partitionierter SSD. Aber das ist man ja schnell los:

# dd if=/dev/zero of=/dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06 status=progress

(NB: Wer noch in /dev/sdX denkt, ls -al /dev/disk/by-id gibt die Übersetzung dazu aus. Allzu viele Platten tummeln sich im Laptop ja auch üblicherweise nicht...)

Danach frische Partitionen anlegen:

# gdisk /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06

Wurde nicht die ganze Disk genullt: Auf die Nachfrage ob eine neue GPT erstellt werden soll ist die Antwort natürlich ja:

1 - Use current GPT
2 - Create blank GPT
Your answer: *2*
Command (? for help):

Ich lege auf der SSD 3 Partitionen an, EFI+/boot, Swap, ZFS-root.

Zuerst also die EFI-System-Partition, mit 2 GB Platz mehr als zu groß (Details: ESP – ArchWiki):

Command (? for help): n
Partition number (1-128, default 1): 1
First sector (34-250069646, default = 2048) or {+-}size{KMGTP}:    
Last sector (2048-250069646, default = 250069646) or {+-}size{KMGTP}: +2G
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): EF00
Changed type of partition to 'EFI System'

Als nächstes swap, ausreichend groß für hibernate, and them some:

Command (? for help): n
Partition number (2-128, default 2): 
First sector (34-250069646, default = 4196352) or {+-}size{KMGTP}: 
Last sector (4196352-250069646, default = 250069646) or {+-}size{KMGTP}: +32G
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): 8200
Changed type of partition to 'Linux swap'

Den Rest des Platzes bekommt die ZFS-Partition, der Typ ist im Prinzip egal. Ich nehme bf00, Solaris root. Weil, warum nicht…

Und zuletzt das Dateisystem für die EFI System Partition:

# mkfs.fat -F32 /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part1

Crypto

Swap + ZFS-root werden vor der Dateisystem-Erzeugung verschlüsselt.

# cryptsetup luksFormat /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part2
# cryptsetup luksFormat /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part3

Und danach gleich wieder öffnen:

# cryptsetup luksOpen /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part2 crypt_swap
# cryptsetup luksOpen /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part3 crypt_zroot

Swapspace erstellen und einhängen:

# mkswap /dev/disk/by-id/dm-name-crypt_swap
# swapon /dev/disk/by-id/dm-name-crypt_swap

Installation von ZFS im Live-System

Um die ZFS-Module zu bauen braucht es ein wenig Vorbereitung. Zuerst das build-system installieren:

# pacman -Sy base-devel git kernel-headers

Mein Archiso-Image war schon ein paar Tage alt, daher habe ich mir die passenden Kernel-Header aus dem Arch Linux Archive holen müssen.

Build-User anlegen:

# useradd -m -s /bin/zsh build
# visudo  # Da build kein Passwort hat bietet sich NOPASSWD an. ;)

Und dann mit diesem die Module holen und bauen:

# sudo -i -u build
% curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/spl-dkms.tar.gz
% tar xzf spl-dkms.tar.gz
% cd spl-dkms
% makepkg -dri
% cd ~
% curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/zfs-dkms.tar.gz
% tar xzf zfs-dkms.tar.gz
% cd zfs-dkms
% makepkg -dri

Nun kann ich ZFS laden und die Dateisysteme anlegen:

# modprobe zfs
# zpool create -f zroot /dev/disk/by-id/dm-name-crypt_zroot
# zfs create -o mountpoint=none zroot/data
# zfs create -o mountpoint=none zroot/ROOT
# zfs create -o compression=lz4 -o mountpoint=/ zroot/ROOT/default
# zfs create -o compression=lz4 -o mountpoint=/home zroot/data/home

Die dabei ausgegebenen Fehlermeldungen das die Dateisysteme nicht gemountet werden können sind zu erwarten. Es läuft ja noch das Livesystem...

Bootflag für das root-System:

# zpool set bootfs=zroot/ROOT/default zroot

Und den Pool unter /mnt für die Installation reimportieren:

# zpool export zroot
# zpool import -d /dev/disk/by-id -R /mnt zroot

Und das Cache-File ins Zielsystem kopieren:

# mkdir -p /mnt/etc/zfs
# cp /etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache

Weiter geht es quasi mit der normalen Installationsprozedur:

# mkdir /mnt/boot
# mount /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part1 /mnt/boot
# pacstrap /mnt base base-devel linux-headers git vim

Danach einmal die fstab generieren lassen, und gleich wieder ausleeren, da die ZFS-Dateisysteme sowieso automatisch gemountet werden.

# genfstab -U /mnt >> /mnt/etc/fstab
# vim /mnt/etc/fstab

Übrig bleiben:

# cat /mnt/etc/fstab
zroot/ROOT/default      /           zfs         rw,relatime,xattr,noacl0 0
UUID=C4E9-3C43          /boot       vfat        rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro    0 2
/dev/disk/by-id/dm-name-crypt_swap  none        swap        defaults    0 0

Damit swap auch beim booten entschlüsselt wird:

# echo "crypt_swap /dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part2 none luks" >> /mnt/etc/crypttab

Zeit für den Wechsel ins zu installierende System:

# arch-chroot /mnt
[root@archiso /]#

Und weiter mit der Installation nach Handbuch:

# vi /etc/locale.gen 
# locale-gen 
# ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime
# hwclock --systohc --utc
# echo LANG=en_US.UTF-8 > /etc/locale.conf
# echo KEYMAP=de-latin1 > /etc/vconsole.conf
# echo euterpe.zknt.org > /etc/hostname
# echo 127.0.0.1 euterpe euterpe.zknt.org >> /etc/hosts
# pacman -S netctl wpa_supplicant  # netctl-profil kann aus dem live-system kopiert werden

(nochmal) ZFS installieren

Im Zielsystem müssen auch nochmal die zfs-packages installiert werden, gleiche Anleitung wie oben. Ob nochmal mit build-User oder ob gleich der richtige Nutzeraccount angelegt wird ist Geschmackssache...

initrd und bootloader

Als letzte Schritte sind noch init und der bootloader zu konfigurieren.

Zuerst den Bootloader installieren:

# bootctl --path=/boot install
# echo -e "default arch\ntimeout 3\neditor 1" > /boot/loader/loader.conf

Wenn hinterher alles funktioniert kann editor hier auf 0 gesetzt werden. Erstmal bleibt er zur Sicherheit an.

Und /boot/loader/entries/arch.conf entsprechend anpassen:

# cat /boot/loader/entries/arch.conf
title Arch Linux
linux /vmlinuz-linux
initrd /initramfs-linux.img
options cryptdevice=/dev/disk/by-id/ata-C400-MTFDDAK128MAM_00000000122603410E06-part3:crypt_zroot zfs=


bootfs zfs=zroot rw

mkinitcpio.conf anpassen:

# vi /etc/mkinitcpio.conf
...
HOOKS="base udev autodetect modconf block encrypt zfs filesystems keyboard fsck"
...
# mkinitcpio -p linux

Zeit fürs Root-Passwort setzen und Neustart!

# passwd
# exit
# umount /mnt/boot
# zfs umount -a
# zpool export zroot
# reboot

Nach dem ersten Boot # zpool set cachefile=/etc/zfs/zpool.cache zroot # systemctl enable zfs.target