Last active
February 2, 2021 13:37
-
-
Save vrivellino/7dcf150da4cc1d07008315643bfdbfb5 to your computer and use it in GitHub Desktop.
Install Ubuntu 18.04: ZFS on encrypted drives with USB boot disk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Adapted from https://github.com/zfsonlinux/zfs/wiki/Ubuntu-18.04-Root-on-ZFS | |
# Should be executed from a live CD environment | |
set -e | |
## CONFIG VARS | |
set -x | |
# Disk drive ids (symlinks in /dev/disk/by-id) | |
bootdisk='usb-SanDisk_Cruzer_AAAAAAAAAAAAAAAAAAAA-0:0' | |
rdisk1='ata-SanDisk_SDSSDHII120G_AAAAAAAAAAAA' | |
rdisk2='ata-SanDisk_SDSSDHII120G_BBBBBBBBBBBB' | |
ddisk1='ata-SanDisk_Ultra_II_240GB_WWWWWWWWWWWW' | |
ddisk2='ata-SanDisk_Ultra_II_240GB_XXXXXXXXXXXX' | |
ddisk3='ata-SanDisk_Ultra_II_250GB_YYYYYYYYYYYY' | |
ddisk4='ata-SanDisk_Ultra_II_250GB_ZZZZZZZZZZZZ' | |
# dirs for key files | |
temp_key_dir=/dev/shm/luks-keys | |
dest_key_dir=/etc/keys/luks | |
# the hostname of the new system | |
system_hostname='vr-pc' | |
## END CONFIG | |
set +x | |
initial() { | |
apt-add-repository universe | |
apt update | |
apt install --yes debootstrap gdisk zfs-initramfs mdadm | |
} | |
partition() { | |
## Clear out software raid and partition configurations on disks | |
for dsk in $bootdisk $rdisk1 $rdisk2 $ddisk1 $ddisk2 $ddisk3 $ddisk4 ; do | |
mdadm --zero-superblock --force /dev/disk/by-id/$dsk | |
sgdisk --zap-all /dev/disk/by-id/$dsk | |
done | |
## Partition bootdisk for /boot (part4), /boot/efi (part3), and remaining space as a spare data partition (part1) | |
sgdisk -n3:1M:+512M -t3:EF00 /dev/disk/by-id/$bootdisk | |
sgdisk -n4:0:+512M -t4:8300 /dev/disk/by-id/$bootdisk | |
sgdisk -n1:0:0 -t1:8300 /dev/disk/by-id/$bootdisk | |
} | |
setup_encryption() { | |
## Setup encryption | |
mkdir -m0700 -p $temp_key_dir | |
for dsk in $rdisk1 $rdisk2 $ddisk1 $ddisk2 $ddisk3 $ddisk4; do | |
echo | |
if ! [[ -f $temp_key_dir/$dsk ]]; then | |
dd if=/dev/urandom of=$temp_key_dir/$dsk bs=32 count=1 | |
fi | |
chmod 600 $temp_key_dir/$dsk | |
# use key file for luksFormat | |
cryptsetup luksFormat --key-file $temp_key_dir/$dsk -c aes-xts-plain64 -s 256 -h sha256 /dev/disk/by-id/$dsk | |
cryptsetup luksAddKey --key-file $temp_key_dir/$dsk /dev/disk/by-id/$dsk | |
done | |
} | |
luks_open() { | |
cryptsetup status luks-root1 || cryptsetup luksOpen --key-file $temp_key_dir/$rdisk1 /dev/disk/by-id/$rdisk1 luks-root1 | |
cryptsetup status luks-root2 || cryptsetup luksOpen --key-file $temp_key_dir/$rdisk2 /dev/disk/by-id/$rdisk2 luks-root2 | |
cryptsetup status luks-data1 || cryptsetup luksOpen --key-file $temp_key_dir/$ddisk1 /dev/disk/by-id/$ddisk1 luks-data1 | |
cryptsetup status luks-data2 || cryptsetup luksOpen --key-file $temp_key_dir/$ddisk2 /dev/disk/by-id/$ddisk2 luks-data2 | |
cryptsetup status luks-data3 || cryptsetup luksOpen --key-file $temp_key_dir/$ddisk3 /dev/disk/by-id/$ddisk3 luks-data3 | |
cryptsetup status luks-data4 || cryptsetup luksOpen --key-file $temp_key_dir/$ddisk4 /dev/disk/by-id/$ddisk4 luks-data4 | |
} | |
initialize_zfs() { | |
## Create zpools | |
if zpool status rpool > /dev/null 2>&1; then | |
zpool destroy rpool | |
fi | |
zpool create -o ashift=12 \ | |
-O atime=off -O canmount=off -O compression=lz4 -O normalization=formD \ | |
-O mountpoint=/ -R /mnt \ | |
rpool mirror /dev/mapper/luks-root1 /dev/mapper/luks-root2 | |
if zpool status datapool > /dev/null 2>&1; then | |
zpool destroy datapool | |
fi | |
zpool create -o ashift=12 \ | |
-O atime=off -O canmount=off -O compression=lz4 -O normalization=formD \ | |
-O mountpoint=/data -R /mnt \ | |
datapool mirror /dev/mapper/luks-data1 /dev/mapper/luks-data2 | |
zpool add datapool mirror /dev/mapper/luks-data3 /dev/mapper/luks-data4 | |
## Create filesystems | |
# root FS | |
zfs create -o canmount=off -o mountpoint=none rpool/ROOT | |
zfs create -o canmount=noauto -o mountpoint=/ rpool/ROOT/ubuntu | |
zfs mount rpool/ROOT/ubuntu | |
# /var | |
zfs create -o canmount=off -o setuid=off -o exec=off rpool/var | |
zfs create -o com.sun:auto-snapshot=false rpool/var/cache | |
zfs create rpool/var/log | |
zfs create rpool/var/spool | |
zfs create -o com.sun:auto-snapshot=false -o exec=on rpool/var/tmp | |
zfs create -o mountpoint=/home -o setuid=off datapool/home | |
} | |
setup_initial_system() { | |
# format & mount boot disk | |
mke2fs -L /boot -t ext2 /dev/disk/by-id/$bootdisk-part4 | |
test -d /mnt/boot || mkdir /mnt/boot | |
mount /dev/disk/by-id/$bootdisk-part4 /mnt/boot | |
# install minimal system | |
chmod 1777 /mnt/var/tmp | |
debootstrap bionic /mnt | |
zfs set devices=off rpool | |
# setup networking | |
echo $system_hostname > /mnt/etc/hostname | |
sed -i '/^127[.]0[.]1[.]1 /d' /mnt/etc/hosts | |
echo "127.0.1.1 $system_hostname" >> /mnt/etc/hosts | |
# grab first device that isn't loopback | |
eth_dev=$(ip link | grep -v '^ ' | awk '{ print $2 }' | cut -f 1 -d : | grep -v '^lo$' | head -n 1) | |
if [[ -n $eth_dev ]]; then | |
cat > /mnt/etc/netplan/$eth_dev.yaml <<EOF | |
network: | |
version: 2 | |
ethernets: | |
NAME: | |
dhcp4: true | |
EOF | |
fi | |
cat > /mnt/etc/apt/sources.list << EOF | |
deb http://archive.ubuntu.com/ubuntu bionic main universe | |
deb-src http://archive.ubuntu.com/ubuntu bionic main universe | |
deb http://security.ubuntu.com/ubuntu bionic-security main universe | |
deb-src http://security.ubuntu.com/ubuntu bionic-security main universe | |
deb http://archive.ubuntu.com/ubuntu bionic-updates main universe | |
deb-src http://archive.ubuntu.com/ubuntu bionic-updates main universe | |
EOF | |
mount --rbind /dev /mnt/dev | |
mount --rbind /proc /mnt/proc | |
mount --rbind /sys /mnt/sys | |
} | |
copy_luks_keys() { | |
mkdir -p -m 700 /mnt$dest_key_dir | |
cp $temp_key_dir/* /mnt$dest_key_dir | |
chmod 400 /mnt$dest_key_dir/* | |
} | |
dest_system_basic_config() { | |
test -e "/root/$(basename "$0")" | |
locale-gen en_US.UTF-8 | |
echo LANG=en_US.UTF-8 > /etc/default/locale | |
dpkg-reconfigure tzdata | |
ln -s /proc/self/mounts /etc/mtab | |
apt update | |
apt install --yes --no-install-recommends linux-image-generic | |
apt install --yes zfs-initramfs cryptsetup dosfstools | |
boot_part_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$bootdisk-part4) | |
sed -i "/$boot_part_uuid/d" /etc/fstab | |
echo UUID=$boot_part_uuid /boot ext2 defaults 0 2 >> /etc/fstab | |
} | |
dest_system_crypttab() { | |
test -e "/root/$(basename "$0")" | |
sed -i '/^luks-\(root\|data\)[0-9] /d' /etc/crypttab | |
rdisk1_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$rdisk1) | |
rdisk2_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$rdisk2) | |
ddisk1_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$ddisk1) | |
ddisk2_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$ddisk2) | |
ddisk3_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$ddisk3) | |
ddisk4_uuid=$(blkid -s UUID -o value /dev/disk/by-id/$ddisk4) | |
echo luks-root1 UUID=$rdisk1_uuid $dest_key_dir/$rdisk1 luks,discard,initramfs >> /etc/crypttab | |
echo luks-root2 UUID=$rdisk2_uuid $dest_key_dir/$rdisk2 luks,discard,initramfs >> /etc/crypttab | |
echo luks-data1 UUID=$ddisk1_uuid $dest_key_dir/$ddisk1 luks,discard,initramfs >> /etc/crypttab | |
echo luks-data2 UUID=$ddisk2_uuid $dest_key_dir/$ddisk2 luks,discard,initramfs >> /etc/crypttab | |
echo luks-data3 UUID=$ddisk3_uuid $dest_key_dir/$ddisk3 luks,discard,initramfs >> /etc/crypttab | |
echo luks-data4 UUID=$ddisk4_uuid $dest_key_dir/$ddisk4 luks,discard,initramfs >> /etc/crypttab | |
sed -i '/^\(CRYPTSETUP\|KEYFILE_PATTERN\)=/d' /etc/cryptsetup-initramfs/conf-hook | |
echo 'CRYPTSETUP=y' >> /etc/cryptsetup-initramfs/conf-hook | |
echo "KEYFILE_PATTERN=$dest_key_dir/*" >> /etc/cryptsetup-initramfs/conf-hook | |
} | |
dest_system_install_grub() { | |
test -e "/root/$(basename "$0")" | |
apt install dosfstools | |
mkdosfs -F 32 -n EFI /dev/disk/by-id/$bootdisk-part3 | |
test -d /boot/efi || mkdir /boot/efi | |
bootpart_uuid=$(blkid -s PARTUUID -o value /dev/disk/by-id/$bootdisk-part3) | |
sed -i "/$bootpart_uuid/d" /etc/fstab | |
echo PARTUUID=$bootpart_uuid /boot/efi vfat nofail,x-systemd.device-timeout=1 0 1 >> /etc/fstab | |
mount /boot/efi | |
apt install --yes grub-efi-amd64 | |
} | |
dest_system_zfs_legacy_mount() { | |
test -e "/root/$(basename "$0")" | |
zfs set mountpoint=legacy rpool/var/log | |
zfs set mountpoint=legacy rpool/var/tmp | |
sed -i '/^rpool\/var\//d' /etc/fstab | |
cat >> /etc/fstab << EOF | |
rpool/var/log /var/log zfs defaults 0 0 | |
rpool/var/tmp /var/tmp zfs defaults 0 0 | |
EOF | |
} | |
dest_system_setup_boot() { | |
test -e "/root/$(basename "$0")" | |
[[ $(grub-probe /) == zfs ]] | |
update-initramfs -c -k all | |
chmod 600 /boot/initrd* | |
sed -i 's/^GRUB_HIDDEN_TIMEOUT=0/## GRUB_HIDDEN_TIMEOUT=0/' /etc/default/grub | |
sed -i 's/^#GRUB_TERMINAL=console/GRUB_TERMINAL=console/' /etc/default/grub | |
sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/## GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"\ | |
GRUB_CMDLINE_LINUX_DEFAULT=""/' /etc/default/grub | |
update-grub | |
grub-install --target=x86_64-efi --efi-directory=/boot/efi \ | |
--bootloader-id=ubuntu --recheck --no-floppy | |
ls /boot/grub/*/zfs.mod | |
} | |
dest_system_final_config_before_reboot() { | |
test -e "/root/$(basename "$0")" | |
getent group lpadmin > /dev/null || addgroup --system lpadmin | |
getent group sambashare > /dev/null || addgroup --system sambashare | |
set +x | |
echo | |
root_pw=$(getent shadow root | cut -f 2 -d :) | |
if [[ $root_pw == '*' ]] || [[ -z $root_pw ]]; then | |
echo 'Set root password for system' | |
passwd | |
echo | |
fi | |
read -p 'Enter username to create: ' username | |
set -x | |
if [[ -n $username ]]; then | |
zfs create datapool/home/$username | |
adduser $username | |
cp -a /etc/skel/.[!.]* /home/$username/ | |
chown -R $username:$username /home/$username | |
usermod -a -G adm,cdrom,dip,lpadmin,plugdev,sambashare,sudo $username | |
fi | |
for snap in rpool/ROOT/ubuntu@install datapool/home@install ; do | |
if zfs list -t snap | grep -q $snap; then | |
zfs destroy $snap | |
fi | |
zfs snapshot $snap | |
done | |
} | |
if [[ -z $1 ]]; then | |
echo | |
echo "Performing pre-installation setups ..." | |
echo | |
set -x | |
initial | |
partition | |
setup_encryption | |
luks_open | |
initialize_zfs | |
echo "Performing minimal installation ..." | |
setup_initial_system | |
copy_luks_keys | |
## Copy script to destination system and exec via chroot | |
cp "$0" /mnt/root | |
chroot /mnt /bin/bash --login "/root/$(basename "$0")" post-install-config | |
set +x | |
echo | |
echo System is ready to be rebooted | |
echo | |
echo If all goes well, you can complete items specified https://github.com/zfsonlinux/zfs/wiki/Ubuntu-18.04-Root-on-ZFS#step-7-configure-swap | |
echo | |
echo The following commands will be executed: | |
echo "# mount | grep -v zfs | tac | awk '/\\/mnt/ {print \$3}' | xargs -i{} umount -lf {}" | |
echo \# zpool export datapool | |
echo \# zpool export rpool | |
echo \# reboot | |
echo | |
read -p 'Press enter to continue ...' input_str | |
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | xargs -i{} umount -lf {} | |
zpool export datapool | |
zpool export rpool | |
reboot | |
elif [[ $1 == post-install-config ]]; then | |
echo | |
echo "Performing post-install configuration ..." | |
echo | |
set -x | |
dest_system_basic_config | |
dest_system_crypttab | |
dest_system_install_grub | |
dest_system_zfs_legacy_mount | |
dest_system_setup_boot | |
dest_system_final_config_before_reboot | |
set +x | |
else | |
echo "Fatal: unknown argument '$1'" >&2 | |
exit 1 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment