mirror of
https://git.myvelabs.com/lab/archlinux.git
synced 2025-12-17 19:46:25 +00:00
1755 lines
43 KiB
Bash
Executable file
1755 lines
43 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
revision=1.0l
|
|
set -a
|
|
set -E
|
|
|
|
# ZFS key
|
|
# zfsgpgkey=DDF7DB817396A49B2A2723F7403BD972F75D9D76 # archzfs
|
|
zfsgpgkey=D0F2AE55C1BF11A026D155813A658A95B8CFCC51 # myvezfs
|
|
|
|
# Exit function
|
|
trap '[ "${?}" -ne 77 ] || exit 77' ERR
|
|
function die
|
|
{
|
|
local reset="$(tput sgr0)"
|
|
local red="${reset}$(tput setaf 1)"
|
|
local yellow="${reset}$(tput setaf 3)"
|
|
|
|
cat <<- abort
|
|
${red}
|
|
Error encountered for the following reason:
|
|
${yellow}
|
|
${@}
|
|
${red}
|
|
Script aborted...
|
|
${reset}
|
|
abort
|
|
|
|
exit 77
|
|
}
|
|
|
|
# EFI system check
|
|
if [ ! -d /sys/firmware/efi/efivars ]
|
|
then
|
|
die 'This script only works with UEFI systems'
|
|
fi
|
|
|
|
# Prompt function
|
|
function reformat
|
|
{
|
|
case "${1}" in
|
|
# Formatting
|
|
bold) format+=($(tput bold))
|
|
;;
|
|
italic) format+=($(tput sitm))
|
|
;;
|
|
uline) format+=($(tput smul))
|
|
;;
|
|
blink) format+=($(tput blink))
|
|
;;
|
|
dim) format+=($(tput dim))
|
|
;;
|
|
|
|
# Colours
|
|
black) format+=($(tput setaf 0))
|
|
;;
|
|
red) format+=($(tput setaf 1))
|
|
;;
|
|
green) format+=($(tput setaf 2))
|
|
;;
|
|
yellow) format+=($(tput setaf 3))
|
|
;;
|
|
blue) format+=($(tput setaf 4))
|
|
;;
|
|
magenta) format+=($(tput setaf 5))
|
|
;;
|
|
cyan) format+=($(tput setaf 6))
|
|
;;
|
|
white) format+=($(tput setaf 7))
|
|
;;
|
|
|
|
# Read-specific
|
|
array) array="-a"
|
|
;;
|
|
secret) secret="-s"
|
|
;;
|
|
press) press="-n 1"
|
|
;;
|
|
|
|
# Everything else goes into line
|
|
*) line="${@}"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Syntax: ask for var [in format] "statement"
|
|
function ask
|
|
{
|
|
local format variable line secret press array
|
|
|
|
case "${1}" in
|
|
for)
|
|
case "${2}" in
|
|
in)
|
|
die "Syntax error detected (missing variable before $(tput sitm)$(tput smso)in$(tput rmso)$(tput ritm))
|
|
Usage: ask for \${variable} in \${format[@]} \"\${line}\""
|
|
;;
|
|
*)
|
|
variable="${2}"
|
|
shift 2
|
|
;;
|
|
esac
|
|
;;
|
|
*) die "Syntax error detected (misuse of $(tput sitm)$(tput smso)for$(tput rmso)$(tput ritm))
|
|
Usage: ask for \${variable} in \${format[@]} \"\${line}\""
|
|
;;
|
|
esac
|
|
|
|
case "${1}" in
|
|
in)
|
|
shift
|
|
for arg in "${@}"
|
|
do
|
|
reformat ${arg}
|
|
done
|
|
;;
|
|
*) line="${@}"
|
|
;;
|
|
esac
|
|
|
|
read ${press} -r -p "$(printf "%s" "${format[@]}" "${line}: " "$(tput sgr0)")" ${secret} ${array} ${variable}
|
|
echo
|
|
|
|
export ${variable}
|
|
}
|
|
|
|
# Syntax: say [as template] [in format] "statement"
|
|
function say
|
|
{
|
|
local format line heading
|
|
|
|
case "${1}" in
|
|
as)
|
|
# Templates
|
|
case ${2} in
|
|
title)
|
|
format+=($(tput setaf 6)) # cyan
|
|
;;
|
|
heading)
|
|
format+=($(tput setaf 3) $(tput bold)) # bold yellow
|
|
heading=":: "
|
|
;;
|
|
success)
|
|
format+=($(tput setaf 2)) # green
|
|
;;
|
|
warning)
|
|
format+=($(tput setaf 1)) # red
|
|
;;
|
|
*)
|
|
die "Syntax error detected (misuse of $(tput sitm)$(tput smso)as$(tput rmso)$(tput ritm))
|
|
Usage: say as \${template} in \${format[@]} \"\${line}\""
|
|
;;
|
|
esac
|
|
shift 2
|
|
;;
|
|
esac
|
|
|
|
case "${1}" in
|
|
in)
|
|
shift
|
|
for arg in "${@}"
|
|
do
|
|
reformat ${arg}
|
|
done
|
|
;;
|
|
*) line="${@}"
|
|
;;
|
|
esac
|
|
|
|
printf "%s" "${format[@]}" "${heading}" "${line}" "$(tput sgr0)"
|
|
echo
|
|
}
|
|
|
|
function presskeytoresume
|
|
{
|
|
read -n 1 -s -p "Press any key when ready..."
|
|
echo
|
|
}
|
|
|
|
# Internet connection check
|
|
if nc -z -w 1 archlinux.org 443 >/dev/null 2>&1 || nc -z -w 1 google.com 443 >/dev/null 2>&1
|
|
then
|
|
timedatectl set-ntp true
|
|
else
|
|
die "No internet connectivity detected, plug in an ethernet cable or run ${reset}${green}iwd-connect${yellow} if using wifi and try again"
|
|
fi
|
|
|
|
##
|
|
## functions start
|
|
##
|
|
|
|
# Default systemd services
|
|
systemd_services+=(systemd-networkd.service systemd-resolved.service sshd.service iptables.service)
|
|
|
|
# Repeat a command until it exits with 0
|
|
function repeat
|
|
{
|
|
until ${@}
|
|
do
|
|
sleep 3
|
|
done
|
|
}
|
|
|
|
# Pacstrap new root and generate fs tables
|
|
function pacstrapGenfstab
|
|
{
|
|
local archpkgs="sudo openssh efibootmgr pacman-contrib \
|
|
vim rsync pv git less openbsd-netcat \
|
|
man-db bash-completion reflector \
|
|
wireguard-tools systemd-resolvconf \
|
|
${linux_firmware[@]} \
|
|
${btrfs[@]} ${zfs[@]} \
|
|
${ucode} \
|
|
${grub[@]} \
|
|
${iwd} \
|
|
${yubikey[@]} \
|
|
${nvidia[@]} \
|
|
${bluetooth[@]}"
|
|
|
|
# Delete unneccessary EFI files
|
|
rm -f /sys/firmware/efi/efivars/dump-*
|
|
for entry in $(efibootmgr | grep "Arch" | grep -o 'Boot....' | sed 's/Boot//')
|
|
do
|
|
efibootmgr --quiet -Bb ${entry}
|
|
done
|
|
|
|
# Ensure pacman.conf settings are properly set
|
|
sed -e '/Color/c Color' \
|
|
-e '/ParallelDownloads/c ParallelDownloads = 10' \
|
|
-i /etc/pacman.conf
|
|
|
|
# Add CacheServer
|
|
if [ ${cacheserver} ]
|
|
then
|
|
sed -e "/^\[core\]$\|^\[extra\]$/a CacheServer = ${cacheserver}" \
|
|
-i /etc/pacman.conf
|
|
fi
|
|
|
|
# Temporarily disable mkinitcpio install
|
|
ln -s -f /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook
|
|
|
|
echo
|
|
say as heading "Installing Arch Linux base packages"
|
|
case ${filesystem} in
|
|
zfs)
|
|
# Add zfs repo
|
|
grep -q '\[myvezfs\]' /etc/pacman.conf || sed -i '/\[core\]/i [myvezfs]\
|
|
Server = https://mirror.myvelabs.com/repo/$repo\
|
|
Server = https://repo.myvelabs.com/$repo\n' /etc/pacman.conf
|
|
|
|
# Add CacheServer
|
|
if [ ${cacheserver} ]
|
|
then
|
|
sed -e "/^\[myvezfs\]$/a CacheServer = ${cacheserver}" \
|
|
-i /etc/pacman.conf
|
|
fi
|
|
|
|
local zfslinux=$(pacman -Syi zfs-${linux_kernel} | grep "Depends On" | sed "s|.*${linux_kernel}=||" | awk '{print $1}')
|
|
|
|
# Install zfs-compatible linux kernel
|
|
if [ ${zfslinux} = $(pacman -Si myvezfs/${linux_kernel} | grep "Version" | awk '{print $3}') ]
|
|
then
|
|
repeat pacstrap -K /mnt --ask 4 \
|
|
base mkinitcpio iptables-nft ${linux_kernel} ${archpkgs}
|
|
else
|
|
repeat pacstrap -K /mnt base mkinitcpio iptables-nft
|
|
if [ ${cacheserver} ]
|
|
then
|
|
pacstrap -K -U /mnt ${cacheserver}/${linux_kernel}-${zfslinux}-x86_64.pkg.tar.zst ||\
|
|
repeat pacstrap -K -U /mnt https://archive.archlinux.org/packages/l/${linux_kernel}/${linux_kernel}-${zfslinux}-x86_64.pkg.tar.zst
|
|
else
|
|
repeat pacstrap -K -U /mnt https://archive.archlinux.org/packages/l/${linux_kernel}/${linux_kernel}-${zfslinux}-x86_64.pkg.tar.zst
|
|
fi
|
|
repeat pacstrap -K /mnt --ask 4 ${archpkgs}
|
|
fi
|
|
|
|
# Add archzfs repo
|
|
sed -e '/\[core\]/i [myvezfs]\
|
|
Server = https://mirror.myvelabs.com/repo/$repo\
|
|
Server = https://repo.myvelabs.com/$repo\n' \
|
|
-i /mnt/etc/pacman.conf
|
|
|
|
# Add archzfs repo keys
|
|
arch-chroot /mnt pacman-key -r ${zfsgpgkey}
|
|
arch-chroot /mnt pacman-key --lsign-key ${zfsgpgkey}
|
|
|
|
# ZFS properties
|
|
zpool set bootfs=zroot/ROOT zroot
|
|
zpool set cachefile=/etc/zfs/zpool.cache zroot
|
|
rsync -a /etc/zfs/zpool.cache /mnt/etc/zfs/
|
|
|
|
# Create zfs directory
|
|
mkdir -p /mnt/zfs/bin/
|
|
|
|
# Configure zfs for mkinitcpio
|
|
echo 'BINARIES+=(/usr/bin/zfs)' >/mnt/etc/mkinitcpio.conf.d/zz-binaries.conf
|
|
sed -e '/^HOOKS/ s/filesystems/zfs &/' \
|
|
-e '/^HOOKS/ s/ fsck//' \
|
|
/mnt/etc/mkinitcpio.conf >/mnt/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
|
|
# Generate fstab
|
|
case ${arch} in
|
|
1|2)
|
|
genfstab -U -p /mnt/boot | sed "s|vfat.*rw|vfat rw,x-systemd.idle-timeout=1min,x-systemd.automount,noauto,nofail|" >>/mnt/etc/fstab
|
|
sed -i '/UUID.*vfat/ s|/|/boot|' /mnt/etc/fstab
|
|
;;
|
|
*) genfstab -U -p /mnt | grep -v zroot >>/mnt/etc/fstab
|
|
;;
|
|
esac
|
|
|
|
# Add systemd services
|
|
systemd_services+=(zfs.target zfs-import-cache.service zfs-mount.service zfs-import.target zfs-trim@zroot.timer zfs-scrub@zroot.timer)
|
|
;;
|
|
btrfs|ext4)
|
|
repeat pacstrap -K /mnt --ask 4 \
|
|
base mkinitcpio iptables-nft ${linux_kernel} ${archpkgs}
|
|
|
|
# Generate fstab
|
|
genfstab -U -p /mnt >>/mnt/etc/fstab
|
|
|
|
case ${filesystem} in
|
|
btrfs) echo 'BINARIES+=(/usr/bin/btrfs)' >/mnt/etc/mkinitcpio.conf.d/zz-binaries.conf
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
|
|
# Ensure pacman.conf settings are properly set
|
|
sed -e '/Color/c Color' \
|
|
-e '/ParallelDownloads/c ParallelDownloads = 10' \
|
|
-i /mnt/etc/pacman.conf
|
|
|
|
# Custom mkinitcpio.conf
|
|
echo 'MODULES_DECOMPRESS="yes"' >/mnt/etc/mkinitcpio.conf.d/zz-modules_decompress.conf
|
|
|
|
# Add CacheServer
|
|
if [ ${cacheserver} ]
|
|
then
|
|
grep -q "^CacheServer" /etc/pacman.conf ||\
|
|
sed -e "/^\[core\]$\|^\[extra\]$\|^\[myvezfs\]$/a CacheServer = ${cacheserver}" \
|
|
-i /mnt/etc/pacman.conf
|
|
fi
|
|
|
|
# Make custom directories
|
|
mkdir -p /mnt/etc/pacman.d/hooks/ /mnt/opt/local/{bin,hooks}/
|
|
|
|
# Manually configure mkinitcpio
|
|
ln -s -f /dev/null /mnt/etc/pacman.d/hooks/90-mkinitcpio-install.hook
|
|
|
|
# Add hooks
|
|
hooks
|
|
}
|
|
|
|
# Update mkinitcpio with encrypt hooks
|
|
function encryptHook
|
|
{
|
|
case ${filesystem} in
|
|
zfs)
|
|
sed "s/block/& ${1}/" -i /mnt/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
# Pacman hook
|
|
install /dev/stdin /mnt/opt/local/hooks/mkinitcpio.conf <<- hook
|
|
#!/usr/bin/env bash
|
|
grep '^HOOKS' /etc/mkinitcpio.conf | sed -e "s/filesystems/zfs &/" -e "s/ fsck//" -e "s/block/& ${1}/" >/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
|
|
sed -e "s|%PKGBASE%|${linux_kernel}|g" \\
|
|
-e "s/^fallback/#&/g" \\
|
|
-e "s/ 'fallback'//" \\
|
|
/usr/share/mkinitcpio/hook.preset >/etc/mkinitcpio.d/${linux_kernel}.preset
|
|
hook
|
|
;;
|
|
*)
|
|
grep '^HOOKS' /mnt/etc/mkinitcpio.conf | sed "s/block/& ${1}/" >/mnt/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
# Pacman hook
|
|
install /dev/stdin /mnt/opt/local/hooks/mkinitcpio.conf <<- hook
|
|
#!/usr/bin/env bash
|
|
grep '^HOOKS' /etc/mkinitcpio.conf | sed "s/block/& ${1}/" >/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
|
|
sed -e "s|%PKGBASE%|${linux_kernel}|g" \\
|
|
-e "s/^fallback/#&/g" \\
|
|
-e "s/ 'fallback'//" \\
|
|
/usr/share/mkinitcpio/hook.preset >/etc/mkinitcpio.d/${linux_kernel}.preset
|
|
hook
|
|
;;
|
|
esac
|
|
|
|
cat >/mnt/etc/pacman.d/hooks/85-mkinitcpio.hook <<- mkinitcpio
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = mkinitcpio
|
|
|
|
[Action]
|
|
Description = Fixing mkinitcpio.conf
|
|
When = PostTransaction
|
|
Exec = /opt/local/hooks/mkinitcpio.conf
|
|
mkinitcpio
|
|
}
|
|
|
|
# Compile yubikey if arch package fails
|
|
function compileYubikey
|
|
{
|
|
cd yubikey-full-disk-encryption
|
|
make install || die 'Yubikey full disk encryption (github) package installation failed'
|
|
}
|
|
|
|
# Install yubikey inside new root
|
|
function chrootYubikey
|
|
{
|
|
if [ -d ~/yubikey-full-disk-encryption ]
|
|
then
|
|
rsync -a ~/yubikey-full-disk-encryption /mnt/
|
|
arch-chroot /mnt /usr/bin/bash <<- YUBIKEY
|
|
compileYubikey
|
|
rm -r /yubikey-full-disk-encryption
|
|
YUBIKEY
|
|
fi
|
|
|
|
rsync -a /etc/ykfde.conf /mnt/etc/
|
|
chmod 600 /mnt/etc/ykfde.conf
|
|
encryptHook ykfde
|
|
|
|
echo
|
|
say in blue 'Copied yubikey configuration files'
|
|
}
|
|
|
|
# Yubikey root encryption
|
|
function encryptRootYubikey
|
|
{
|
|
yubikey=(yubikey-personalization)
|
|
|
|
for package in yubikey-personalization yubikey-full-disk-encryption
|
|
do
|
|
if ! pacman -Q | grep -q -w ${package}
|
|
then
|
|
missing_yubikey_requirements+=(${package})
|
|
fi
|
|
done
|
|
|
|
if [ ${missing_yubikey_requirements} ]
|
|
then
|
|
yes | pacman -Sy ${missing_yubikey_requirements[@]}
|
|
echo
|
|
fi
|
|
|
|
function ykfdeFormat
|
|
{
|
|
# Custom challenge slot
|
|
# sed -i '/YKFDE_CHALLENGE_SLOT/c YKFDE_CHALLENGE_SLOT="1"' /etc/ykfde.conf
|
|
|
|
case ${decryptroot} in
|
|
[yY]) sed '/YKFDE_CHALLENGE=/c YKFDE_CHALLENGE="'"$(printf '%q' "${lukspass}")"'"' -i /etc/ykfde.conf ;;
|
|
[nN]) sed '/YKFDE_CHALLENGE_PASSWORD_NEEDED/c YKFDE_CHALLENGE_PASSWORD_NEEDED="1"' -i /etc/ykfde.conf ;;
|
|
esac
|
|
|
|
say as heading "Encrypting ${rootpart} with yubikey HMAC encryption"
|
|
until printf '%s\n' "${lukspass}" "${lukspass}" | ykfde-format ${encryptopts} ${luks_header} ${rootpart}
|
|
do
|
|
sleep 3
|
|
done
|
|
printf '%s\n' "${lukspass}" |\
|
|
ykfde-open \
|
|
-d ${rootpart} \
|
|
-n cryptroot \
|
|
-- ${luks_header}
|
|
}
|
|
|
|
while true
|
|
do
|
|
# Try with yubikey-full-disk-encryption pacman package
|
|
ykfdeFormat
|
|
|
|
if dmsetup ls | grep -q 'cryptroot'
|
|
then
|
|
yubikey+=(yubikey-full-disk-encryption)
|
|
echo
|
|
break
|
|
fi
|
|
|
|
for package in git make
|
|
do
|
|
if ! pacman -Q | grep -q -w ${package}
|
|
then
|
|
make_yubikey_requirements+=(${package})
|
|
fi
|
|
done
|
|
|
|
echo
|
|
|
|
say in blue 'Setting up yubikey'
|
|
if [ ${make_yubikey_requirements} ]
|
|
then
|
|
yes | pacman -Sy ${make_yubikey_requirements[@]}
|
|
echo
|
|
fi
|
|
|
|
# Try yubikey full disk encryption again using latest git
|
|
cd ~/
|
|
git clone https://github.com/agherzan/yubikey-full-disk-encryption.git
|
|
compileYubikey
|
|
ykfdeFormat
|
|
|
|
if dmsetup ls | grep -q 'cryptroot'
|
|
then
|
|
echo
|
|
break
|
|
fi
|
|
|
|
die 'Unable to encrypt root partition'
|
|
done
|
|
}
|
|
|
|
# Root encryption
|
|
function encryptRootLuks
|
|
{
|
|
say as heading "Encrypting ${rootpart}"
|
|
until printf '%s' "${lukspass}" | cryptsetup luksFormat -v ${encryptopts} ${luks_header} ${rootpart}
|
|
do
|
|
sleep 3
|
|
done
|
|
printf '%s' "${lukspass}" | cryptsetup open -v ${rootpart} cryptroot ${luks_header}
|
|
|
|
if dmsetup ls | grep -q 'cryptroot'
|
|
then
|
|
echo
|
|
else
|
|
die 'Unable to encrypt root partition'
|
|
fi
|
|
}
|
|
|
|
# External boot encryption
|
|
function encryptBootLuks
|
|
{
|
|
say as heading "Encrypting ${bootpart}"
|
|
until printf '%s' "${grubpass}" | cryptsetup luksFormat -v --type luks1 ${bootpart}
|
|
do
|
|
sleep 3
|
|
done
|
|
printf '%s' "${grubpass}" | cryptsetup open -v ${bootpart} cryptboot
|
|
yes | mkfs.ext2 -q /dev/mapper/cryptboot
|
|
|
|
if dmsetup ls | grep -q cryptboot
|
|
then
|
|
echo
|
|
else
|
|
die 'Unable to encrypt boot partition'
|
|
fi
|
|
}
|
|
|
|
# Default cryptsetup options
|
|
encryptopts="--cipher aes-xts-plain64 \
|
|
--key-size 256 \
|
|
--iter-time 2000 \
|
|
--hash sha512"
|
|
|
|
# Generate keyfile for external boot
|
|
function generateKeyfile
|
|
{
|
|
echo
|
|
say as heading 'Generating keyfile'
|
|
dd bs=1 count=256 if=/dev/random of=/mnt/crypto_keyfile.bin status=progress
|
|
chmod 000 /mnt/crypto_keyfile.bin
|
|
printf '%s' "${grubpass}" | cryptsetup luksAddKey ${bootpart} /mnt/crypto_keyfile.bin
|
|
|
|
if [[ ${decryptroot} = [yY] ]]
|
|
then
|
|
case ${arch} in
|
|
3) printf '%s' "${lukspass}" | cryptsetup luksAddKey ${rootpart} /mnt/crypto_keyfile.bin ;;
|
|
6) printf '%s' "${lukspass}" | cryptsetup luksAddKey /mnt/boot/header.img /mnt/crypto_keyfile.bin ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
# Format partitions
|
|
function configureVolumes
|
|
{
|
|
say as heading 'Configuring volumes'
|
|
|
|
# Root
|
|
case ${filesystem} in
|
|
ext4)
|
|
yes | mkfs.ext4 ${rootvol}
|
|
mount -o noatime ${rootvol} /mnt
|
|
;;
|
|
btrfs)
|
|
mkfs.btrfs -f ${rootvol}
|
|
btrfsopts="compress=zstd:2,noatime,space_cache=v2"
|
|
|
|
# Create btrfs subvolumes
|
|
mount ${rootvol} /mnt
|
|
for subvol in @{,home,var{,/pkg,/log,/tmp,/qemu},opt} # ,snapshots{,/root,/home}
|
|
do
|
|
btrfs subvolume create /mnt/${subvol}
|
|
done
|
|
chattr +C /mnt/@var/qemu
|
|
chmod 1777 /mnt/@var/tmp
|
|
umount /mnt
|
|
|
|
# Mount @root subvolume
|
|
mount --mkdir -o ${btrfsopts},subvol=@ ${rootvol} /mnt
|
|
# Mount home and home snapshots subvolumes
|
|
mount --mkdir -o ${btrfsopts},subvol=@home ${rootvol} /mnt/home
|
|
# mount --mkdir -o ${btrfsopts},subvol=@snapshots/home ${rootvol} /mnt/home/.snapshots
|
|
# mount --mkdir -o ${btrfsopts},subvol=@snapshots/root ${rootvol} /mnt/.snapshots
|
|
# Mount remaining subvolumes
|
|
mount --mkdir -o ${btrfsopts},subvol=@var/pkg ${rootvol} /mnt/var/cache/pacman/pkg
|
|
mount --mkdir -o ${btrfsopts},subvol=@var/log ${rootvol} /mnt/var/log
|
|
mount --mkdir -o ${btrfsopts},subvol=@var/tmp ${rootvol} /mnt/var/tmp
|
|
mount --mkdir -o ${btrfsopts},subvol=@var/qemu ${rootvol} /mnt/var/lib/libvirt/images
|
|
|
|
mount --mkdir -o ${btrfsopts},subvol=@opt ${rootvol} /mnt/opt
|
|
mount --mkdir -o ${btrfsopts},subvol=/ ${rootvol} /mnt/btrfs
|
|
echo
|
|
;;
|
|
zfs)
|
|
until printf '%s' "${lukspass}" |\
|
|
zpool create -f zroot \
|
|
-o ashift=12 \
|
|
-o autotrim=on \
|
|
-R /mnt \
|
|
-O acltype=posixacl \
|
|
-O canmount=off \
|
|
-O compression=zstd \
|
|
-O dnodesize=auto \
|
|
-O normalization=formD \
|
|
-O atime=off \
|
|
-O xattr=sa \
|
|
-O mountpoint=none \
|
|
${zpoolencryption} ${rootvol}
|
|
do
|
|
sleep 3
|
|
done
|
|
|
|
# Mount datasets
|
|
zfs create -o mountpoint=/ -o canmount=noauto zroot/ROOT
|
|
zfs create -o mountpoint=/.boot zroot/BOOT
|
|
zfs create -o mountpoint=/home zroot/HOME
|
|
zfs create -o mountpoint=/var/log zroot/LOG
|
|
zfs create -o mountpoint=/var/tmp zroot/TMP
|
|
zfs create -o mountpoint=/var/cache/pacman/pkg zroot/PKG
|
|
zfs create -o mountpoint=/var/lib/libvirt/images zroot/QEMU
|
|
|
|
# Verify zpool
|
|
zpool export zroot
|
|
zpool import -R /mnt zroot -N -d ${rootvol}
|
|
# Native encryption
|
|
case ${arch} in
|
|
2) printf '%s' "${lukspass}" | zfs load-key zroot ;;
|
|
esac
|
|
zfs mount zroot/ROOT
|
|
zfs mount -a
|
|
;;
|
|
esac
|
|
|
|
# Boot volume
|
|
if [[ ${arch} = [3567] ]]
|
|
then
|
|
yes | mkfs.fat -F 32 ${efivol}
|
|
else
|
|
yes | mkfs.fat -F 32 ${bootvol}
|
|
fi
|
|
|
|
mount --mkdir ${bootvol} /mnt/boot
|
|
|
|
# EFI volume
|
|
if [[ ${arch} = [3567] ]]
|
|
then
|
|
mount --mkdir ${efivol} /mnt/boot/efi
|
|
grub+=(grub)
|
|
fi
|
|
}
|
|
|
|
# Configure grub
|
|
function configureGrub
|
|
{
|
|
sed '/GRUB_TIMEOUT=/c GRUB_TIMEOUT=3' -i /etc/default/grub
|
|
sed '/GRUB_ENABLE_CRYPTODISK/c GRUB_ENABLE_CRYPTODISK=y' -i /etc/default/grub
|
|
echo 'cryptboot '${bootpart}' /crypto_keyfile.bin luks' >>/etc/crypttab
|
|
}
|
|
|
|
# Ask for system settings
|
|
function initialSetup
|
|
{
|
|
echo
|
|
|
|
# Username
|
|
say in blue 'Create a new user'
|
|
until [ ${username} ]
|
|
do
|
|
ask for username 'Username'
|
|
if [ -z ${username} ]
|
|
then
|
|
say as warning 'Username cannot be empty, try again'
|
|
fi
|
|
done
|
|
|
|
# Hostname
|
|
say in blue 'Create a name for your computer'
|
|
until [ ${hostname} ]
|
|
do
|
|
ask for hostname 'Hostname'
|
|
if [ -z ${hostname} ]
|
|
then
|
|
say as warning 'Hostname cannot be empty, try again'
|
|
fi
|
|
done
|
|
|
|
# User password
|
|
say in blue "Set a password for ${username}"
|
|
until [ "${userpass}" = "${userpass2}" -a "${userpass}" ]
|
|
do
|
|
ask for userpass in secret 'User password'
|
|
ask for userpass2 in secret 'Verify user password'
|
|
echo
|
|
if [ -z "${userpass}" ]
|
|
then
|
|
say as warning 'Password field cannot be empty, try again'
|
|
elif [ "${userpass}" != "${userpass2}" ]
|
|
then
|
|
say as warning 'Passwords did not match, try again'
|
|
fi
|
|
done
|
|
say as success "${username}@${hostname}'s password has been saved"
|
|
echo
|
|
|
|
# Encryption key
|
|
if [[ ${arch} = [2-7] ]]
|
|
then
|
|
say in blue 'Create an encryption passphrase for the system drive'
|
|
until [ "${lukspass}" = "${lukspass2}" -a "${lukspass}" -a "${#lukspass}" -ge 8 ]
|
|
do
|
|
ask for lukspass in secret 'Encryption password'
|
|
ask for lukspass2 in secret 'Verify encryption password'
|
|
echo
|
|
if [ -z "${lukspass}" ]
|
|
then
|
|
say as warning 'Passphrase field cannot be empty, try again'
|
|
elif [ "${lukspass}" != "${lukspass2}" ]
|
|
then
|
|
say as warning 'Passphrases did not match, try again'
|
|
elif [ "${#lukspass}" -lt 8 ]
|
|
then
|
|
say as warning 'Passphrase needs to be at least 8 characters'
|
|
fi
|
|
done
|
|
say as success 'Encryption passphrase has been saved'
|
|
echo
|
|
fi
|
|
|
|
# GRUB password
|
|
if [[ ${arch} = [3567] ]]
|
|
then
|
|
say in blue 'Create an encryption passphrase for the bootloader'
|
|
until [ "${grubpass}" = "${grubpass2}" -a "${grubpass}" -a "${#grubpass}" -ge 6 ]
|
|
do
|
|
ask for grubpass in secret 'Bootloader password'
|
|
ask for grubpass2 in secret 'Verify bootloader password'
|
|
echo
|
|
if [ -z "${grubpass}" ]
|
|
then
|
|
say as warning 'Password field cannot be empty, try again'
|
|
elif [ "${grubpass}" != "${grubpass2}" ]
|
|
then
|
|
say as warning 'Passwords did not match, try again'
|
|
elif [ "${#grubpass}" -lt 6 ]
|
|
then
|
|
say as warning 'Passphrase needs to be at least 6 characters'
|
|
fi
|
|
done
|
|
say as success 'Bootloader password has been saved'
|
|
echo
|
|
fi
|
|
|
|
# Assign system drive
|
|
say in blue 'Identify the system drive from the list of available devices below'
|
|
lsblk -d -o name,model,size,mountpoint | grep -v 'archiso'
|
|
say in uline white dim '## SSD and HDD device format begins with "sd" or "hd" (sda, sdb, sd[*])'
|
|
say in uline white dim '## NVME and PCI device format is "nvme[*]n1" (nvme0n1, nvme1n1, nvme[*]n1)'
|
|
while true
|
|
do
|
|
ask for drive 'Installation device'
|
|
if [ ${drive} ] && lsblk -o name | grep -q -w ${drive}
|
|
then
|
|
break
|
|
else
|
|
lsblk -d -o name,model,size,mountpoint | grep -v 'archiso'
|
|
if [ -z ${drive} ]
|
|
then
|
|
say as warning 'Field cannot be empty, try again'
|
|
else
|
|
say as warning 'Invalid selection or drive not available, try again'
|
|
fi
|
|
fi
|
|
done
|
|
say as success "Installation drive set as $(tput smso)/dev/${drive}$(tput rmso)"
|
|
|
|
# Assign detached boot drive (if applicable)
|
|
if [[ ${arch} = [67] ]]
|
|
then
|
|
echo
|
|
say in blue 'Identify the removable boot drive from the list of available devices below'
|
|
lsblk -d -o name,model,size,mountpoint | grep -v "archiso\|$drive"
|
|
say in uline white dim '## Removable drives usually begin with "sd" (sda, sdb, sd[*])'
|
|
while true
|
|
do
|
|
ask for external_drive 'Removable boot device'
|
|
if [ ${external_drive} ] && lsblk -o name | grep -v ${drive} | grep -q -w ${external_drive}
|
|
then
|
|
break
|
|
else
|
|
lsblk -d -o name,model,size,mountpoint | grep -v "archiso\|$drive"
|
|
if [ -z ${external_drive} ]
|
|
then
|
|
say as warning 'Field cannot be empty, try again'
|
|
elif [ ${external_drive} = ${drive} ]
|
|
then
|
|
say as warning 'Boot drive cannot be the same as the system drive, try again'
|
|
else
|
|
say as warning 'Invalid selection or drive not available, try again'
|
|
fi
|
|
fi
|
|
done
|
|
say as success "Detached boot drive set as $(tput smso)/dev/${external_drive}$(tput rmso)"
|
|
fi
|
|
}
|
|
|
|
# Virtual machine defaults
|
|
function virtualMachineSetup
|
|
{
|
|
if ls -l /dev/disk/* | grep -q VBOX
|
|
then
|
|
hostname=vbox
|
|
drive=sda
|
|
external_drive=sdb
|
|
else
|
|
hostname=qemu
|
|
if ls -l /dev/disk/* | grep -q virtio
|
|
then
|
|
drive=vda
|
|
elif ls -l /dev/disk/* | grep -q QEMU
|
|
then
|
|
drive=sda
|
|
fi
|
|
fi
|
|
|
|
username=user
|
|
userpass=123
|
|
lukspass=12345678
|
|
grubpass=123456
|
|
|
|
echo
|
|
say in uline white dim 'Troubleshooting defaults loaded'
|
|
}
|
|
|
|
function hooks
|
|
{
|
|
# Common pacman.conf
|
|
install /dev/stdin /mnt/opt/local/hooks/pacman.conf <<- 'hook'
|
|
#!/usr/bin/env bash
|
|
if [ -f /etc/pacman.conf.pacnew ]
|
|
then
|
|
sed -e '/ParallelDownloads/c ParallelDownloads = 10' \
|
|
-e '/Color/c Color' /etc/pacman.conf.pacnew >/etc/pacman.conf
|
|
rm /etc/pacman.conf.pacnew
|
|
fi
|
|
hook
|
|
|
|
case ${filesystem} in
|
|
zfs)
|
|
# pacman.conf
|
|
sed -e "/Color\/c Color/a\\
|
|
-e '/^\\\[core\\\]$/i [myvezfs]\\\\\\
|
|
Server = https://mirror.myvelabs.com/repo/\$repo\\\\\\
|
|
Server = https://repo.myvelabs.com/\$repo\\\n' \\\\" \
|
|
-i /mnt/opt/local/hooks/pacman.conf
|
|
|
|
cat >/mnt/etc/pacman.d/hooks/100-pacman.conf.hook <<- pacman
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = pacman
|
|
|
|
[Action]
|
|
Description = Fixing pacman.conf
|
|
When = PostTransaction
|
|
Exec = /opt/local/hooks/pacman.conf
|
|
pacman
|
|
|
|
# mkinitcpio.conf
|
|
install /dev/stdin /mnt/opt/local/hooks/mkinitcpio.conf <<- 'hook'
|
|
#!/usr/bin/env bash
|
|
grep '^HOOKS' /etc/mkinitcpio.conf | sed 's/filesystems fsck/zfs filesystems/' >/etc/mkinitcpio.conf.d/zz-hooks.conf
|
|
|
|
sed -e "s|%PKGBASE%|${linux_kernel}|g" \\
|
|
-e "s/^fallback/#&/g" \\
|
|
-e "s/ 'fallback'//" \\
|
|
/usr/share/mkinitcpio/hook.preset >/etc/mkinitcpio.d/${linux_kernel}.preset
|
|
hook
|
|
|
|
cat >/mnt/etc/pacman.d/hooks/85-mkinitcpio.conf.hook <<- mkinitcpio
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = mkinitcpio
|
|
|
|
[Action]
|
|
Description = Fixing mkinitcpio.conf
|
|
When = PostTransaction
|
|
Exec = /opt/local/hooks/mkinitcpio.conf
|
|
mkinitcpio
|
|
;;
|
|
*)
|
|
# pacman.conf hook
|
|
cat >/mnt/etc/pacman.d/hooks/100-pacman.conf.hook <<- pacman
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = pacman
|
|
|
|
[Action]
|
|
Description = Fixing pacman.conf
|
|
When = PostTransaction
|
|
Exec = /opt/local/hooks/pacman.conf
|
|
pacman
|
|
;;
|
|
esac
|
|
}
|
|
|
|
##
|
|
## functions end
|
|
##
|
|
|
|
##
|
|
## Static variables
|
|
##
|
|
|
|
# Determine network controller
|
|
if lspci | grep -q 'Network controller'
|
|
then
|
|
if ! lspci | grep -q 'Ethernet controller'
|
|
then
|
|
iwd=iwd
|
|
systemd_services+=(iwd.service)
|
|
fi
|
|
fi
|
|
|
|
# Determine ucode
|
|
if lscpu | grep 'Model name:' | grep -q AMD
|
|
then
|
|
ucode=amd-ucode
|
|
elif lscpu | grep 'Model name:' | grep -q Intel
|
|
then
|
|
ucode=intel-ucode
|
|
else
|
|
die 'Unable to determine CPU type'
|
|
fi
|
|
|
|
# Detect bluetooth
|
|
if lsmod | grep -q bluetooth || [ -d /sys/class/bluetooth ]
|
|
then
|
|
bluetooth=(bluez bluez-utils)
|
|
fi
|
|
|
|
# Read flags
|
|
rm -f sshkeys
|
|
while [ ${1} ]
|
|
do
|
|
case ${1} in
|
|
-s | --ssh-key )
|
|
if [ "${2}" ]
|
|
then
|
|
echo "${2}" >>sshkeys
|
|
if ! ssh-keygen -l -f sshkeys >/dev/null
|
|
then
|
|
die 'Invalid SSH public key detected'
|
|
fi
|
|
shift
|
|
fi
|
|
;;
|
|
-o | --option )
|
|
if [ "${2}" ]
|
|
then
|
|
arch=${2}
|
|
shift
|
|
fi
|
|
;;
|
|
-c | --cache )
|
|
if [ "${2}" ]
|
|
then
|
|
cacheserver=${2}
|
|
shift
|
|
fi
|
|
;;
|
|
-k | --kernel )
|
|
case "${2}" in
|
|
linux) linux_kernel=linux
|
|
shift
|
|
;;
|
|
linux-lts) linux_kernel=linux-lts
|
|
shift
|
|
;;
|
|
linux-hardened) linux_kernel=linux-hardened
|
|
shift
|
|
;;
|
|
linux-zen) linux_kernel=linux-zen
|
|
shift
|
|
;;
|
|
*) die "Invalid option for flag \"${1}\""
|
|
;;
|
|
esac
|
|
;;
|
|
-f | --filesystem )
|
|
case "${2}" in
|
|
ext4)
|
|
filesystem_choice=1
|
|
shift
|
|
;;
|
|
btrfs)
|
|
filesystem_choice=2
|
|
shift
|
|
;;
|
|
zfs)
|
|
filesystem_choice=3
|
|
shift
|
|
;;
|
|
*) die "Invalid option for flag \"${1}\""
|
|
;;
|
|
esac
|
|
;;
|
|
-y | --noconfirm )
|
|
noconfirm=yes
|
|
load_defaults=y
|
|
shred_installation_disk=n
|
|
shred_boot=n
|
|
begin_install=Y
|
|
;;
|
|
-? | -h | --help )
|
|
cat <<- help
|
|
Parameters:
|
|
-s, --ssh-key Add SSH public key (enclosed in quotes)
|
|
-o, --option Arch installation setup
|
|
-c, --cache Pacman cache server
|
|
-k, --kernel Linux kernel
|
|
-f, --filesystem Filesystem choice
|
|
-y, --noconfirm Skip choices
|
|
-?, -h, --help This help screen
|
|
help
|
|
exit 0
|
|
;;
|
|
* ) die "Unknown flag: ${1}"
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
clear
|
|
|
|
say in uline white dim "## rev ${revision}"
|
|
echo
|
|
say in bold blue "$(tput smso)Arch Linux Installer"
|
|
echo
|
|
say in yellow "Select a system configuration from the options below"
|
|
cat <<- menu
|
|
1) Basic
|
|
2) Full Disk Encryption with EFISTUB
|
|
3) Full Disk Encryption with Secured GRUB
|
|
4) Yubikey Full Disk Encryption with EFISTUB
|
|
5) Yubikey Full Disk Encryption with Secured GRUB
|
|
menu
|
|
|
|
if ls -l /dev/disk/* | grep -q 'virtio\|QEMU'
|
|
then
|
|
echo
|
|
until [[ ${arch} = [1-5] ]]
|
|
do
|
|
ask for arch in press ':'
|
|
echo
|
|
[[ ${arch} = [1-5] ]] || say as warning 'Invalid selection, type an option from 1 to 5'
|
|
done
|
|
else
|
|
cat <<- menu
|
|
6) Full Disk Encryption with Detached LUKS Header and Secured GRUB
|
|
7) Yubikey Encrypted Detached LUKS Header with Secured GRUB
|
|
menu
|
|
echo
|
|
until [[ ${arch} = [1-7] ]]
|
|
do
|
|
ask for arch in press ':'
|
|
echo
|
|
[[ ${arch} = [1-7] ]] || say as warning 'Invalid selection, type an option from 1 to 7'
|
|
done
|
|
fi
|
|
|
|
# Prevent continuing unless yubikey is detected, only applies to yubikey setups
|
|
if [[ ${arch} = [457] ]] && ! dmesg | grep -q -i YubiKey
|
|
then
|
|
say as warning 'Yubikey not detected'
|
|
say as warning 'Insert one to continue...'
|
|
until dmesg | grep -q -i YubiKey
|
|
do
|
|
sleep 1
|
|
done
|
|
fi
|
|
|
|
clear
|
|
|
|
say in uline white dim "## rev ${revision}"
|
|
echo -e -n "\n$(tput setab 7)$(tput setaf 4)$(tput bold)\t"
|
|
case ${arch} in
|
|
1) echo 'Basic Installation' ;;
|
|
2) echo 'Full Disk Encryption with EFISTUB' ;;
|
|
3) echo 'Full Disk Encryption with Secured GRUB' ;;
|
|
4) echo 'Yubikey Full Disk Encryption with EFISTUB' ;;
|
|
5) echo 'Yubikey Full Disk Encryption with Secured GRUB' ;;
|
|
6) echo 'Full Disk Encryption with Detached LUKS Header and Secured GRUB' ;;
|
|
7) echo 'Yubikey Encrypted Detached LUKS Header with Secured GRUB' ;;
|
|
esac
|
|
echo "$(tput sgr0)"
|
|
|
|
presskeytoresume
|
|
|
|
# Initial setup
|
|
if ls -l /dev/disk/* | grep -q 'VBOX\|virtio\|QEMU'
|
|
then
|
|
cacheserver=http://192.168.122.1:9090
|
|
|
|
case ${noconfirm} in
|
|
yes)
|
|
virtualMachineSetup
|
|
;;
|
|
*)
|
|
echo
|
|
while true
|
|
do
|
|
ask for load_defaults in press yellow 'Would you like to load virtual machine troubleshooting defaults? (y/n)'
|
|
case ${load_defaults} in
|
|
[yY])
|
|
virtualMachineSetup
|
|
break
|
|
;;
|
|
[nN])
|
|
initialSetup
|
|
break
|
|
;;
|
|
*)
|
|
echo
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
esac
|
|
else
|
|
initialSetup
|
|
fi
|
|
|
|
# Linux firmware
|
|
if ls -l /dev/disk/* | grep -q VBOX
|
|
then
|
|
linux_firmware=(virtualbox-guest-utils)
|
|
systemd_services+=(vboxservice.service)
|
|
elif ls -l /dev/disk/* | grep -q 'virtio\|QEMU'
|
|
then
|
|
linux_firmware=(qemu-guest-agent spice-vdagent)
|
|
systemd_services+=(qemu-guest-agent.service)
|
|
else
|
|
# Detect needed firmwares
|
|
linux_firmware=(linux-firmware)
|
|
for firmware in $(pacman -Ssq linux-firmware- | sed 's/linux-firmware-//')
|
|
do
|
|
if lspci | grep -q -i ${firmware}
|
|
then
|
|
linux_firmware+=(linux-firmware-${firmware})
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Choose linux kernel
|
|
echo
|
|
say in blue "Select a linux kernel:"
|
|
cat <<- menu
|
|
1) linux
|
|
2) linux-lts
|
|
3) linux-hardened
|
|
4) linux-zen
|
|
menu
|
|
until [ ${linux_kernel} ]
|
|
do
|
|
ask for linux_kernel_choice in press ':'
|
|
case ${linux_kernel_choice} in
|
|
1) linux_kernel=linux ;;
|
|
2) linux_kernel=linux-lts ;;
|
|
3) linux_kernel=linux-hardened ;;
|
|
4) linux_kernel=linux-zen ;;
|
|
*) echo
|
|
say as warning 'Invalid selection, type an option from 1 to 4'
|
|
;;
|
|
esac
|
|
done
|
|
echo
|
|
say as success "You have chosen $(tput smso)${linux_kernel}$(tput rmso) for a kernel"
|
|
|
|
# GPU firmware
|
|
if lspci | grep VGA | grep -q QXL
|
|
then
|
|
linux_firmware+=(xf86-video-qxl)
|
|
fi
|
|
if lspci | grep VGA | grep -q NVIDIA
|
|
then
|
|
case ${linux_kernel} in
|
|
linux) nvidia=(nvidia-open nvidia-settings) ;;
|
|
linux-lts) nvidia=(nvidia-lts nvidia-settings) ;;
|
|
*) nvidia=(nvidia-dkms nvidia-settings) ;;
|
|
esac
|
|
fi
|
|
|
|
# Choose filesystem
|
|
echo
|
|
say in blue "Select a filesystem:"
|
|
cat <<- menu
|
|
1) ext4
|
|
2) btrfs
|
|
menu
|
|
if modprobe zfs >/dev/null 2>&1
|
|
then
|
|
cat <<- MENU
|
|
3) zfs
|
|
MENU
|
|
fi
|
|
until [[ ${filesystem_choice} = [1-3] ]]
|
|
do
|
|
ask for filesystem_choice in press ':'
|
|
if ! [[ ${filesystem_choice} = [1-3] ]]
|
|
then
|
|
echo
|
|
say as warning 'Invalid selection, type an option from 1 to 3'
|
|
fi
|
|
done
|
|
case ${filesystem_choice} in
|
|
1)
|
|
filesystem=ext4
|
|
rootflags='quiet rw'
|
|
;;
|
|
2)
|
|
filesystem=btrfs
|
|
rootflags='rootflags=subvol=@ quiet rw'
|
|
btrfs=(btrfs-progs timeshift cronie)
|
|
systemd_services+=(cronie.service)
|
|
;;
|
|
3)
|
|
filesystem=zfs
|
|
rootflags='zfs=bootfs quiet rw'
|
|
zfs=(zfs-${linux_kernel} zfs-utils)
|
|
;;
|
|
esac
|
|
echo
|
|
say as success "You have chosen $(tput smso)${filesystem}$(tput rmso) for a filesystem"
|
|
|
|
# Option to automatically decrypt / using a keyfile, only applies to encrypted GRUB/yubikey 1FA setups
|
|
if [[ ${arch} = [3-7] ]]
|
|
then
|
|
echo
|
|
if [[ ${arch} = [457] ]]
|
|
then
|
|
say in uline white dim '## Root will be decrypted as long as yubikey is inserted when prompted'
|
|
fi
|
|
until [[ ${decryptroot} = [yYnN] ]]
|
|
do
|
|
ask for decryptroot in press yellow "Would you like to automatically decrypt the root partition upon boot? (y/n)"
|
|
if ! [[ ${decryptroot} = [yYnN] ]]
|
|
then
|
|
echo
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Skip confirmations if flag is on
|
|
if [ -z ${noconfirm} ]
|
|
then
|
|
|
|
echo
|
|
# Device shredding
|
|
until [[ ${shred_installation_disk} = [yYnN] ]]
|
|
do
|
|
ask for shred_installation_disk in press yellow "Would you like to shred your installation drive? (y/n)"
|
|
|
|
if ! [[ ${shred_installation_disk} = [yYnN] ]]
|
|
then
|
|
echo
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
|
fi
|
|
done
|
|
|
|
if [[ ${arch} = [67] ]]
|
|
then
|
|
echo
|
|
until [[ ${shred_boot} = [yYnN] ]]
|
|
do
|
|
if [[ ${shred_installation_disk} = [yY] ]]
|
|
then
|
|
ask for shred_boot in press yellow "Would you also like to shred your removable drive? (y/n)"
|
|
else
|
|
ask for shred_boot in press yellow "Would you like to shred your removable drive? (y/n)"
|
|
fi
|
|
if ! [[ ${shred_boot} = [yYnN] ]]
|
|
then
|
|
echo
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
|
fi
|
|
done
|
|
fi
|
|
echo
|
|
|
|
if [[ ${shred_installation_disk} = [nN] && ${shred_boot} = [nN] ]] || [[ ${arch} != [67] && ${shred_installation_disk} = [nN] ]]
|
|
then
|
|
say in yellow 'Disk shredding skipped'
|
|
echo
|
|
|
|
elif [[ ${shred_installation_disk} = [yY] || ${shred_boot} = [yY] ]]
|
|
then
|
|
say in blue "Select an overwrite source:"
|
|
cat <<- menu
|
|
1) zero
|
|
2) urandom
|
|
menu
|
|
until [[ ${disk_destroyer} = [12] ]]
|
|
do
|
|
ask for disk_destroyer in press ':'
|
|
if ! [[ ${disk_destroyer} = [12] ]]
|
|
then
|
|
echo
|
|
say as warning 'Invalid selection, type an option from 1 to 2'
|
|
fi
|
|
done
|
|
echo
|
|
|
|
case ${disk_destroyer} in
|
|
1) dd=zero ;;
|
|
2) dd=urandom ;;
|
|
esac
|
|
fi
|
|
|
|
say as heading "Shredding drives"
|
|
cat <<- warning
|
|
$(tput setab 1)#############################################################$(tput sgr0)
|
|
$(tput setab 1)#############################################################$(tput sgr0)
|
|
$(tput setab 1) $(tput sgr0)
|
|
$(tput setab 1) $(tput blink)!!!WARNING!!! $(tput sgr0)
|
|
$(tput setab 1) !!!ALL DATA WILL BE WIPED FROM THE DRIVE!!! $(tput sgr0)
|
|
$(tput setab 1) $(tput sgr0)
|
|
$(tput setab 1)#############################################################$(tput sgr0)
|
|
$(tput setab 1)#############################################################$(tput sgr0)
|
|
## This will overwrite the drive by one pass
|
|
## Confirm by typing "Y" to proceed
|
|
## Anything other than capital Y will abort the installation
|
|
warning
|
|
ask for begin_install in press ':'
|
|
|
|
if ! [[ ${begin_install} = Y ]]
|
|
then
|
|
echo
|
|
say as warning 'Aborting installation'
|
|
say as warning 'Installer script stopped'
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
fi # Skip disk shredding
|
|
|
|
# Unmount if mounted
|
|
for mounted in /mnt/boot /mnt
|
|
do
|
|
if mount | grep -q -w ${mounted}
|
|
then
|
|
umount -R ${mounted} || die 'A drive or partition is already mounted on /mnt, unable to proceed'
|
|
fi
|
|
done
|
|
|
|
# Export existing zpools
|
|
zpool export -a >/dev/null 2>&1
|
|
|
|
# Close opened luks containers
|
|
for mounted in cryptroot cryptboot
|
|
do
|
|
if dmsetup ls | grep -q -w ${mounted}
|
|
then
|
|
cryptsetup close "/dev/mapper/${mounted}" || die 'LUKS container already exists, unable to proceed'
|
|
fi
|
|
done
|
|
|
|
# Grab disk-id of ${drive}
|
|
installation_disk=$(ls -l /dev/disk/by-id/* | grep "${drive}$" | grep 'wwn\|nvme-uuid\|nvme-nvme\|nvme-eui\|QEMU\|virtio\|VBOX' | awk '{print $9}' | head -1)
|
|
external_boot=$(ls -l /dev/disk/by-id/* | grep "${external_drive}$" | grep 'wwn\|nvme-uuid\|nvme-nvme\|nvme-eui\|QEMU\|virtio\|VBOX\|usb' | awk '{print $9}' | head -1)
|
|
|
|
# Exit if disk IDs are null
|
|
if [ -z ${installation_disk} ]
|
|
then
|
|
die 'Ensure installation disk has a valid ID'
|
|
fi
|
|
if [ ${external_drive} ] && [ -z ${external_boot} ]
|
|
then
|
|
die 'Ensure external boot disk has a valid ID'
|
|
fi
|
|
|
|
echo
|
|
|
|
# Shred installation drive
|
|
blkdiscard --quiet --force --secure ${installation_disk} >/dev/null 2>&1
|
|
wipefs --quiet --force --all ${installation_disk}
|
|
|
|
if [[ ${shred_installation_disk} = [yY] ]]
|
|
then
|
|
say as heading "Shredding ${installation_disk}"
|
|
dd if=/dev/${dd} of=${installation_disk} bs=4M status=progress
|
|
echo 3 >/proc/sys/vm/drop_caches
|
|
echo
|
|
fi
|
|
|
|
# Shred external boot drive
|
|
if [[ ${arch} = [67] ]]
|
|
then
|
|
wipefs --quiet --force --all ${external_boot}
|
|
fi
|
|
if [[ ${shred_boot} = [yY] ]]
|
|
then
|
|
say as heading "Shredding ${external_boot}"
|
|
dd if=/dev/${dd} of=${external_boot} bs=4M status=progress
|
|
echo 3 >/proc/sys/vm/drop_caches
|
|
say as success 'Device shredded'
|
|
echo
|
|
fi
|
|
|
|
case ${arch} in
|
|
1) # Basic setup
|
|
parted --script --align=optimal ${installation_disk} \
|
|
mklabel gpt \
|
|
mkpart boot 1MiB 300MiB \
|
|
mkpart root 300MiB 100% \
|
|
set 1 esp on
|
|
partprobe ${installation_disk}
|
|
|
|
export {efivol,bootvol}=${installation_disk}-part1
|
|
export {rootpart,rootvol}=${installation_disk}-part2
|
|
|
|
configureVolumes
|
|
pacstrapGenfstab
|
|
|
|
echo
|
|
say as heading 'Configuring efistub'
|
|
efibootmgr --disk ${installation_disk} --part 1 --create --label "Arch Linux (${hostname})" --loader "/vmlinuz-${linux_kernel}" --unicode "initrd=\initramfs-${linux_kernel}.img root=${rootpart} ${rootflags}"
|
|
;;
|
|
|
|
2|4) # FDE with EFISTUB
|
|
parted --script --align=optimal ${installation_disk} \
|
|
mklabel gpt \
|
|
mkpart boot 1MiB 300MiB \
|
|
mkpart root 300MiB 100% \
|
|
set 1 esp on
|
|
partprobe ${installation_disk}
|
|
|
|
export {efivol,bootvol}=${installation_disk}-part1
|
|
rootpart=${installation_disk}-part2
|
|
rootvol=/dev/mapper/cryptroot
|
|
|
|
case ${arch} in
|
|
2)
|
|
case ${filesystem} in
|
|
zfs)
|
|
zpoolencryption="-O encryption=aes-256-gcm \
|
|
-O keyformat=passphrase \
|
|
-O keylocation=prompt"
|
|
rootvol=${installation_disk}-part2
|
|
;;
|
|
*) encryptRootLuks ;;
|
|
esac
|
|
;;
|
|
4) encryptRootYubikey ;;
|
|
esac
|
|
|
|
configureVolumes
|
|
pacstrapGenfstab
|
|
|
|
case ${arch} in
|
|
2)
|
|
echo
|
|
say as heading 'Configuring efistub'
|
|
case ${filesystem} in
|
|
zfs) efibootmgr --disk ${installation_disk} --part 1 --create --label "Arch Linux (${hostname})" --loader "/vmlinuz-${linux_kernel}" --unicode "initrd=\initramfs-${linux_kernel}.img ${rootflags}"
|
|
;;
|
|
btrfs|ext4)
|
|
encryptHook encrypt
|
|
efibootmgr --disk ${installation_disk} --part 1 --create --label "Arch Linux (${hostname})" --loader "/vmlinuz-${linux_kernel}" --unicode "initrd=\initramfs-${linux_kernel}.img cryptdevice=${rootpart}:cryptroot root=/dev/mapper/cryptroot ${rootflags}"
|
|
;;
|
|
esac
|
|
;;
|
|
4)
|
|
chrootYubikey
|
|
echo
|
|
say as heading 'Configuring efistub'
|
|
efibootmgr --disk ${installation_disk} --part 1 --create --label "Arch Linux (${hostname})" --loader "/vmlinuz-${linux_kernel}" --unicode "initrd=\initramfs-${linux_kernel}.img cryptdevice=${rootpart}:cryptroot root=/dev/mapper/cryptroot ${rootflags}"
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
3|5) # FDE with Secured GRUB
|
|
parted --script --align=optimal ${installation_disk} \
|
|
mklabel gpt \
|
|
mkpart efi 1MiB 40MiB \
|
|
mkpart boot 40MiB 300MiB \
|
|
mkpart root 300MiB 100% \
|
|
set 1 esp on
|
|
partprobe ${installation_disk}
|
|
|
|
efivol=${installation_disk}-part1
|
|
bootpart=${installation_disk}-part2
|
|
rootpart=${installation_disk}-part3
|
|
rootvol=/dev/mapper/cryptroot
|
|
bootvol=/dev/mapper/cryptboot
|
|
|
|
case ${arch} in
|
|
3) encryptRootLuks ;;
|
|
5) encryptRootYubikey ;;
|
|
esac
|
|
|
|
encryptBootLuks
|
|
configureVolumes
|
|
generateKeyfile
|
|
pacstrapGenfstab
|
|
|
|
case ${arch} in
|
|
3)
|
|
encryptHook encrypt
|
|
if [[ ${decryptroot} = [yY] ]]
|
|
then
|
|
echo "FILES=(/crypto_keyfile.bin)" >/mnt/etc/mkinitcpio.conf.d/zz-files.conf
|
|
fi
|
|
;;
|
|
5)
|
|
chrootYubikey
|
|
echo "MODULES=(loop)" >/mnt/etc/mkinitcpio.conf.d/zz-modules.conf
|
|
;;
|
|
esac
|
|
|
|
sed "/^GRUB_CMDLINE_LINUX_DEFAULT/c GRUB_CMDLINE_LINUX_DEFAULT=\"cryptdevice=${rootpart}:cryptroot root=/dev/mapper/cryptroot ${rootflags}\"" \
|
|
-i /mnt/etc/default/grub
|
|
grep -q "${rootpart}" /mnt/etc/default/grub || die 'Grub cfg generation failed'
|
|
|
|
arch-chroot /mnt /usr/bin/bash <<- grub
|
|
configureGrub
|
|
grub
|
|
;;
|
|
|
|
6|7) # FDE with detached luks header
|
|
parted --script --align=optimal ${external_boot} \
|
|
mklabel gpt \
|
|
mkpart efi 1MiB 40MiB \
|
|
mkpart boot 40MiB 300MiB \
|
|
mkpart none 300MiB 100% \
|
|
set 1 esp on
|
|
partprobe ${external_boot}
|
|
|
|
rootpart=${installation_disk}
|
|
efivol=${external_boot}-part1
|
|
bootpart=${external_boot}-part2
|
|
rootvol=/dev/mapper/cryptroot
|
|
bootvol=/dev/mapper/cryptboot
|
|
luks_header=--header=header.img
|
|
|
|
case ${arch} in
|
|
6) encryptRootLuks ;;
|
|
7) encryptRootYubikey ;;
|
|
esac
|
|
|
|
encryptBootLuks
|
|
configureVolumes
|
|
mv header.img /mnt/boot
|
|
generateKeyfile
|
|
pacstrapGenfstab
|
|
|
|
# Mkinitcpio files
|
|
cpiofiles=(/boot/header.img)
|
|
|
|
case ${arch} in
|
|
6)
|
|
sed -e '/for cryptopt in/i\\n local headerFlag=false' \
|
|
-e '/case ${cryptopt} in/a\
|
|
header)\
|
|
cryptargs="${cryptargs} --header /boot/header.img"\
|
|
headerFlag=true\
|
|
;;' \
|
|
-e 's/cryptsetup isLuks/$headerFlag || &/' \
|
|
-i /mnt/usr/lib/initcpio/hooks/encrypt
|
|
|
|
encryptHook encrypt
|
|
|
|
# encrypt hook
|
|
install /dev/stdin /mnt/opt/local/hooks/encrypt-initcpio <<- 'hook'
|
|
#!/usr/bin/env bash
|
|
if [ -f /usr/lib/initcpio/hooks/encrypt.pacnew ]
|
|
then
|
|
sed -e '/for cryptopt in/i\\n local headerFlag=false' \
|
|
-e '/case ${cryptopt} in/a\
|
|
header)\
|
|
cryptargs="${cryptargs} --header /boot/header.img"\
|
|
headerFlag=true\
|
|
;;' \
|
|
-e 's/cryptsetup isLuks/$headerFlag || &/' \
|
|
-i /usr/lib/initcpio/hooks/encrypt.pacnew
|
|
mv /usr/lib/initcpio/hooks/encrypt.pacnew /usr/lib/initcpio/hooks/encrypt
|
|
fi
|
|
hook
|
|
cat >/mnt/etc/pacman.d/hooks/85-encrypt.hook <<- encrypt
|
|
[Trigger]
|
|
Operation = Install
|
|
Operation = Upgrade
|
|
Type = Package
|
|
Target = cryptsetup
|
|
|
|
[Action]
|
|
Description = Fixing encrypt hook for detached headers
|
|
When = PostTransaction
|
|
Exec = /opt/local/hooks/encrypt-initcpio
|
|
encrypt
|
|
|
|
if [[ ${decryptroot} = [yY] ]]
|
|
then
|
|
cpiofiles+=(/crypto_keyfile.bin)
|
|
fi
|
|
;;
|
|
7)
|
|
chrootYubikey
|
|
sed '/YKFDE_LUKS_OPTIONS/c YKFDE_LUKS_OPTIONS="--header=/boot/header.img"' -i /mnt/etc/ykfde.conf
|
|
;;
|
|
esac
|
|
|
|
# mkinitcpio.conf
|
|
echo "FILES=(${cpiofiles[@]})" >/mnt/etc/mkinitcpio.conf.d/zz-files.conf
|
|
echo "MODULES=(ext2 loop)" >/mnt/etc/mkinitcpio.conf.d/zz-modules.conf
|
|
|
|
sed "/^GRUB_CMDLINE_LINUX_DEFAULT/c GRUB_CMDLINE_LINUX_DEFAULT=\"cryptdevice=${rootpart}:cryptroot:header root=/dev/mapper/cryptroot ${rootflags}\"" \
|
|
-i /mnt/etc/default/grub
|
|
grep -q "${rootpart}" /mnt/etc/default/grub || die 'Grub cfg generation failed'
|
|
|
|
arch-chroot /mnt /usr/bin/bash <<- grub
|
|
configureGrub
|
|
grub
|
|
;;
|
|
esac
|
|
|
|
unset lukspass grubpass
|
|
|
|
# Re-activate mkinitcpio
|
|
rm -f /mnt/etc/pacman.d/hooks/90-mkinitcpio-install.hook
|
|
sed -e "s|%PKGBASE%|${linux_kernel}|g" \
|
|
-e "s/^fallback/#&/g" \
|
|
-e "s/ 'fallback'//" \
|
|
/mnt/usr/share/mkinitcpio/hook.preset >/mnt/etc/mkinitcpio.d/${linux_kernel}.preset
|
|
rsync -a /mnt/usr/lib/modules/*/vmlinuz /mnt/boot/vmlinuz-${linux_kernel}
|
|
|
|
# Copy some networking files
|
|
ln -s -f /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
|
|
rsync -a /etc/systemd/network/20-ethernet.network /mnt/etc/systemd/network/
|
|
|
|
if [ ${iwd} ]
|
|
then
|
|
rsync -a /usr/local/bin/iwd-connect /mnt/opt/local/bin/
|
|
chmod +x /mnt/opt/local/bin/iwd-connect
|
|
|
|
mkdir -p /mnt/var/lib/iwd /mnt/etc/iwd
|
|
|
|
rsync -a /var/lib/iwd/*.psk /mnt/var/lib/iwd/
|
|
# sed -i '/^Passphrase=/d' /mnt/var/lib/iwd/*.psk
|
|
|
|
cat >/mnt/etc/iwd/main.conf <<- iwd
|
|
[General]
|
|
EnableNetworkConfiguration=true
|
|
iwd
|
|
fi
|
|
|
|
# chroot setup
|
|
arch-chroot /mnt /usr/bin/bash <<- chroot || die "chroot setup failed"
|
|
curl --fail --silent https://git.myvelabs.com/lab/archlinux/raw/branch/master/functions/chroot -o /tmp/chroot
|
|
/usr/bin/bash /tmp/chroot
|
|
chroot
|
|
|
|
# System services
|
|
arch-chroot /mnt systemctl enable ${systemd_services[@]} || die "Unable to start systemd services"
|
|
|
|
# SSH Authentication
|
|
if [ -f sshkeys ]
|
|
then
|
|
cat sshkeys >/mnt/home/${username}/.ssh/authorized_keys
|
|
fi
|
|
|
|
# DE installer
|
|
install /dev/stdin /mnt/opt/local/bin/desktop <<- desktop
|
|
#!/usr/bin/env bash
|
|
set -e
|
|
if curl --fail --silent https://git.myvelabs.com/lab/archlinux/raw/branch/master/functions/desktop -o /tmp/desktop
|
|
then
|
|
/usr/bin/bash /tmp/desktop \${@}
|
|
sudo rm -f \${0}
|
|
else
|
|
exit 1
|
|
fi
|
|
desktop
|
|
|
|
# BTRFS fresh snapshot
|
|
case ${filesystem} in
|
|
btrfs)
|
|
arch-chroot /mnt /usr/bin/bash <<- "timeshift"
|
|
# Take fresh snapshots
|
|
timeshift --btrfs >/dev/null
|
|
timeshift --create --comments "Fresh install" >/dev/null
|
|
sed \
|
|
-e '/stop_cron_emails/ s/false/true/' \
|
|
-e '/schedule_monthly/ s/false/true/' \
|
|
-e '/schedule_weekly/ s/false/true/' \
|
|
-e '/schedule_daily/ s/false/true/' \
|
|
-e '/schedule_hourly/ s/false/true/' \
|
|
-e '/schedule_boot/ s/true/false/' \
|
|
-e '/count_monthly/ s/: ".*",/: "3",/' \
|
|
-e '/count_weekly/ s/: ".*",/: "4",/' \
|
|
-e '/count_daily/ s/: ".*",/: "5",/' \
|
|
-e '/count_hourly/ s/: ".*",/: "6",/' \
|
|
-i /etc/timeshift/timeshift.json
|
|
echo -e '\e[1;33mSnapshots taken\e[0m\n'
|
|
timeshift
|
|
;;
|
|
esac
|
|
|
|
# Reboot only if script succeeded
|
|
if arch-chroot /mnt uname -a | grep -q Linux
|
|
then
|
|
for mounted in /mnt/boot /mnt
|
|
do
|
|
umount -R ${mounted}
|
|
done
|
|
case ${filesystem} in
|
|
zfs)
|
|
zfs snapshot zroot/ROOT@fresh-installation
|
|
zpool export -a
|
|
;;
|
|
esac
|
|
say as success in bold "Installer has completed and system drive has been unmounted"
|
|
say as success in bold "Boot into the new system, connect to a network and install a DE by running $(tput smso)desktop$(tput rmso) in the terminal"
|
|
say as success in bold "Rebooting..."
|
|
echo
|
|
reboot
|
|
else
|
|
die 'Something does not feel right'
|
|
fi
|