Skip to content

Instantly share code, notes, and snippets.

@AStevensTaylor
Forked from altercation/archdual.sh
Last active February 26, 2019 11:33
Show Gist options
  • Save AStevensTaylor/3222cd9c984c887b5e8358b7f1ad3396 to your computer and use it in GitHub Desktop.
Save AStevensTaylor/3222cd9c984c887b5e8358b7f1ad3396 to your computer and use it in GitHub Desktop.
arch-dual drive config
#!/bin/sh
#----------------------------------------------------------------------
# Arch Linux Installation Script
#
# This installs, with no intervention (other than initial edit to change
# RAID array drive values), Arch Linux on an encrypted btrfs raid1 array.
#----------------------------------------------------------------------
set -eu
#----------------------------------------------------------------------
# DRIVE & SCRIPT VALUES
#----------------------------------------------------------------------
DRIVE1=/dev/nvme0n1
DRIVE2=/dev/sda
echo -e "confirm device assignments in script first\nDRIVE 1 = $DRIVE1\nDRIVE 2 = $DRIVE2\n\nlsblk:\n"; lsblk; exit
# script values
HOST=deadbeef
USER=astevenstaylor
USERSHELL=zsh
MOUNT=/mnt
MOUNTOPTS=defaults,x-mount.mkdir
BTRFSOPTS=$MOUNTOPTS,ssd,noatime
#----------------------------------------------------------------------
# preflight cleanup (close mapped encrypted devices, unmount, shutdown swap)
#----------------------------------------------------------------------
[ -n "${SWAP:=$(swapon --noheadings --show=NAME)}" ] && swapoff $SWAP
umount /key || :
umount /mnt/boot || :
umount -R /mnt || :
for MAPPED in $(ls /dev/mapper); do [ "$MAPPED" != "control" ] && cryptsetup close /dev/mapper/$MAPPED || : ; done
#----------------------------------------------------------------------
# optional: secure wipe of drive (takes a while)
#----------------------------------------------------------------------
# sgdisk --zap-all /dev/device
# cryptsetup open --type plain /dev/device container --key-file /dev/random
# dd if=/dev/zero of=/dev/mapper/container status=progress
# cryptsetup close container
#----------------------------------------------------------------------
# Create Partitions
#----------------------------------------------------------------------
# NOTE: EFI partition should be at least 550MiB according to
# http://www.rodsbooks.com/efi-bootloaders/principles.html
for DRIVE in $DRIVE1 $DRIVE2
do
sgdisk --zap-all $DRIVE
done
sgdisk --clear \
--new=1:0:+2GiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:+32GiB --typecode=2:8200 --change-name=2:cryptswap \
--new=3:0:0 --typecode=3:8300 --change-name=3:cryptsystem1 \
$DRIVE1
sgdisk --clear \
--new=1:0:0 --typecode=1:8300 --change-name=1:cryptsystem2 \
$DRIVE2
#----------------------------------------------------------------------
#
#----------------------------------------------------------------------
# get passphrase
while ! ${MATCH:-false}
do
echo -en "Enter Passphrase : "
read -rs PASS
echo -en "\nConfirm Passphrase : "
read -rs CONF
[ "$PASS" = "$CONF" ] \
&& { MATCH=true; echo -e "\n\nPassphrases matched.\n"; } \
|| echo -e "\n\nPassphrases didn't match--try again.\n"
done
# encrypt cryptsystem* partitions with passphrases
for PART in cryptsystem1 cryptsystem2
do
echo $PASS | cryptsetup luksFormat --align-payload=8192 -s 256 -c aes-xts-plain64 /dev/disk/by-partlabel/$PART
echo $PASS | cryptsetup open /dev/disk/by-partlabel/$PART ${PART#crypt}
done
#----------------------------------------------------------------------
# Format & Mount Partitions
#----------------------------------------------------------------------
mkfs.fat -F32 -n EFI /dev/disk/by-partlabel/EFI
# make btrfs raid
mkfs.btrfs --force --data raid0 --metadata raid1 --label system /dev/mapper/system1 /dev/mapper/system2
# mount btrfs top-level subvolume for further subvolume creation
mount -t btrfs -o $BTRFSOPTS LABEL=system $MOUNT
btrfs subvolume create $MOUNT/root
btrfs subvolume create $MOUNT/home
btrfs subvolume create $MOUNT/snapshots
umount -R $MOUNT
# remount subvolumes
mount -t btrfs -o $BTRFSOPTS,subvol=root LABEL=system $MOUNT
mount -t btrfs -o $BTRFSOPTS,subvol=home LABEL=system $MOUNT/home
mount -t btrfs -o $BTRFSOPTS,subvol=snapshots LABEL=system $MOUNT/.snapshots
# mount EFI
mount -o $MOUNTOPTS LABEL=EFI $MOUNT/boot
# make and activate swap
cryptsetup open --type plain --key-file /dev/urandom /dev/disk/by-partlabel/cryptswap swap
mkswap -L swap /dev/mapper/swap
swapon -L swap
#----------------------------------------------------------------------
# Install Base System
#----------------------------------------------------------------------
# install base system
pacstrap $MOUNT base base-devel btrfs-progs iw zsh vim terminus-font efibootmgr intel-ucode git
# generate fstab
genfstab -L -p $MOUNT >> $MOUNT/etc/fstab
# remove the subvolume ID element of the fstab statements to
# allow mounting by subvolume name only (to facilitate rollbacks)
sed -i 's/,subvolid=[[:digit:]]*//g' $MOUNT/etc/fstab
# enter swap into crypttab
echo 'swap /dev/disk/by-partlabel/cryptswap /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256' >> $MOUNT/etc/crypttab
# make sure we have a crypttab.initramfs as well
echo 'swap /dev/disk/by-partlabel/cryptswap /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256' > $MOUNT/etc/crypttab.initramfs
# remove LABEL identifier for swap in fstab as it may/will not have a label from crypttab
# (TODO: could *maybe* solve this with luks.name= but I'm then using UUID)
sed -i 's+LABEL=swap+/dev/mapper/swap+' $MOUNT/etc/fstab
#----------------------------------------------------------------------
# Prepare chroot script
#----------------------------------------------------------------------
cat > $MOUNT/setup.sh <<EOFSETUP
#!/bin/sh
set -eu
HOST=$HOST
USER=$USER
USERSHELL=$USERSHELL
# get partition UUIDs (have to do this before entering chroot since lsblk won't report there)
CRYPTSWAP_UUID=$(lsblk --nodeps --noheadings -oUUID /dev/disk/by-partlabel/cryptswap)
CRYPTSYSTEM1_UUID=$(lsblk --nodeps --noheadings -oUUID /dev/disk/by-partlabel/cryptsystem1)
CRYPTSYSTEM2_UUID=$(lsblk --nodeps --noheadings -oUUID /dev/disk/by-partlabel/cryptsystem2)
EOFSETUP
cat >> $MOUNT/setup.sh <<'EOFSETUP'
setfont ter-u18n
echo "FONT=ter-u18n" > /etc/vconsole.conf
echo "KEYMAP=uk" >> /etc/vconsole.conf
echo "en_GB.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
echo "LANG=en_GB.UTF-8" > /etc/locale.conf
export LANG=en_GB.UTF-8
mv /etc/localtime /etc/localtime.orig
ln -s /usr/share/zoneinfo/Europe/London /etc/localtime
hwclock --systohc --utc
#timedatectl set-ntp true
echo $HOST > /etc/hostname
#edit /etc/hosts the same way... see beginners guide
mv /etc/hosts /etc/hosts.orig
cat > /etc/hosts <<EOF
#
# /etc/hosts: static lookup table for host names
#
#<ip-address> <hostname.domain.org> <hostname>
127.0.0.1 localhost.localdomain localhost
::1 localhost.localdomain localhost
127.0.0.1 $HOST.localdomain $HOST
::1 $HOST.localdomain $HOST
# End of file
EOF
# add user, set passwords
useradd -m -G wheel -s $(which $USERSHELL) $USER
while ! ${MATCH:-false}
do
echo -en "Enter Initial Root/User Passphrase : "
read -rs PASS
echo -en "\nConfirm Passphrase : "
read -rs CONF
[ "$PASS" = "$CONF" ] \
&& { MATCH=true; echo -e "\n\nPassphrases matched.\n"; } \
|| echo -e "\n\nPassphrases didn't match--try again.\n"
done
for ACCOUNT in root $USER; do
echo "$ACCOUNT:$PASS" | chpasswd
done
# enable sudo for wheel
tmpfile=$(mktemp)
echo "%wheel ALL=(ALL) NOPASSWD: ALL" > $tmpfile
visudo -cf $tmpfile \
&& mv $tmpfile /etc/sudoers.d/wheel \
|| { echo "ERROR updating sudoers; no change made"; exit 1; }
# reconfigure and regenerate initrams
mv /etc/mkinitcpio.conf /etc/mkinitcpio.orig
cat > /etc/mkinitcpio.conf << EOF
#MODULES="nvidia nvidia_modeset nvidia_uvm nvidia_drm"
BINARIES="/usr/bin/btrfs"
FILES=""
HOOKS="base systemd sd-vconsole modconf keyboard block filesystems btrfs sd-encrypt fsck"
EOF
mkinitcpio -p linux
# remove exiting boot entry
EXISTINGENTRY=$(efibootmgr | grep "Linux Boot Manager" | cut -d " " -f 1 | sed "s/[^[:digit:]]*//g" | head -n1)
[ -n "$EXISTINGENTRY" ] && efibootmgr -b$EXISTINGENTRY -B
# install bootloader ( || : to swallow error code... this seems to return one even on success?)
bootctl --path=/boot install || :
# configure loader defaults
cat > /boot/loader/loader.conf <<EOF
default arch
timeout 5
editor 0
EOF
# configure initial entry
# TODO: try PARTLABEL in lieu of UUID
cat > /boot/loader/entries/arch.conf <<EOF
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options \
rd.luks.name=$CRYPTSYSTEM1_UUID=system1 \
rd.luks.name=$CRYPTSYSTEM2_UUID=system2 \
root=LABEL=system rootflags=subvol=/root,rw,x-systemd.device-timeout=0 intel_iommu=on
EOF
cat > /boot/loader/entries/arch_fallback.conf <<EOF
title Arch Linux Fallback
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux-fallback.img
options \
rd.luks.name=$CRYPTSYSTEM1_UUID=system1 \
rd.luks.name=$CRYPTSYSTEM2_UUID=system2 \
root=LABEL=system rootflags=subvol=/root,rw,x-systemd.device-timeout=0 intel_iommu=on
EOF
echo "use efibootmgr to ensure correct entry for systemd-boot is present, active, and in bootorder"
# add new efi boot entry
NEWENTRY=$(efibootmgr | grep "Linux Boot Manager" | cut -d " " -f 1 | sed "s/[^[:digit:]]*//g" | tail -n1)
BOOTORDER=$(efibootmgr | grep BootOrder | cut -d " " -f 2)
efibootmgr -o$NEWENTRY,$BOOTORDER
EOFSETUP
#----------------------------------------------------------------------
# Execute chroot script
#----------------------------------------------------------------------
chmod +x $MOUNT/setup.sh
arch-chroot $MOUNT sh -c '/setup.sh'
#!/bin/sh
#----------------------------------------------------------------------
# Arch Linux Installation Script
#
# This installs, with no intervention (other than initial edit to change
# RAID array drive values), Arch Linux on an encrypted btrfs raid1 array.
#----------------------------------------------------------------------
set -eu
#----------------------------------------------------------------------
# DRIVE & SCRIPT VALUES
#----------------------------------------------------------------------
DRIVE=/dev/sda
# script values
HOST=deadbeef
USER=astevenstaylor
USERSHELL=zsh
MOUNT=/mnt
MOUNTOPTS=defaults,x-mount.mkdir
BTRFSOPTS=$MOUNTOPTS,ssd,noatime
#----------------------------------------------------------------------
# preflight cleanup (close mapped encrypted devices, unmount, shutdown swap)
#----------------------------------------------------------------------
[ -n "${SWAP:=$(swapon --noheadings --show=NAME)}" ] && swapoff $SWAP
umount /key || :
umount /mnt/boot || :
umount -R /mnt || :
for MAPPED in $(ls /dev/mapper); do [ "$MAPPED" != "control" ] && cryptsetup close /dev/mapper/$MAPPED || : ; done
#----------------------------------------------------------------------
# optional: secure wipe of drive (takes a while)
#----------------------------------------------------------------------
# sgdisk --zap-all /dev/device
# cryptsetup open --type plain /dev/device container --key-file /dev/random
# dd if=/dev/zero of=/dev/mapper/container status=progress
# cryptsetup close container
#----------------------------------------------------------------------
# Create Partitions
#----------------------------------------------------------------------
# NOTE: EFI partition should be at least 550MiB according to
# http://www.rodsbooks.com/efi-bootloaders/principles.html
sgdisk --zap-all $DRIVE
sgdisk --clear \
--new=1:0:+2GiB --typecode=1:ef00 --change-name=1:EFI \
--new=2:0:+8GiB --typecode=2:8200 --change-name=2:cryptswap \
--new=3:0:0 --typecode=3:8300 --change-name=3:cryptsystem \
$DRIVE
#----------------------------------------------------------------------
#
#----------------------------------------------------------------------
# get passphrase
while ! ${MATCH:-false}
do
echo -en "Enter Passphrase : "
read -rs PASS
echo -en "\nConfirm Passphrase : "
read -rs CONF
[ "$PASS" = "$CONF" ] \
&& { MATCH=true; echo -e "\n\nPassphrases matched.\n"; } \
|| echo -e "\n\nPassphrases didn't match--try again.\n"
done
# encrypt cryptsystem* partitions with passphrases
for PART in cryptsystem
do
echo $PASS | cryptsetup luksFormat --align-payload=8192 -s 256 -c aes-xts-plain64 /dev/disk/by-partlabel/$PART
echo $PASS | cryptsetup open /dev/disk/by-partlabel/$PART ${PART#crypt}
done
#----------------------------------------------------------------------
# Format & Mount Partitions
#----------------------------------------------------------------------
mkfs.fat -F32 -n EFI /dev/disk/by-partlabel/EFI
# make btrfs raid
mkfs.btrfs --force --label system /dev/mapper/system
# mount btrfs top-level subvolume for further subvolume creation
mount -t btrfs -o $BTRFSOPTS LABEL=system $MOUNT
btrfs subvolume create $MOUNT/root
btrfs subvolume create $MOUNT/home
btrfs subvolume create $MOUNT/snapshots
umount -R $MOUNT
# remount subvolumes
mount -t btrfs -o $BTRFSOPTS,subvol=root LABEL=system $MOUNT
mount -t btrfs -o $BTRFSOPTS,subvol=home LABEL=system $MOUNT/home
mount -t btrfs -o $BTRFSOPTS,subvol=snapshots LABEL=system $MOUNT/.snapshots
# mount EFI
mount -o $MOUNTOPTS LABEL=EFI $MOUNT/boot
# make and activate swap
cryptsetup open --type plain --key-file /dev/urandom /dev/disk/by-partlabel/cryptswap swap
mkswap -L swap /dev/mapper/swap
swapon -L swap
#----------------------------------------------------------------------
# Install Base System
#----------------------------------------------------------------------
# install base system
pacstrap $MOUNT base base-devel btrfs-progs iw zsh vim terminus-font efibootmgr intel-ucode git
# generate fstab
genfstab -L -p $MOUNT >> $MOUNT/etc/fstab
# remove the subvolume ID element of the fstab statements to
# allow mounting by subvolume name only (to facilitate rollbacks)
sed -i 's/,subvolid=[[:digit:]]*//g' $MOUNT/etc/fstab
# enter swap into crypttab
echo 'swap /dev/disk/by-partlabel/cryptswap /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256' >> $MOUNT/etc/crypttab
# make sure we have a crypttab.initramfs as well
echo 'swap /dev/disk/by-partlabel/cryptswap /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256' > $MOUNT/etc/crypttab.initramfs
# remove LABEL identifier for swap in fstab as it may/will not have a label from crypttab
# (TODO: could *maybe* solve this with luks.name= but I'm then using UUID)
sed -i 's+LABEL=swap+/dev/mapper/swap+' $MOUNT/etc/fstab
#----------------------------------------------------------------------
# Prepare chroot script
#----------------------------------------------------------------------
cat > $MOUNT/setup.sh <<EOFSETUP
#!/bin/sh
set -eu
HOST=$HOST
USER=$USER
USERSHELL=$USERSHELL
# get partition UUIDs (have to do this before entering chroot since lsblk won't report there)
CRYPTSWAP_UUID=$(lsblk --nodeps --noheadings -oUUID /dev/disk/by-partlabel/cryptswap)
CRYPTSYSTEM_UUID=$(lsblk --nodeps --noheadings -oUUID /dev/disk/by-partlabel/cryptsystem)
EOFSETUP
cat >> $MOUNT/setup.sh <<'EOFSETUP'
setfont ter-u18n
echo "FONT=ter-u18n" > /etc/vconsole.conf
echo "KEYMAP=uk" >> /etc/vconsole.conf
echo "en_GB.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
echo "LANG=en_GB.UTF-8" > /etc/locale.conf
export LANG=en_GB.UTF-8
mv /etc/localtime /etc/localtime.orig
ln -s /usr/share/zoneinfo/Europe/London /etc/localtime
hwclock --systohc --utc
#timedatectl set-ntp true
echo $HOST > /etc/hostname
#edit /etc/hosts the same way... see beginners guide
mv /etc/hosts /etc/hosts.orig
cat > /etc/hosts <<EOF
#
# /etc/hosts: static lookup table for host names
#
#<ip-address> <hostname.domain.org> <hostname>
127.0.0.1 localhost.localdomain localhost
::1 localhost.localdomain localhost
127.0.0.1 $HOST.localdomain $HOST
::1 $HOST.localdomain $HOST
# End of file
EOF
# add user, set passwords
useradd -m -G wheel -s $(which $USERSHELL) $USER
while ! ${MATCH:-false}
do
echo -en "Enter Initial Root/User Passphrase : "
read -rs PASS
echo -en "\nConfirm Passphrase : "
read -rs CONF
[ "$PASS" = "$CONF" ] \
&& { MATCH=true; echo -e "\n\nPassphrases matched.\n"; } \
|| echo -e "\n\nPassphrases didn't match--try again.\n"
done
for ACCOUNT in root $USER; do
echo "$ACCOUNT:$PASS" | chpasswd
done
# enable sudo for wheel
tmpfile=$(mktemp)
echo "%wheel ALL=(ALL) NOPASSWD: ALL" > $tmpfile
visudo -cf $tmpfile \
&& mv $tmpfile /etc/sudoers.d/wheel \
|| { echo "ERROR updating sudoers; no change made"; exit 1; }
# reconfigure and regenerate initrams
mv /etc/mkinitcpio.conf /etc/mkinitcpio.orig
cat > /etc/mkinitcpio.conf << EOF
#MODULES="nvidia nvidia_modeset nvidia_uvm nvidia_drm"
BINARIES="/usr/bin/btrfs"
FILES=""
HOOKS="base systemd sd-vconsole modconf keyboard block filesystems btrfs sd-encrypt fsck"
EOF
mkinitcpio -p linux
# remove exiting boot entry
EXISTINGENTRY=$(efibootmgr | grep "Linux Boot Manager" | cut -d " " -f 1 | sed "s/[^[:digit:]]*//g" | head -n1)
[ -n "$EXISTINGENTRY" ] && efibootmgr -b$EXISTINGENTRY -B
# install bootloader ( || : to swallow error code... this seems to return one even on success?)
bootctl --path=/boot install || :
# configure loader defaults
cat > /boot/loader/loader.conf <<EOF
default arch
timeout 5
editor 0
EOF
# configure initial entry
# TODO: try PARTLABEL in lieu of UUID
cat > /boot/loader/entries/arch.conf <<EOF
title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options \
rd.luks.name=$CRYPTSYSTEM_UUID=system1 \
root=LABEL=system rootflags=subvol=/root,rw,x-systemd.device-timeout=0 intel_iommu=on
EOF
cat > /boot/loader/entries/arch_fallback.conf <<EOF
title Arch Linux Fallback
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux-fallback.img
options \
rd.luks.name=$CRYPTSYSTEM_UUID=system1 \
root=LABEL=system rootflags=subvol=/root,rw,x-systemd.device-timeout=0 intel_iommu=on
EOF
echo "use efibootmgr to ensure correct entry for systemd-boot is present, active, and in bootorder"
# add new efi boot entry
NEWENTRY=$(efibootmgr | grep "Linux Boot Manager" | cut -d " " -f 1 | sed "s/[^[:digit:]]*//g" | tail -n1)
BOOTORDER=$(efibootmgr | grep BootOrder | cut -d " " -f 2)
efibootmgr -o$NEWENTRY,$BOOTORDER
EOFSETUP
#----------------------------------------------------------------------
# Execute chroot script
#----------------------------------------------------------------------
chmod +x $MOUNT/setup.sh
arch-chroot $MOUNT sh -c '/setup.sh'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment