2022-02-26

alpineLinux: Notebook Installation on encrypted Partitions

#software

I'm toying with alpine linux since a while. I am truely impressed by it's slim footprint on the disk. So I have tried it on a Dell Notebook as a "sys" install (everything on disk). Needless to say, it worked like a charm. But how about adding uefi/grub boot and disk encryption? While reading up on available documentation, I came across a detailed post at wejn.org. The author (Michal Jirků?) makes two points:

Now that shell script is full of wonders: using nvme disks; using zfs; using software RAID (mdadm) for the non-zfs parts; and on, all the way to using secure boot with his own keys ... what a ride.

https://wejn.org/2021/01/alpinelinux-secure-boot-with-full-encryption/

This bit really triggered my interest:

There were quite a number of puzzle pieces, which I had been searching before, everything in one place. Fabulous!

So I set out to install alpine linux on a Dell Latitude E6230 with a similar structure:

The commands to achieve this are listed below. It's not strictly a shell script. Be sure to adapt sizes and key material to your liking. I did repeat this installation on another notebook. Everything worked except for the secureboot step. So I tried another notebook (Dell Latitude E7470) and it worked on the first attempt. So either I did some mistake or omission, or that particular UEFI did not successfully replace the keys.

If you try this, don't follow it blindly! I don't want to hear that you bricked your system. Nonetheless, have the appropriate amount of fun!

Thanks to Michal and others for all the documentation!

Cheers,

~ew

https://alpinelinux.org/

Home

# --- the gory details -----------------------------------------------
# boot an alpinelinux installation stick, possibly with uefi enabled
# once at the login prompt, login as root

# --- install a few more tools
apk add e2fsprogs lsblk parted cryptsetup lvm2 iproute2 bash

# --- delete old boot block and partition table
dd if=/dev/zero of=/dev/sda bs=1M count=20

# --- create new partitions; there might be better ways
parted /dev/sda \
       mklabel gpt \
       mkpart ESP fat32 2048s 256M \
       set 1 esp on set 1 boot on \
       mkpart BOOT ext2 256M 512M \
       mkpart OS   ext2   1G  64G \
       print
partprobe -s /dev/sda

# --- Partition 1: Efi System Partition ------------------------------
#     format
mkfs.vfat -F 32 /dev/sda1                                   # /boot/efi

# --- Partition 2: ext2 for /boot ------------------------------------
#     create a key file. This mantra is intented to be used interactively,
#     hence "echo -n", because in keyfiles, \n will NOT end the input, but
#     EOF will.
echo -n "mai-sikkritt-mantra" > ./p2.mantra
chmod 0600 ./p2.mantra
#     create a crypto_luks1 container on partition 2 (boot)
#     use luks1 format!
cryptsetup luksFormat --type luks1 -d ./p2.mantra /dev/sda2
#     open and format
cryptsetup open -d ./p2.mantra /dev/sda2 sda2_crypt
mkfs.ext2 /dev/mapper/sda2_crypt

# --- Partition 3: crypto_luks1, hosting lvm2 ------------------------
#     create a keyfile. This mantra is not intented to be used interactively.
touch ./crypto_keyfile.bin
chmod 0600 ./crypto_keyfile.bin
dd bs=512 count=4 if=/dev/urandom of=./crypto_keyfile.bin
#    format and open crypto_luks1 container
cryptsetup luksFormat --type luks1 -d ./crypto_keyfile.bin /dev/sda3
cryptsetup open -d ./crypto_keyfile.bin /dev/sda3 sda3_crypt
#    use as physical volume in volume group 0
pvcreate /dev/mapper/sda3_crypt
vgcreate vg0 /dev/mapper/sda3_crypt
#    create 3 logical volumes and format them
lvcreate -a y -l 2048 -n lvroot vg0
lvcreate -a y -l 2048 -n lvswap vg0
lvcreate -a y -l 2048 -n lvhome vg0
mkfs.ext4 /dev/mapper/vg0-lvroot
mkswap    /dev/mapper/vg0-lvswap
swapon    /dev/mapper/vg0-lvswap
mkfs.ext4 /dev/mapper/vg0-lvhome

# --- mount target tree on /mnt --------------------------------------
mount -t ext4 /dev/mapper/vg0-lvroot   /mnt
mkdir /mnt/boot
mount -t ext2 /dev/mapper/sda2_crypt   /mnt/boot
mkdir /mnt/boot/efi
mount /dev/sda1                        /mnt/boot/efi
mkdir /mnt/home
mount -t ext4 /dev/mapper/vg0-lvhome   /mnt/home
#    copy key files
cp -a ./p2.mantra ./crypto_keyfile.bin /mnt/

# --- install alpinelinux on /mnt ------------------------------------
# this part can be automated by creating and editing an "answer file"
setup-timezone  # UTC
setup-alpine -q # us us-altgr-intl
setup-sshd      # openssh
setup-ntp       # chrony
apk add efibootmgr grub-efi
export BOOTLOADER=grub
export USE_EFI=1
setup-disk -m sys /mnt

# --- now play the chroot trick :-) ----------------------------------
#     a number of things need to be configured in the newly
#     installed system. So we make it functional ...
mount -t proc /proc /mnt/proc
mount --rbind /dev /mnt/dev
mount --make-rslave /mnt/dev
mount --rbind /sys /mnt/sys

#    enter chroot
chroot /mnt
PS1="(chroot) $PS1"
apk add grub grub-efi efibootmgr # probably unneeded
apk del syslinux                 # optional

#    create a configuration to generate a grub.cfg file
blkid /dev/sda3 # get UUID, is used in next step
cat <<EOF >/etc/default/grub
GRUB_DISTRIBUTOR="Alpine"
GRUB_TIMEOUT=5
GRUB_DISABLE_SUBMENU=y
GRUB_DISABLE_RECOVERY=true
GRUB_DISABLE_OS_PROBER=y
GRUB_PRELOAD_MODULES="part_gpt cryptodisk luks ext2"
GRUB_ENABLE_CRYPTODISK=y
GRUB_DISABLE_LINUX_PARTUUID=true
GRUB_DISABLE_LINUX_UUID=true
GRUB_CMDLINE_LINUX_DEFAULT="modules=sd-mod,usb-storage,ext4 quiet rootfstype=ext4 cryptroot=UUID=<UUID-OF-sda3> cryptdm=sda3_crypt cryptkey"
EOF
#    generate grub.cfg
grub-mkconfig -o /boot/grub/grub.cfg
#    install grub -> /boot/efi/EFI/alpine/grubx64.efi
grub-install --target=x86_64-efi --efi-directory=/boot/efi

#    generate initramfs; this stage must decrypt sda3, start the
#    volume group and mount lvroot; therefore a few more features
#    need to be enabled; cryptkey will include file /crypto_keyfile.bin
cat <<EOF >/etc/mkinitfs/mkinitfs.conf
features="ata base ide scsi usb virtio ext2 ext4 lvm cryptsetup cryptkey"
EOF
#    refresh initramfs-lts
mkinitfs -c /etc/mkinitfs/mkinitfs.conf -b / $(ls /lib/modules/)

#    enable cryptsetup to open these partitions in the booted system
cat <<EOF >> /etc/conf.d/dmcrypt
target='sda2_crypt'
source='/dev/sda2'
key='/p2.mantra'

target='sda3_crypt'
source='/dev/sda3'
key='/crypto_keyfile.bin'

# fin
EOF

#     make sda2_crypt available during boot
apk add cryptsetup-openrc
rc-update add dmcrypt boot
rc-update add lvm sysinit # ??? sysvinit or boot?

#     edit /etc/fstab; "lsblk -f" is your friend
cat <<EOF >> /etc/fstab
UUID=<UUID-of-sda2_crypt> /boot ext4 rw,relatime 0 2
UUIE=<UUID-of-lvswap>     swap  swap defaults    0 0
EOF
rc-update add swap boot

# --- secureboot -----------------------------------------------------
#     first a set of properly formatted key,cert files are generated
#     files: {KEK,PK,db}.{auth,cer,crt,esl,key}
apk add efi-mkkeys sbsigntool
mkdir /etc/uefi-keys
efi-mkkeys -s "Your Name" -o /etc/uefi-keys
cp /etc/uefi-keys/*.auth /boot/efi/

#     there is a funny error, which can be worked around
sed -i 's/SecureBoot/SecureB00t/' /boot/efi/EFI/alpine/grubx64.efi
#     now we need to sign grub
sbsign --key /etc/uefi-keys/db.key --cert /etc/uefi-keys/db.crt /boot/efi/EFI/alpine/grubx64.efi
mv /boot/efi/EFI/alpine/grubx64.efi.signed /boot/efi/EFI/alpine/grubx64.efi

exit
#    chroot exited
umount -l /mnt/dev
umount -l /mnt/proc
umount -l /mnt/sys

umount /mnt/boot/efi
umount /mnt/boot
umount /mnt/home
umount /mnt
swapoff /dev/mapper/vg0-lvswap
vgchange -a n
cryptsetup close sda3_crypt
cryptsetup close sda2_crypt

reboot


# remove the usb stick

# enter UEFI
# - enable secure boot
# - enable custom key management
# - replace keys db KEK PK (apparently order is significant) with the
#   correspondig *.auth files
# - create a uefi boot entry, if not there
# - save + exit
#
# Now booting up should
# - show grub asking for the mantra of sda2
# - show the grub.cfg menu
# - boot alpine linux and start the system without any further questions
#
# Time to set a password on UEFI
# Time for post-install activities
#
# Congrats!