GRUB boot live ISO

customize esp, bpool, swap, reserve size
customize previously hard-coded '/sys/'
add INST_ID to chroot variables
append INST_ID to zfs key name

Signed-off-by: Maurice Zhou <ja@apvc.uk>
This commit is contained in:
Maurice Zhou
2021-05-08 03:33:39 +08:00
committed by Richard Laager
parent 16cb298acf
commit 578f2901a7

View File

@@ -12,8 +12,11 @@ Overview
Caution
~~~~~~~
- This guide uses entire physical disks.
- Multiple systems on one disk is not supported.
- This guide wipes entire physical disks.
- For advanced users, distros with ZFS support (Linux, FreeBSD)
can be manually installed in datasets.
- Other operating system without ZFS support can be installed
in the reserved space.
- Target disk will be wiped. Back up your data before continuing.
- The target system, virtual or physical, must have at least 2GB RAM,
or the DKMS module might fail to build.
@@ -221,11 +224,15 @@ Preparations
INST_UUID=$(dd if=/dev/urandom bs=1 count=100 2>/dev/null | tr -dc 'a-z0-9' | cut -c-6)
#. Identify this installation in ZFS filesystem path::
INST_ID=arch
#. Target disk
List available disks with::
fdisk -l /dev/disk/by-id/*
ls /dev/disk/by-id/*
If using virtio as disk bus, use
``/dev/disk/by-path/*`` or ``/dev/vd*``.
@@ -249,6 +256,28 @@ Preparations
hook treats ``:`` as argument separator without a means to
escape this character.
#. Set ESP size. ESP contains Live ISO for recovery,
as described `below <#enable-recovery-with-local-live-iso>`__::
INST_PARTSIZE_ESP=4 # in GB
#INST_PARTSIZE_ESP=1 # if local recovery is not needed
#. Set boot pool size. To avoid running out of space while using
boot environments, the minimum is 4GB. Adjust the size if you
intend to use multiple kernel/distros::
INST_PARTSIZE_BPOOL=4
#. Set swap size. It's `recommended <https://chrisdown.name/2018/01/02/in-defence-of-swap.html>`__
to setup a swap partition. If you intend to use hibernation,
the minimum should be no less than RAM size. Skip if swap is not needed::
INST_PARTSIZE_SWAP=8
#. Reserve space at disk end, skip if not needed::
INST_PARTSIZE_RESERVE=
System Installation
-------------------
@@ -260,31 +289,28 @@ System Installation
sgdisk --zap-all $i
# EFI system partition; must be created
sgdisk -n1:1M:+1G -t1:EF00 $i
sgdisk -n1:1M:+${INST_PARTSIZE_ESP}G -t1:EF00 $i
# Boot pool partition
sgdisk -n2:0:+4G -t2:BE00 $i
sgdisk -n2:0:+${INST_PARTSIZE_BPOOL}G -t2:BE00 $i
# with swap
sgdisk -n3:0:-8G -t3:BF00 $i
sgdisk -n4:0:0 -t4:8200 $i
# swap
if [ "${INST_PARTSIZE_SWAP}" != "" ]; then
sgdisk -n4:0:+${INST_PARTSIZE_SWAP}G -t4:8200 $i
fi
# without swap (not recommended)
#sgdisk -n3:0:0 -t3:BF00 $i
# root pool partition
if [ "${INST_PARTSIZE_RESERVE}" = "" ]; then
sgdisk -n3:0:0 -t3:BF00 $i
else
sgdisk -n3:0:-${INST_PARTSIZE_RESERVE}G -t3:BF00 $i
fi
# with BIOS booting; can co-exist with EFI
sgdisk -a1 -n5:24K:+1000K -t5:EF02 $i
done
It's `recommended <https://chrisdown.name/2018/01/02/in-defence-of-swap.html>`__
to create a swap partition.
Adjust the swap partition size to your needs.
If hibernation is needed,
swap size should be same or larger than RAM.
Check RAM size with ``free -h``.
#. When creating pools, for single disk installation, omit topology specification
``mirror``::
@@ -436,7 +462,7 @@ System Installation
zfs create \
-o canmount=off \
-o mountpoint=none \
bpool_$INST_UUID/sys
bpool_$INST_UUID/$INST_ID
#. Create system root container:
@@ -448,7 +474,7 @@ System Installation
zfs create \
-o canmount=off \
-o mountpoint=none \
rpool_$INST_UUID/sys
rpool_$INST_UUID/$INST_ID
- Encrypted:
@@ -485,57 +511,57 @@ System Installation
-o encryption=on \
-o keylocation=prompt \
-o keyformat=passphrase \
rpool_$INST_UUID/sys
rpool_$INST_UUID/$INST_ID
#. Create container datasets::
zfs create -o canmount=off -o mountpoint=none bpool_$INST_UUID/sys/BOOT
zfs create -o canmount=off -o mountpoint=none rpool_$INST_UUID/sys/ROOT
zfs create -o canmount=off -o mountpoint=none rpool_$INST_UUID/sys/DATA
zfs create -o canmount=off -o mountpoint=none bpool_$INST_UUID/$INST_ID/BOOT
zfs create -o canmount=off -o mountpoint=none rpool_$INST_UUID/$INST_ID/ROOT
zfs create -o canmount=off -o mountpoint=none rpool_$INST_UUID/$INST_ID/DATA
#. Create root and boot filesystem datasets::
zfs create -o mountpoint=legacy -o canmount=noauto bpool_$INST_UUID/sys/BOOT/default
zfs create -o mountpoint=/ -o canmount=off rpool_$INST_UUID/sys/DATA/default
zfs create -o mountpoint=/ -o canmount=noauto rpool_$INST_UUID/sys/ROOT/default
zfs create -o mountpoint=legacy -o canmount=noauto bpool_$INST_UUID/$INST_ID/BOOT/default
zfs create -o mountpoint=/ -o canmount=off rpool_$INST_UUID/$INST_ID/DATA/default
zfs create -o mountpoint=/ -o canmount=noauto rpool_$INST_UUID/$INST_ID/ROOT/default
#. Mount root and boot filesystem datasets::
zfs mount rpool_$INST_UUID/sys/ROOT/default
zfs mount rpool_$INST_UUID/$INST_ID/ROOT/default
mkdir /mnt/boot
mount -t zfs bpool_$INST_UUID/sys/BOOT/default /mnt/boot
mount -t zfs bpool_$INST_UUID/$INST_ID/BOOT/default /mnt/boot
#. Create datasets to separate user data from root filesystem::
# create containers
for i in {usr,var,var/lib};
do
zfs create -o canmount=off rpool_$INST_UUID/sys/DATA/default/$i
zfs create -o canmount=off rpool_$INST_UUID/$INST_ID/DATA/default/$i
done
for i in {home,root,srv,usr/local,var/log,var/spool};
do
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/$i
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/$i
done
chmod 750 /mnt/root
#. Create optional user data datasets to omit data from rollback::
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/games
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/www
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/games
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/www
# for GNOME
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/AccountsService
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/AccountsService
# for Docker
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/docker
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/docker
# for NFS
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/nfs
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/nfs
# for LXC
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/lxc
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/lxc
# for LibVirt
zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/libvirt
zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/libvirt
##other application
# zfs create -o canmount=on rpool_$INST_UUID/sys/DATA/default/var/lib/$name
# zfs create -o canmount=on rpool_$INST_UUID/$INST_ID/DATA/default/var/lib/$name
Add other datasets when needed, such as PostgreSQL.
@@ -609,7 +635,7 @@ System Configuration
#. Generate fstab::
echo bpool_$INST_UUID/sys/BOOT/default /boot zfs rw,xattr,posixacl 0 0 >> /mnt/etc/fstab
echo bpool_$INST_UUID/$INST_ID/BOOT/default /boot zfs rw,xattr,posixacl 0 0 >> /mnt/etc/fstab
for i in ${DISK[@]}; do
echo UUID=$(blkid -s UUID -o value ${i}-part1) /boot/efis/${i##*/}-part1 vfat \
@@ -678,7 +704,8 @@ System Configuration
echo "INST_PRIMARY_DISK=$INST_PRIMARY_DISK
INST_LINVAR=$INST_LINVAR
INST_UUID=$INST_UUID" > /mnt/root/chroot
INST_UUID=$INST_UUID
INST_ID=$INST_ID" > /mnt/root/chroot
echo DISK=\($(for i in ${DISK[@]}; do printf "$i "; done)\) >> /mnt/root/chroot
arch-chroot /mnt bash --login
@@ -728,7 +755,7 @@ Optional Configuration
Skip to `bootloader <#bootloader>`__ section if
no optional configuration is needed.
Boot Environment Manager
Boot environment manager
~~~~~~~~~~~~~~~~~~~~~~~~
A boot environment is a dataset which contains a bootable
@@ -785,7 +812,7 @@ Supply password with SSH
mkinitcpio -P
Encrypted boot pool
Encrypt boot pool
~~~~~~~~~~~~~~~~~~~
If encryption is enabled earlier, boot pool can be optionally encrypted.
@@ -803,15 +830,15 @@ and persistent encrypted swap.
mkdir /etc/cryptkey.d/
chmod 700 /etc/cryptkey.d/
dd bs=32 count=1 if=/dev/urandom of=/etc/cryptkey.d/rpool_$INST_UUID-key-zfs
dd bs=32 count=1 if=/dev/urandom of=/etc/cryptkey.d/rpool_$INST_UUID-${INST_ID}-key-zfs
for i in ${DISK[@]}; do
dd bs=32 count=1 if=/dev/urandom of=/etc/cryptkey.d/${i##*/}-part2-bpool_$INST_UUID-key-luks
done
#. Backup boot pool::
zfs snapshot -r bpool_$INST_UUID/sys@pre-luks
zfs send -Rv bpool_$INST_UUID/sys@pre-luks > /root/bpool_$INST_UUID-pre-luks
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
#. Unmount EFI partition::
@@ -863,8 +890,8 @@ and persistent encrypted swap.
#. Restore boot pool backup::
cat /root/bpool_$INST_UUID-pre-luks | zfs recv bpool_$INST_UUID/sys
rm /root/bpool_$INST_UUID-pre-luks
zfs recv bpool_${INST_UUID}/${INST_ID} < /root/bpool_$INST_UUID-${INST_ID}-pre-luks
rm /root/bpool_$INST_UUID-${INST_ID}-pre-luks
#. Mount boot dataset and EFI partitions::
@@ -878,9 +905,9 @@ and persistent encrypted swap.
#. Change root pool password to key file::
zfs change-key -l \
-o keylocation=file:///etc/cryptkey.d/rpool_$INST_UUID-key-zfs \
-o keylocation=file:///etc/cryptkey.d/rpool_$INST_UUID-${INST_ID}-key-zfs \
-o keyformat=raw \
rpool_$INST_UUID/sys
rpool_$INST_UUID/$INST_ID
#. Import encrypted boot pool from ``/dev/mapper``::
@@ -927,7 +954,7 @@ and persistent encrypted swap.
echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
#. **Important**: Back up root dataset key ``/etc/cryptkey.d/rpool_$INST_UUID-key-zfs``
#. **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,
@@ -984,6 +1011,85 @@ and persistent encrypted swap.
in hibernation, see `kernel documentation
<https://www.kernel.org/doc/html/latest/power/swsusp.html>`__.
Enable recovery with local live ISO
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GRUB `can be configured <https://wiki.archlinux.org/title/Multiboot_USB_drive>`__ to boot ISO file directly:
- GRUB mounts ISO as `loopback device <https://www.gnu.org/software/grub/manual/grub/html_node/loopback.html>`__.
- GRUB loads vmlinuz and initrd from loopback device.
- The location of ISO is passed to initrd `archiso_loop_mnt
<https://gitlab.archlinux.org/archlinux/archiso/-/blob/master/archiso/initcpio/hooks/archiso_loop_mnt>`__
hook.
In this section, we will download Live ISO to EFI system partition and configure GRUB to
boot from it. This enables system recovery and re-installation.
#. Download Live iso to EFI system partition::
mkdir /boot/efi/iso
cd /boot/efi/iso
curl -O https://mirrors.ocf.berkeley.edu/archlinux/iso/2021.05.01/archlinux-2021.05.01-x86_64.iso
curl -O https://archlinux.org/iso/2021.05.01/archlinux-2021.05.01-x86_64.iso.sig
gpg --auto-key-retrieve --verify archlinux-2021.05.01-x86_64.iso.sig
Additionally you can build your own live image
with `archiso package <https://gitlab.archlinux.org/archlinux/archiso>`__.
An unofficial live image with built-in ZFS support is available
`here <https://gitlab.com/m_zhou/archiso>`__.
GRUB supports verifying checksum.
See `manual page
<https://www.gnu.org/software/grub/manual/grub/html_node/Command_002dline-and-menu-entry-commands.html#Command_002dline-and-menu-entry-commands>`__
for details.
#. Add custom GRUB entry for ``/boot/efi/iso/archlinux-*.iso``::
tee /etc/grub.d/43_archiso <<-'FOE'
#!/bin/sh
ESP_MNT=/boot/efi
ISO_REL=/iso
ISO_PATH=${ESP_MNT}/${ISO_REL}
# df command needs warm up due to systemd mount-on-demand
ls $ISO_PATH 1> /dev/null
ESP_UUID=$(blkid -s UUID -o value $(df --output=source ${ISO_PATH} | tail -n +2))
cat <<EOF
submenu 'archiso' {
configfile \$prefix/archiso.cfg
}
EOF
tee /boot/grub/archiso.cfg 1> /dev/null <<EOF
insmod search_fs_uuid
set isorootuuid=$ESP_UUID
search --fs-uuid --no-floppy --set=isopart \$isorootuuid
set isopath=$ISO_REL
EOF
ISO_NUM=0
for isofile in $ISO_PATH/archlinux-*.iso; do
if [ "$ISO_NUM" -gt 300 ]; then break; fi
isoname=${isofile##*/}
tee -a /boot/grub/archiso.cfg 1> /dev/null <<EOF
menuentry "$isoname" {
loopback loop0 (\$isopart)\$isopath/$isoname
linux (loop0)/arch/boot/x86_64/vmlinuz-linux earlymodules=loop img_dev=/dev/disk/by-uuid/\$isorootuuid img_loop=\$isopath/$isoname
initrd (loop0)/arch/boot/intel-ucode.img (loop0)/arch/boot/amd-ucode.img (loop0)/arch/boot/x86_64/initramfs-linux.img
}
EOF
ISO_NUM=$(( $ISO_NUM + 1 ))
done
FOE
chmod +x /etc/grub.d/43_archiso
You can also boot Live ISO for other distros, see `glim
<https://github.com/thias/glim/tree/master/grub2>`__
configurations.
ISO is not mirrored to other devices due to its size.
Change ``$ESP_MNT`` to adapt to other ESP.
#. Generate ``grub.cfg`` in the next step. If a new file
has been added later, regenerate ``grub.cfg``.
Bootloader
----------------------------
@@ -1076,6 +1182,8 @@ GRUB Installation
# OK -> Enroll Hash -> loader.efi -> Yes -> Reboot System -> Yes
Re-enrolling the hash is needed if GRUB has been reinstalled.
#. If using multi-disk setup, mirror EFI system partitions::
# mirror ESP content
@@ -1118,8 +1226,8 @@ Finish Installation
#. Take a snapshot of the clean installation for future use::
zfs snapshot -r rpool_$INST_UUID/sys@install
zfs snapshot -r bpool_$INST_UUID/sys@install
zfs snapshot -r rpool_$INST_UUID/$INST_ID@install
zfs snapshot -r bpool_$INST_UUID/$INST_ID@install
#. Unmount EFI system partition::
@@ -1218,7 +1326,7 @@ This section is also applicable if you are in
time 2021-05-03 12:14:08 Monday, UUID f14d7bdf89fe21fb - Sector size 512B -
Total size 4192256KiB
#. List boot environments nested inside ``bpool/sys/BOOT``::
#. List boot environments nested inside ``bpool/$INST_ID/BOOT``::
grub> ls (crypto0)/sys/BOOT
@/ default/ be0/
@@ -1300,11 +1408,11 @@ Recovery
If using password::
zfs load-key rpool_$INST_UUID/sys
zfs load-key rpool_$INST_UUID/$INST_ID
If using keyfile::
zfs load-key -L file:///path/to/keyfile rpool_$INST_UUID/sys
zfs load-key -L file:///path/to/keyfile rpool_$INST_UUID/$INST_ID
#. Find the current boot environment::
@@ -1313,7 +1421,7 @@ Recovery
#. Mount root filesystem::
zfs mount rpool_$INST_UUID/sys/ROOT/$BE
zfs mount rpool_$INST_UUID/$INST_ID/ROOT/$BE
#. chroot into the system::