Optional Configuration

Skip to bootloader section if no optional configuration is needed.

Boot environment manager

A boot environment is a dataset which contains a bootable instance of an operating system. Within the context of this installation, boot environments can be created on-the-fly to preserve root file system states before pacman transactions.

Install rozb3-pac pacman hook and bieaz from AUR to create boot environments. Prebuilt packages are also available.

Supply password with SSH

  1. Install mkinitcpio tools:

    pacman -S mkinitcpio-netconf mkinitcpio-dropbear openssh
    
  2. Store public keys in /etc/dropbear/root_key:

    vi /etc/dropbear/root_key
    
  3. Edit mkinitcpio:

    tee /etc/mkinitcpio.conf <<- 'EOF'
    HOOKS=(base udev autodetect modconf block keyboard netconf dropbear zfsencryptssh zfs filesystems)
    EOF
    
  4. Add ip= to kernel command line:

    # example DHCP
    echo 'GRUB_CMDLINE_LINUX="ip=::::::dhcp"' >> /etc/default/grub
    

    Details for ip= can be found at here.

  5. Generate host keys:

    ssh-keygen -Am pem
    dropbearconvert openssh dropbear /etc/ssh/ssh_host_ed25519_key /etc/dropbear/dropbear_ed25519_host_key
    

    mkinitcpio-dropbear lacks support for converting ed25519 host key, see this pull request.

Encrypt boot pool

Note: This will disable password with SSH. The password previously set for root pool will be replaced by keyfile, embedded in initrd.

  1. LUKS password:

    LUKS_PWD=secure-passwd
    

    You will need to enter the same password for each disk at boot. As root pool key is protected by this password, the previous warning about password strength still apply.

    Double-check password here. Complete reinstallation is needed if entered wrong.

  2. Create encryption keys:

    mkdir /etc/cryptkey.d/
    chmod 700 /etc/cryptkey.d/
    dd bs=32 count=1 if=/dev/urandom of=/etc/cryptkey.d/rpool_$INST_UUID-${INST_ID}-key-zfs
    dd bs=32 count=1 if=/dev/urandom of=/etc/cryptkey.d/bpool_$INST_UUID-key-luks
    chmod u=r,go= /etc/cryptkey.d/*
    
  3. Backup boot pool:

    zfs snapshot -r bpool_$INST_UUID/$INST_ID@pre-luks
    zfs send -Rv bpool_$INST_UUID/$INST_ID@pre-luks > /root/bpool_$INST_UUID-${INST_ID}-pre-luks
    
  4. Unmount EFI partition:

    umount /boot/efi
    
    for i in ${DISK}; do
     umount /boot/efis/${i##*/}-part1
    done
    
  5. Destroy boot pool:

    zpool destroy bpool_$INST_UUID
    
  6. Create LUKS containers:

    for i in ${DISK}; do
     cryptsetup luksFormat -q --type luks1 --key-file /etc/cryptkey.d/bpool_$INST_UUID-key-luks $i-part2
     echo $LUKS_PWD | cryptsetup luksAddKey --key-file /etc/cryptkey.d/bpool_$INST_UUID-key-luks $i-part2
     cryptsetup open ${i}-part2 ${i##*/}-part2-luks-bpool_$INST_UUID --key-file /etc/cryptkey.d/bpool_$INST_UUID-key-luks
     echo ${i##*/}-part2-luks-bpool_$INST_UUID ${i}-part2 /etc/cryptkey.d/bpool_$INST_UUID-key-luks discard >> /etc/crypttab
    done
    

    GRUB 2.06 still does not have complete support for LUKS2, LUKS1 is used instead.

  7. Embed key file in initrd:

    echo 'FILES=(/etc/cryptkey.d/* )' >> /etc/mkinitcpio.conf
    
  8. Recreate boot pool with mappers as vdev:

    zpool create \
        -o compatibility=grub2 \
        -o ashift=12 \
        -o autotrim=on \
        -O acltype=posixacl \
        -O canmount=off \
        -O compression=lz4 \
        -O devices=off \
        -O normalization=formD \
        -O relatime=on \
        -O xattr=sa \
        -O mountpoint=/boot \
        bpool_$INST_UUID \
        $INST_VDEV \
        $(for i in ${DISK}; do
           printf "/dev/mapper/${i##*/}-part2-luks-bpool_$INST_UUID ";
          done)
    
  9. Restore boot pool backup:

    zfs recv bpool_${INST_UUID}/${INST_ID} < /root/bpool_$INST_UUID-${INST_ID}-pre-luks
    rm /root/bpool_$INST_UUID-${INST_ID}-pre-luks
    
  10. Mount boot dataset and EFI partitions:

    mount /boot
    mount /boot/efi
    
    for i in ${DISK}; do
     mount /boot/efis/${i##*/}-part1
    done
    
  11. As keys are stored in initrd, set secure permissions for /boot:

    chmod 700 /boot
    
  12. Change root pool password to key file:

    zfs change-key -l \
    -o keylocation=file:///etc/cryptkey.d/rpool_$INST_UUID-${INST_ID}-key-zfs \
    -o keyformat=raw \
    rpool_$INST_UUID/$INST_ID
    
  13. Import encrypted boot pool from /dev/mapper:

    curl -L https://git.io/Jsfwj > /etc/systemd/system/zfs-import-bpool-mapper.service
    systemctl enable zfs-import-bpool-mapper.service
    
  14. Remove zfsencryptssh hook. Encrypted boot pool is incompatible with password by SSH:

    sed -i 's|zfsencryptssh||g' /etc/mkinitcpio.conf
    

    If zfsencryptssh is not removed, initrd will stuck at fail to load key material and fail to boot.

  15. Enable GRUB cryptodisk:

    echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
    
  16. Important: Back up root dataset key /etc/cryptkey.d/rpool_$INST_UUID-${INST_ID}-key-zfs to a secure location.

    In the possible event of LUKS container corruption, data on root set will only be available with this key.

Persistent swap and hibernation

  1. Optional: enable persistent swap partition. By default encryption key of swap partition is discarded on reboot:

    INST_SWAPKEY=/etc/cryptkey.d/${INST_PRIMARY_DISK##*/}-part4-key-luks-swap
    INST_SWAPMAPPER=${INST_PRIMARY_DISK##*/}-part4-luks-swap
    
    # fstab
    # remove all existing swap entries
    sed -i '/ none swap defaults 0 0/d' /etc/fstab
    # add single swap entry for LUKS encrypted swap partition
    echo "/dev/mapper/${INST_SWAPMAPPER} none swap defaults 0 0" >> /etc/fstab
    
    # comment out entry in crypttab
    sed -i "s|^${INST_PRIMARY_DISK##*/}-part4-swap|#${INST_PRIMARY_DISK##*/}-part4-swap|" /etc/crypttab
    
    # create key and format partition as LUKS container
    dd bs=32 count=1 if=/dev/urandom of=${INST_SWAPKEY};
    chmod u=r,go= /etc/cryptkey.d/*
    cryptsetup luksFormat -q --type luks2 --key-file ${INST_SWAPKEY} ${INST_PRIMARY_DISK}-part4
    cryptsetup luksOpen ${INST_PRIMARY_DISK}-part4 ${INST_SWAPMAPPER} --key-file ${INST_SWAPKEY}
    
    # initialize swap space
    mkswap /dev/mapper/${INST_SWAPMAPPER}
    
  2. Optional: after enabling persistent swap partition, enable hibernation:

    # add hook in initrd
    sed -i 's| zfs | encrypt resume zfs |' /etc/mkinitcpio.conf
    # add kernel cmdline to decrypt swap in initrd
    echo "GRUB_CMDLINE_LINUX=\" \
    zfs_import_dir=${INST_PRIMARY_DISK%/*} \
    cryptdevice=PARTUUID=$(blkid -s PARTUUID -o value ${INST_PRIMARY_DISK}-part4):${INST_SWAPMAPPER}:allow-discards \
    cryptkey=rootfs:${INST_SWAPKEY} \
    resume=/dev/mapper/${INST_SWAPMAPPER}\"" \
    >> /etc/default/grub
    

    Note that hibernation might not work with discrete graphics, virtio graphics or AMD APU integrated graphics. This is not specific to this guide.

    Computer must resume from a continuous swap space, resume from multiple swap partitions is not supported.

    encrypt hook can only decrypt one container at boot. sd-encrypt can decrypt multiple devices but is not compatible with zfs hook.

    Do not touch anything on disk while the computer is in hibernation, see kernel documentation.

Boot Live ISO with GRUB

GRUB can be configured to boot ISO file directly.

In this section, we will download Live ISO to ESP and configure GRUB to boot from it. This enables system recovery and re-installation.

  1. Download Live iso to EFI system partition:

    mkdir /boot/efi/iso
    cd /boot/efi/iso
    # select a mirror # curl -O https://mirrors.ocf.berkeley.edu/archlinux/iso/2021.09.01/archlinux-2021.09.01-x86_64.iso
    curl -O https://archlinux.org/iso/2021.09.01/archlinux-2021.09.01-x86_64.iso.sig
    gpg --auto-key-retrieve --verify archlinux-2021.09.01-x86_64.iso.sig
    

    Additionally you can build your own live image with archiso package.

    GRUB supports verifying checksum. See manual page for details.

  2. Add custom GRUB entry for /boot/efi/iso/archlinux-*.iso:

    curl -L https://git.io/Jsfr3 > /etc/grub.d/43_archiso
    chmod +x /etc/grub.d/43_archiso
    

    You can also boot Live ISO for other distros, see glim configurations.

    ISO is not mirrored to other devices due to its size. Change $ESP_MNT to adapt to other ESP.

  3. Generate grub.cfg in the next step. If a new file has been added later, regenerate grub.cfg.