Skip to content

Instantly share code, notes, and snippets.

@xunil154
Created August 20, 2019 19:10
Show Gist options
  • Save xunil154/e7292db25428a26cdfca4d683a9bcb8d to your computer and use it in GitHub Desktop.
Save xunil154/e7292db25428a26cdfca4d683a9bcb8d to your computer and use it in GitHub Desktop.
ArchLinux install script with ZFS as the root, all sitting on LUKS encryption, using systemd-boot for UEFI (ArchLinux with ZFS Root on LUKS with UEFI)
#!/bin/sh
SWAPSIZE=3 # IN GB
SYSTEM_NAME=aerozine # Hostname and used in ZFS structure (zroot/sys/${SYSTEM_NAME}/ROOT/default)
USER=xunil # Initial admin user for the system
# Archlinux on ZFS Root on LUKS setup script (v1.0)
#
# USAGE: ./ArchLinux_ZFS_LUKS_Setup.sh /dev/sda
#
# Author: Chris Campbell (https://gist.github.com/xunil154)
#
# With LOTS of help from online docs:
# ZFS Guide: https://ramsdenj.com/2016/06/23/arch-linux-on-zfs-part-2-installation.html
# https://wiki.archlinux.org/index.php/Installing_Arch_Linux_on_ZFS#Configure_the_root_filesystem
# https://aur.archlinux.org/packages/mkinitcpio-sd-zfs/
# https://wiki.archlinux.org/index.php/Dm-crypt/Encrypting_an_entire_system
#
# The key to making this all work was 'mkinitcpio-sd-zfs' and the 'root=zfs:zroot/sys/${SYSTEM_NAME}/ROOT/default'
# line in the systemd-boot options. Wiki's say to use just 'zfs=zroot/sys/${SYSTEM_NAME}/ROOT/default' which
# is not honnored through sd-zfs
#
# This script will auto-partition the disk with [ /boot 1GiB FAT ][ LUKS for the rest of the disk ]
if [ $# -ne 1 ]
then
echo "Usage: $0 <device>"
echo "Where <device> is sdX"
echo "Example: $0 sda"
echo "do NOT use the /dev/disk/by-id/.... the script will find that automatically"
exit 1
fi
#### START SCRIPT CODE ####
ee(){
if [ $1 -ne 0 ]
then
echo "[ERROR] ${2}"
exit $1
fi
}
HOOKS="base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt sd-zfs usr filesystems shutdown"
echo "############# Disk Configuration #################"
userdev=$(basename "$1")
DISK=$(find /dev/disk/by-id -type l -printf "%f:%l\n" | grep -E "${userdev}$" | cut -d':' -f 1)
if [ -z "${DISK}" ]
then
ee 1 "Could not lookup disk by id... are you sure '${userdev}' is correct?"
fi
DISKDEV="/dev/disk/by-id/${DISK}"
echo "USING DISK: ${DISKDEV}"
echo "If this is wrong, press Ctrl^C within 15 seconds"
sleep 5
echo "10s"
sleep 5
echo "5s"
sleep 5
echo "Destroying existing partitions"
sgdisk --zap-all ${DISKDEV}
sgdisk -p ${DISKDEV}
echo "Creating the following partition scheme"
echo "[ EFI BOOT 1G ][ LUKS * ]"
#parted --script "$DISKDEV" mklabel gpt mkpart primary fat32 1MiB 2GiB set 1 esp on name 1 efi mkpart primary 2GiB 100% name 2 sys
sgdisk --new=0:1M:1G "${DISKDEV}"
sgdisk --new=0:0:0 ${DISKDEV}
sgdisk --typecode=1:EF00 ${DISKDEV}
sgdisk --typecode=2:8309 ${DISKDEV}
sgdisk --change-name=1:efi
sgdisk --change-name=2:sys
sgdisk -p ${DISKDEV}
echo "Partitions created, reloading table on ${DISKDEV}"
partprobe ${DISKDEV}
sleep 2
BOOT="${DISK}-part1"
BOOTDEV=/dev/disk/by-id/$BOOT
CRYPT="${DISK}-part2"
CRYPTDEV=/dev/disk/by-id/$CRYPT
BOOTUUID=$(blkid -o export $BOOTDEV | grep -E '^UUID' | cut -d'=' -f2)
echo "Creating FAT32 FS on ${BOOTDEV}"
ls -l "${BOOTDEV}"
mkfs.fat -F32 ${BOOTDEV}
ee $? "Failed to format boot partition ${BOOTDEV}"
echo "############# LUKS Setup ##############"
echo "Using $CRYPTDEV as LUKS partition"
echo "Done, you have 10s to review and cancel..."
sleep 5
echo "5s"
sleep 2
echo "3s"
sleep 3
modprobe dm-crypt
modprobe dm-mod
cryptsetup luksFormat -v --cipher aes-xts-plain64 -s 512 -h sha512 --iter-time 3000 --use-random $CRYPTDEV
ee $? "Failed to create LUKS"
echo "Mounting $CRYPTDEV... you will need to enter the passphrase again"
cryptsetup open $CRYPTDEV luks_zfs
ee $? "ERROR: Failed to decrypt luks"
echo "############## ZFS Setup #################"
ZFSDISK=$(ls /dev/disk/by-id | grep CRYPT)
ZFSDISKDEV=/dev/disk/by-id/$ZFSDISK
modprobe zfs
ee $? "Failed to load ZFS module"
echo "Creating zroot pool"
zpool create -o ashift=12 -f zroot -o altroot=/mnt -m none $ZFSDISKDEV
ee $? "Failed to create 'zroot' zpool"
zfs set compression=on zroot
zfs set atime=on zroot
zfs set relatime=on zroot
SYS_ROOT=zroot/sys
zfs create -o mountpoint=none -p ${SYS_ROOT}/${SYSTEM_NAME}
zfs create -o mountpoint=none ${SYS_ROOT}/${SYSTEM_NAME}/ROOT
zfs create -o mountpoint=/ ${SYS_ROOT}/${SYSTEM_NAME}/ROOT/default
zpool set bootfs=${SYS_ROOT}/${SYSTEM_NAME}/ROOT/default
zfs create -o mountpoint=legacy ${SYS_ROOT}/${SYSTEM_NAME}/home
zfs create -V ${SWAPSIZE}G -b $(getconf PAGESIZE) -o logbias=throughput -o sync=always -o primarycache=metadata -o com.sun:auto-snapshot=false zroot/swap
mkswap -f /dev/zvol/zroot/swap
ee $? "Failed to create SWAP"
zfs create -o canmount=off -o mountpoint=/var -o xattr=sa ${SYS_ROOT}/${SYSTEM_NAME}/var
zfs create -o canmount=off -o mountpoint=/var/lib ${SYS_ROOT}/${SYSTEM_NAME}/var/lib
zfs create -o canmount=off -o mountpoint=/var/lib/systemd ${SYS_ROOT}/${SYSTEM_NAME}/var/lib/systemd
zfs create -o canmount=off -o mountpoint=/usr ${SYS_ROOT}/${SYSTEM_NAME}/usr
SYSTEM_DATASETS='var/lib/systemd/coredump var/log var/log/journal var/lib/lxc var/lib/lxd var/lib/machines var/lib/libvirt var/cache usr/local'
for ds in ${SYSTEM_DATASETS}; do zfs create -o mountpoint=legacy ${SYS_ROOT}/${SYSTEM_NAME}/${ds}; done
zfs create -o mountpoint=legacy -o acltype=posixacl ${SYS_ROOT}/${SYSTEM_NAME}/var/log/journal
echo "All datasets created.... unmounting for instillation"
zfs umount -a
ee $? "Failed to unmount ZFS filesystmes"
zpool export zroot
ee $? "Failed to export zroot"
echo "################### Instillation Preparation ###########################"
echo "Importing zpool"
zpool import -d $ZFSDISKDEV -R /mnt zroot
ee $? "Failed to import zroot pool"
mkdir -p /etc/zfs
mkdir -p /mnt/etc/zfs
zpool set cachefile=/etc/zfs/zpool.cache zroot
mv /etc/zfs/zpool.cache /mnt/etc/zfs/zpool.cache
ln -s /mnt/etc/zfs/zpool.cache /etc/zfs/zpool.cache
echo "KEYMAP=us" > /mnt/etc/vconsole.conf
mkdir -p /mnt/boot
mount $BOOTDEV /mnt/boot
ee $? "Failed to mount ${BOOTDEV} to /mnt/boot"
DIRS='/home /var/lib/systemd/coredump /var/log /var/log/journal /var/lib/lxc /var/lib/machines /var/lib/libvert /var/cache /usr/local'
for d in ${DIRS}; do mkdir -p "/mnt${d}"; mount -t zfs ${SYS_ROOT}/${SYSTEM_NAME}${d} /mnt${d}; done
genfstab -U -p /mnt >> /mnt/etc/fstab
echo "/dev/zvol/zroot/swap none swap discard 0 0" >> /mnt/etc/fstab
repo='$repo'
echo "#################### INSTILLATION #########################"
pacstrap -i /mnt base base-devel
ee $? "Failed to pacstrap"
echo "Installing systemd-boot"
bootctl --path=/mnt/boot install
ee $? "Failed to install systemd-boot"
CRYPTUUID=$(blkid -o export $CRYPTDEV | grep -E '^UUID' | cut -d'=' -f2)
mkdir -p /mnt/boot/loader/entries
cat <<EOF > /mnt/boot/loader/entries/arch.conf
title Arch Linux
linux /vmlinuz-linux
initrd /initramfs-linux.img
options rd.luks.uuid=${CRYPTUUID} rd.luks.name=${CRYPTUUID}=luks_zfs rd.luks.options=timeout=20s,cipher=aes-xts-plain64:sha512,size=512 root=zfs:zroot/sys/${SYSTEM_NAME}/ROOT/default rw
EOF
echo "Configuring/generating setup scripts (/mnt/chroot_install.sh /mnt/post_install.sh)"
cat <<EOF >> /mnt/etc/pacman.conf
[archzfs]
Server = http://archzfs.com/$repo/x86_64
EOF
sed -i -e "s/^HOOKS=(.*$/HOOKS=(${HOOKS})/" /mnt/etc/mkinitcpio.conf
echo "/etc/mkinitcpio.conf HOOKS config..."
grep -E '^HOOKS=' /mnt/etc/mkinitcpio.conf
echo '%wheel ALL=(ALL) ALL' > /mnt/etc/sudoers.d/wheel
cat <<EOF >> /mnt/chroot_install.sh
#!/bin/sh
pacman-key -r F75D9D76
pacman-key --lsign-key F75D9D76
pacman --noconfirm -Sy zfs-linux sudo vim wget curl
cd /tmp
wget https://aur.archlinux.org/cgit/aur.git/snapshot/mkinitcpio-sd-zfs.tar.gz
tar -xzf mkinitcpio-sd-zfs.tar.gz
chown nobody:nobody mkinitcpio-sd-zfs
cd mkinitcpio-sd-zfs
sudo -u nobody makepkg
pacman --noconfirm -U *.xz
mkinitcpio -p linux
ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
hwclock --systohc
sed -i -e 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
locale-gen
echo 'LANG=en_US.UTF-8' >> /etc/locale.conf
echo $SYSTEM_NAME > /etc/hostname
echo '127.0.0.1 localhost' > /etc/hosts
echo '::1 localhost' >> /etc/hosts
echo "127.0.0.1 ${SYSTEM_NAME}.localdomain ${SYSTEM_NAME}" >> /etc/hosts
loadkeys us
localectl set-keymap --no-convert us
#echo "Setting the root password"
#passwd
echo "Creating user $USER"
useradd -m -g users -G wheel,storage,power -s /bin/bash $USER
echo "Set password for $USER"
passwd $USER
echo "Done, do whatever you want then Ctrl+D"
EOF
chmod +x /mnt/chroot_install.sh
cat <<EOF > /mnt/post_install.sh
#!/bin/sh
zpool set cachefile=/etc/zfs/zpool.cache zroot
systemctl enable zfs.target
systemctl enable zfs-import-cache
systemctl enable zfs-mount
systemctl enable zfs-import.target
zgenhostid $(hostid)
mkinitcpio -p linux
EOF
chmod +x /mnt/post_install.sh
echo "##### DONE #####"
echo "Running /mnt/chroot_install.sh"
arch-chroot /mnt /chroot_install.sh
echo "Done... dropping into arch-chroot"
arch-chroot /mnt /bin/bash
#echo "##### IMPORTANT #####"
#echo "remember to 'umount /mnt/boot' and 'zfs umount -a' and 'zpool export zroot' OR"
#echo "cat unmount.txt | xargs unmount; zfs unmount -a; zpool export zroot"
cat <<EOF > unmount.txt
/mnt/boot
/mnt/usr/local
/mnt/var/cache
/mnt/home
/mnt/var/lib/systemd/coredump
/mnt/var/log/journal
/mnt/var/log
/mnt/var/lib/lxc
/mnt/var/lib/machines
/mnt
EOF
echo "UNMOUNTING AND EXPORING POOL"
cat unmount.txt | xargs umount
zfs unmount -a
zpool export zroot
echo "Done, reboot into the new system! (hopefully)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment