mirror of
https://git.myvelabs.com/lab/archlinux.git
synced 2025-12-17 21:26:25 +00:00
1582 lines
42 KiB
Bash
1582 lines
42 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -a
|
||
|
|
set -E
|
||
|
|
revision=1.0
|
||
|
|
|
||
|
|
# Fill in details
|
||
|
|
hostname=
|
||
|
|
newuser=
|
||
|
|
port=
|
||
|
|
|
||
|
|
primaryip=
|
||
|
|
gateway=
|
||
|
|
primaryip6=
|
||
|
|
gateway6=
|
||
|
|
subnet=255.255.255.0
|
||
|
|
|
||
|
|
# ZFS key
|
||
|
|
zfsgpgkey=D0F2AE55C1BF11A026D155813A658A95B8CFCC51
|
||
|
|
|
||
|
|
# SSH keys
|
||
|
|
curl --silent --fail https://myvelabs.com/lab/archlinux/raw/branch/master/notes/sshkeys.pub -o sshkeys
|
||
|
|
|
||
|
|
# Static variables
|
||
|
|
originaliface=$(dmesg | grep $(ip r | grep 'default via' | awk '{print $5}') | grep 'renamed from' | awk '{print $NF}')
|
||
|
|
iface=$(ip r | grep 'default via' | awk '{print $5}')
|
||
|
|
|
||
|
|
# 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
|
||
|
|
}
|
||
|
|
|
||
|
|
# Quit if zfs modules aren't loaded
|
||
|
|
modprobe zfs || die 'ZFS modules are not loaded'
|
||
|
|
|
||
|
|
##
|
||
|
|
## Declare functions
|
||
|
|
##
|
||
|
|
|
||
|
|
# 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 5) $(tput bold)) # bold magenta
|
||
|
|
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
|
||
|
|
}
|
||
|
|
|
||
|
|
# Repeat a command until it exits with code 0
|
||
|
|
function repeat
|
||
|
|
{
|
||
|
|
until ${@}
|
||
|
|
do
|
||
|
|
sleep 3
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
# Fetch flags
|
||
|
|
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
|
||
|
|
;;
|
||
|
|
-p | --port )
|
||
|
|
if [ "${2}" ]
|
||
|
|
then
|
||
|
|
port=${2}
|
||
|
|
shift
|
||
|
|
fi
|
||
|
|
;;
|
||
|
|
-c | --cache )
|
||
|
|
if [ "${2}" ]
|
||
|
|
then
|
||
|
|
cacheserver=${2}
|
||
|
|
shift
|
||
|
|
fi
|
||
|
|
;;
|
||
|
|
-? | -h | --help )
|
||
|
|
cat <<- help
|
||
|
|
Parameters:
|
||
|
|
-s, --ssh-key Add SSH public key (enclosed in quotes)
|
||
|
|
-p, --port SSH port
|
||
|
|
-c, --cache Pacman cache server
|
||
|
|
-?, -h, --help This help screen
|
||
|
|
help
|
||
|
|
exit 0
|
||
|
|
;;
|
||
|
|
* )
|
||
|
|
die "Unknown flag: ${1}"
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
shift
|
||
|
|
done
|
||
|
|
|
||
|
|
# Ensure pacman.conf settings are properly set
|
||
|
|
if [ ! -f /etc/pacman.conf.bkp ]
|
||
|
|
then
|
||
|
|
mv /etc/pacman.conf /etc/pacman.conf.bkp
|
||
|
|
fi
|
||
|
|
sed -e '/ParallelDownloads/c ParallelDownloads = 10' \
|
||
|
|
-e '/Color/c Color' \
|
||
|
|
-e '/\[core\]/i [myvezfs]\
|
||
|
|
Server = https://repo.myvelabs.com/$repo\n' /etc/pacman.conf.bkp >/etc/pacman.conf
|
||
|
|
|
||
|
|
# Internet connection check
|
||
|
|
if ping -q -c 1 -W 5 archlinux.org >/dev/null || ! ping -q -c 1 -W 5 google.com >/dev/null
|
||
|
|
then
|
||
|
|
timedatectl set-ntp true
|
||
|
|
pacman -Sy --ask 4 >/dev/null 2>&1
|
||
|
|
else
|
||
|
|
die 'No internet connectivity detected, plug in an ethernet cable and try again'
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Detect needed firmwares
|
||
|
|
linux_firmware=(qemu-guest-agent)
|
||
|
|
virtualMachineService=qemu-guest-agent
|
||
|
|
|
||
|
|
# Virtual machine defaults
|
||
|
|
function virtualMachineDefaults
|
||
|
|
{
|
||
|
|
hostname=testzfs
|
||
|
|
userpass=123
|
||
|
|
lukspass=12345678
|
||
|
|
installation_drive_array=($(lsblk -d -n -o name | grep -v 'loop0\|sr0'))
|
||
|
|
|
||
|
|
say in italic 'Troubleshooting defaults loaded'
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
|
||
|
|
# Grab system variables
|
||
|
|
function initialSetup
|
||
|
|
{
|
||
|
|
# User password
|
||
|
|
say as title "Set a password for user"
|
||
|
|
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 "user@${hostname}'s password has been saved"
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Encryption key
|
||
|
|
say as title "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
|
||
|
|
|
||
|
|
# Assign system drive
|
||
|
|
say as title "Identify the system drive from the list of available devices below"
|
||
|
|
lsblk -d -o name,model,size,mountpoint | grep -v "archiso\|sr0"
|
||
|
|
say in italic '## SSD and HDD device format begins with "sd" or "hd" (sda, sdb, sd[*])'
|
||
|
|
say in italic '## NVME and PCI device format is "nvme[*]n1" (nvme0n1, nvme1n1, nvme[*]n1)'
|
||
|
|
while true
|
||
|
|
do
|
||
|
|
ask for installation_drive "Installation device"
|
||
|
|
if [ ${installation_drive} ] && \
|
||
|
|
lsblk -o name | grep -q -w ${installation_drive}
|
||
|
|
then
|
||
|
|
installation_drive_array=${installation_drive}
|
||
|
|
break
|
||
|
|
else
|
||
|
|
lsblk -d -o name,model,size,mountpoint | grep -v "archiso\|sr0"
|
||
|
|
if [ -z ${installation_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 /dev/${installation_drive}"
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
|
||
|
|
# Generate SSH keys
|
||
|
|
function gensshid
|
||
|
|
{
|
||
|
|
ssh-keygen -q \
|
||
|
|
-t ed25519 \
|
||
|
|
-P "" \
|
||
|
|
-C "${USER}@${hostname}" \
|
||
|
|
-f ~/.ssh/id_ed25519
|
||
|
|
mkdir ~/.ssh/sockets/
|
||
|
|
}
|
||
|
|
|
||
|
|
clear
|
||
|
|
|
||
|
|
say in italic "## rev ${revision}"
|
||
|
|
|
||
|
|
echo
|
||
|
|
say in uline white dim "Full Disk Encryption"
|
||
|
|
echo
|
||
|
|
presskeytoresume
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Setup
|
||
|
|
if ls -l /dev/disk/* | grep -q 'VBOX\|virtio\|QEMU'
|
||
|
|
then
|
||
|
|
while true
|
||
|
|
do
|
||
|
|
ask for load_defaults in blue press "Would you like to load virtual machine troubleshooting defaults? (y/n)"
|
||
|
|
echo
|
||
|
|
|
||
|
|
case ${load_defaults} in
|
||
|
|
[yY])
|
||
|
|
virtualMachineDefaults
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
[nN])
|
||
|
|
initialSetup
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
else
|
||
|
|
initialSetup
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Fetch disk ID of installation disks
|
||
|
|
for drive in ${installation_drive_array[@]}
|
||
|
|
do
|
||
|
|
installation_disks+=(/dev/${drive})
|
||
|
|
done
|
||
|
|
|
||
|
|
# Default zroot layout
|
||
|
|
zrootlayout="$(printf '%s2 ' ${installation_disks[@]})"
|
||
|
|
|
||
|
|
# Device shredding
|
||
|
|
while true
|
||
|
|
do
|
||
|
|
ask for shred_disk in blue press 'Would you like to shred your installation drive? (y/n)'
|
||
|
|
echo
|
||
|
|
|
||
|
|
case ${shred_disk} in
|
||
|
|
[yY])
|
||
|
|
say as title "Select an overwrite source"
|
||
|
|
cat <<- shred
|
||
|
|
1) zero
|
||
|
|
2) urandom
|
||
|
|
shred
|
||
|
|
while true
|
||
|
|
do
|
||
|
|
ask for shred_source in press ":"
|
||
|
|
case ${shred_source} in
|
||
|
|
1) shred=zero
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
2) shred=urandom
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
*) echo
|
||
|
|
say as warning "Invalid selection, type an option from 1 to 2"
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
echo
|
||
|
|
# Disk shredding function
|
||
|
|
function destroy_disk
|
||
|
|
{
|
||
|
|
say as title "Shredding ${1}"
|
||
|
|
dd if=/dev/${shred} of=${1} bs=4M status=progress
|
||
|
|
echo 3 >/proc/sys/vm/drop_caches
|
||
|
|
say as success "${1} shredded"
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
[nN])
|
||
|
|
say in yellow "Disk shredding skipped"
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Empty disk shredding function
|
||
|
|
function destroy_disk
|
||
|
|
{
|
||
|
|
true
|
||
|
|
}
|
||
|
|
break
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
say as warning 'Not a valid answer, type "y" or "n"'
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
|
||
|
|
done
|
||
|
|
|
||
|
|
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 in bold 'Aborting installation
|
||
|
|
Installer script stopped'
|
||
|
|
echo
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Unmount if mounted
|
||
|
|
until ! mount | grep -q /mnt/boot
|
||
|
|
do
|
||
|
|
umount /mnt/boot
|
||
|
|
done
|
||
|
|
|
||
|
|
# Export zpool
|
||
|
|
zpool export -a
|
||
|
|
|
||
|
|
# Clean slate
|
||
|
|
for drive in ${installation_disks[@]}
|
||
|
|
do
|
||
|
|
destroy_disk ${drive} &
|
||
|
|
done
|
||
|
|
wait
|
||
|
|
for drive in ${installation_disks[@]}
|
||
|
|
do
|
||
|
|
blkdiscard --quiet --force --secure ${drive} >/dev/null 2>&1
|
||
|
|
wipefs --quiet --force --all ${drive}
|
||
|
|
done
|
||
|
|
|
||
|
|
# Partition each drive
|
||
|
|
say as heading "Partitioning system drive"
|
||
|
|
for drive in ${installation_disks[@]}
|
||
|
|
do
|
||
|
|
parted --script --align=optimal ${drive} \
|
||
|
|
mklabel msdos \
|
||
|
|
mkpart primary 1MiB 300MiB \
|
||
|
|
mkpart primary 300MiB 100% \
|
||
|
|
set 1 boot on
|
||
|
|
partprobe ${drive}
|
||
|
|
done
|
||
|
|
say as success "Partitioning completed!"
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Format zfs
|
||
|
|
say as heading "Formatting zfs partitions"
|
||
|
|
until printf '%s' "${lukspass}" |\
|
||
|
|
zpool create -f zroot \
|
||
|
|
-o ashift=12 \
|
||
|
|
-o autotrim=on \
|
||
|
|
-R /mnt \
|
||
|
|
-O acltype=posixacl \
|
||
|
|
-O canmount=off \
|
||
|
|
-O compression=lz4 \
|
||
|
|
-O dnodesize=auto \
|
||
|
|
-O normalization=formD \
|
||
|
|
-O atime=off \
|
||
|
|
-O xattr=sa \
|
||
|
|
-O mountpoint=none \
|
||
|
|
-O encryption=aes-256-gcm \
|
||
|
|
-O keyformat=passphrase \
|
||
|
|
-O keylocation=prompt \
|
||
|
|
${zrootlayout} \
|
||
|
|
>/dev/null
|
||
|
|
do
|
||
|
|
sleep 1
|
||
|
|
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=/etc/letsencrypt zroot/HTTPS
|
||
|
|
zfs create -o mountpoint=/docker zroot/DOCKER
|
||
|
|
zfs create -o mountpoint=/docker/SQL -o recordsize=16K zroot/DOCKER/SQL
|
||
|
|
zpool export zroot
|
||
|
|
zpool import -R /mnt zroot -N -d ${installation_disks}2
|
||
|
|
printf '%s' "${lukspass}" | zfs load-key zroot
|
||
|
|
zfs mount zroot/ROOT
|
||
|
|
zfs mount -a
|
||
|
|
|
||
|
|
# Format efi partition
|
||
|
|
for drive in ${installation_disks[@]}
|
||
|
|
do
|
||
|
|
yes | mkfs.fat -F 32 ${drive}1
|
||
|
|
done
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Mount efi partition
|
||
|
|
install -d -m 755 /mnt/boot/
|
||
|
|
mount ${installation_disks}1 /mnt/boot
|
||
|
|
|
||
|
|
# Fix directory permissions
|
||
|
|
chmod 1777 /mnt/var/tmp
|
||
|
|
|
||
|
|
# # Confirm mounts
|
||
|
|
findmnt '/mnt' | grep -q -w 'zroot/ROOT' || die 'Root partition has not been mounted properly'
|
||
|
|
findmnt '/mnt/boot' | grep -q -w '/mnt/boot' || die 'Boot partition has not been mounted properly'
|
||
|
|
|
||
|
|
# Default packages
|
||
|
|
archpkgs="${linux_firmware[@]} \
|
||
|
|
sudo openssh syslinux fakeroot \
|
||
|
|
vim pacman-contrib bash-completion \
|
||
|
|
mkinitcpio-netconf mkinitcpio-tinyssh tinyssh \
|
||
|
|
reflector rsync \
|
||
|
|
fail2ban \
|
||
|
|
nginx-mainline certbot certbot-nginx apache \
|
||
|
|
iptables-nft openbsd-netcat \
|
||
|
|
docker docker-compose \
|
||
|
|
wireguard-tools systemd-resolvconf \
|
||
|
|
ufw unbound \
|
||
|
|
git pv"
|
||
|
|
|
||
|
|
# Temporarily disable mkinitcpio
|
||
|
|
ln -s -f /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook
|
||
|
|
|
||
|
|
# Install archzfs
|
||
|
|
say as heading "Installing Arch Linux base packages"
|
||
|
|
repeat pacstrap -K /mnt --ask 4 \
|
||
|
|
linux zfs-linux zfs-utils \
|
||
|
|
base mkinitcpio dbus-broker-units \
|
||
|
|
${archpkgs}
|
||
|
|
|
||
|
|
# Configure zfs for pacman
|
||
|
|
sed -e '/ParallelDownloads/c ParallelDownloads = 10' \
|
||
|
|
-e '/Color/c Color' \
|
||
|
|
-e '/\[core\]/i [myvezfs]\
|
||
|
|
Server = https://repo.myvelabs.com/$repo\n' \
|
||
|
|
-i /mnt/etc/pacman.conf
|
||
|
|
|
||
|
|
# Add archzfs keys
|
||
|
|
arch-chroot /mnt pacman-key -r ${zfsgpgkey} >/dev/null 2>&1
|
||
|
|
arch-chroot /mnt pacman-key --lsign-key ${zfsgpgkey} >/dev/null 2>&1
|
||
|
|
|
||
|
|
# Manually configure mkinitcpio
|
||
|
|
sed -e "s|%PKGBASE%|linux|g" \
|
||
|
|
-e "s/^fallback/#&/g" \
|
||
|
|
-e "s/ 'fallback'//" \
|
||
|
|
/mnt/usr/share/mkinitcpio/hook.preset >/mnt/etc/mkinitcpio.d/linux.preset
|
||
|
|
rsync -a /mnt/usr/lib/modules/*/vmlinuz /mnt/boot/vmlinuz-linux
|
||
|
|
|
||
|
|
# Create custom system dirs
|
||
|
|
mkdir -p /mnt/etc/{zfs,pacman.d/hooks,nginx/sites-{available,enabled}} \
|
||
|
|
/mnt/zfs/{bin,snapshots} \
|
||
|
|
/mnt/etc/systemd/{resolved,journald,networkd}.conf.d \
|
||
|
|
/mnt/local/{bin,systemd,hooks}
|
||
|
|
|
||
|
|
# Configure zfs for mkinitcpio
|
||
|
|
echo 'BINARIES+=(/usr/bin/zfs)' >/mnt/etc/mkinitcpio.conf.d/zz-binaries.conf
|
||
|
|
echo 'MODULES_DECOMPRESS="yes"' >/mnt/etc/mkinitcpio.conf.d/zz-modules.conf
|
||
|
|
grep '^HOOKS' /mnt/etc/mkinitcpio.conf | sed 's/filesystems fsck/netconf tinyssh zfsencryptssh zfs filesystems/' >/mnt/etc/mkinitcpio.conf.d/zz-hooks.conf
|
||
|
|
|
||
|
|
# 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/zpool.cache
|
||
|
|
|
||
|
|
# Copy 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/zz-fallback.network
|
||
|
|
|
||
|
|
cat >/mnt/etc/systemd/network/20-${iface}.network <<- network
|
||
|
|
[Match]
|
||
|
|
Name=${iface}
|
||
|
|
|
||
|
|
[Network]
|
||
|
|
Description=Static network
|
||
|
|
DHCP=no
|
||
|
|
|
||
|
|
# IPv4
|
||
|
|
Address=${primaryip}/24
|
||
|
|
Gateway=${gateway}
|
||
|
|
|
||
|
|
# IPv6
|
||
|
|
Address=${primaryip6}/64
|
||
|
|
|
||
|
|
[Route]
|
||
|
|
Gateway=${gateway6}
|
||
|
|
GatewayOnLink=yes
|
||
|
|
network
|
||
|
|
|
||
|
|
# SSH and Dropbear
|
||
|
|
cat sshkeys >/mnt/etc/tinyssh/root_key
|
||
|
|
|
||
|
|
# Chroot into new root
|
||
|
|
arch-chroot /mnt /usr/bin/bash <<"CHROOT"
|
||
|
|
|
||
|
|
# Global bashrc
|
||
|
|
tee -a /etc/skel/.bashrc >/dev/null <<'bashglobal'
|
||
|
|
|
||
|
|
# Add local functions folder to path
|
||
|
|
export PATH=${PATH}:${HOME}/.local/bin:/local/bin:/zfs/bin
|
||
|
|
export SUDO_PROMPT=$'\a'"$(tput rev)[sudo] password for %p:$(tput sgr0)"' '
|
||
|
|
|
||
|
|
# Colored prompts
|
||
|
|
alias diff='diff --color=auto'
|
||
|
|
alias ip='ip -color=auto'
|
||
|
|
export LESS='-R --use-color -Dd+r$Du+b$'
|
||
|
|
|
||
|
|
# Source bash functions
|
||
|
|
for file in $(find ~/.local/functions -type f)
|
||
|
|
do
|
||
|
|
. ${file}
|
||
|
|
done
|
||
|
|
|
||
|
|
# Auto cd into directory
|
||
|
|
shopt -s autocd
|
||
|
|
|
||
|
|
# Enable tab complete for sudo
|
||
|
|
complete -c -f sudo
|
||
|
|
|
||
|
|
# Ignore duplicate and whitespace history entries
|
||
|
|
export HISTCONTROL=ignoreboth
|
||
|
|
|
||
|
|
#
|
||
|
|
# ~/.bash_aliases
|
||
|
|
#
|
||
|
|
|
||
|
|
# ZFS/btrfs
|
||
|
|
alias zfs='sudo zfs'
|
||
|
|
alias zpool='sudo zpool'
|
||
|
|
|
||
|
|
# Shutdown reboot
|
||
|
|
alias poweroff='sudo poweroff'
|
||
|
|
alias reboot='sudo reboot'
|
||
|
|
|
||
|
|
# Clear bash history
|
||
|
|
alias clearhistory='rm ${HISTFILE}; history -c -w'
|
||
|
|
|
||
|
|
# Miscellanous pacman
|
||
|
|
alias orphans='sudo pacman -Rcns $(pacman -Qtdq)'
|
||
|
|
alias unlock-pacman='sudo rm /var/lib/pacman/db.lck && sudo pacman -Syyu'
|
||
|
|
|
||
|
|
# Rsync
|
||
|
|
alias rsync='rsync -v -h --progress --info=progress2 --partial --append-verify'
|
||
|
|
# --log-file=
|
||
|
|
# --remove-source-files
|
||
|
|
|
||
|
|
#
|
||
|
|
# ~/.bash_functions
|
||
|
|
#
|
||
|
|
|
||
|
|
# Pacman tools
|
||
|
|
function installer
|
||
|
|
{
|
||
|
|
sudo pacman -S ${@}
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
|
||
|
|
function uninstall
|
||
|
|
{
|
||
|
|
sudo pacman -Rcns ${@}
|
||
|
|
echo
|
||
|
|
}
|
||
|
|
|
||
|
|
function syur
|
||
|
|
{
|
||
|
|
/local/bin/syu &&
|
||
|
|
reboot
|
||
|
|
}
|
||
|
|
|
||
|
|
function syup
|
||
|
|
{
|
||
|
|
/local/bin/syu &&
|
||
|
|
poweroff
|
||
|
|
}
|
||
|
|
|
||
|
|
# Update bash
|
||
|
|
function update-bash
|
||
|
|
{
|
||
|
|
vim ~/.bashrc &&
|
||
|
|
source ~/.bashrc
|
||
|
|
}
|
||
|
|
bashglobal
|
||
|
|
|
||
|
|
# Root bashrc
|
||
|
|
rsync -a /etc/skel/.bashrc ~/
|
||
|
|
mkdir -p ~/.local/functions
|
||
|
|
cat > ~/.local/functions/root <<- 'rootbashrc'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
# Root shell color
|
||
|
|
PS1="$(tput setaf 1)[\u@\h \W \$?]\$$(tput sgr0) "
|
||
|
|
|
||
|
|
# Colored prompts
|
||
|
|
alias ll='ls --color=auto -l -a -h'
|
||
|
|
alias egrep='egrep --color=auto'
|
||
|
|
alias fgrep='fgrep --color=auto'
|
||
|
|
|
||
|
|
# Disable history
|
||
|
|
unset HISTFILE
|
||
|
|
rm -f ${HISTFILE}
|
||
|
|
history -c -w
|
||
|
|
rootbashrc
|
||
|
|
|
||
|
|
# Configure ssh
|
||
|
|
gensshid
|
||
|
|
ssh-keygen -A >/dev/null
|
||
|
|
cat >/etc/ssh/sshd_config.d/zz-homelab.conf <<- sshd
|
||
|
|
Port ${port:-22}
|
||
|
|
PermitRootLogin no
|
||
|
|
PasswordAuthentication no
|
||
|
|
AuthenticationMethods publickey
|
||
|
|
sshd
|
||
|
|
|
||
|
|
# tinyssh
|
||
|
|
rm -r -f /etc/tinyssh/sshkeydir/
|
||
|
|
tinyssh-convert /etc/tinyssh/sshkeydir/ </etc/ssh/ssh_host_ed25519_key
|
||
|
|
|
||
|
|
# ZFS setup
|
||
|
|
zpool set cachefile=/etc/zfs/zpool.cache zroot
|
||
|
|
zgenhostid $(hostid)
|
||
|
|
|
||
|
|
# Trim zroot monthly
|
||
|
|
cat >/etc/systemd/system/zfs-trim@.timer <<'TRIM'
|
||
|
|
[Unit]
|
||
|
|
Description=Monthly zpool trim on %i
|
||
|
|
|
||
|
|
[Timer]
|
||
|
|
OnCalendar=monthly
|
||
|
|
AccuracySec=1h
|
||
|
|
Persistent=true
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
TRIM
|
||
|
|
|
||
|
|
cat >/etc/systemd/system/zfs-trim@.service <<'TRIM'
|
||
|
|
[Unit]
|
||
|
|
Description=zpool trim on %i
|
||
|
|
Documentation=man:zpool-trim(8)
|
||
|
|
Requires=zfs.target
|
||
|
|
After=zfs.target
|
||
|
|
ConditionACPower=true
|
||
|
|
ConditionPathIsDirectory=/sys/module/zfs
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
Nice=19
|
||
|
|
IOSchedulingClass=idle
|
||
|
|
KillSignal=SIGINT
|
||
|
|
ExecStart=/bin/sh -c '\
|
||
|
|
if /usr/bin/zpool status %i | grep "trimming"; then\
|
||
|
|
exec /usr/bin/zpool wait -t trim %i;\
|
||
|
|
else exec /usr/bin/zpool trim -w %i; fi'
|
||
|
|
ExecStop=-/bin/sh -c '/usr/bin/zpool trim -s %i 2>/dev/null || true'
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
TRIM
|
||
|
|
|
||
|
|
# Scrub zroot monthly
|
||
|
|
cat >/etc/systemd/system/zfs-scrub@.timer <<'SCRUB'
|
||
|
|
[Unit]
|
||
|
|
Description=Monthly zpool scrub on %i
|
||
|
|
|
||
|
|
[Timer]
|
||
|
|
OnCalendar=monthly
|
||
|
|
AccuracySec=1h
|
||
|
|
Persistent=true
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
SCRUB
|
||
|
|
|
||
|
|
cat >/etc/systemd/system/zfs-scrub@.service <<'SCRUB'
|
||
|
|
[Unit]
|
||
|
|
Description=zpool scrub on %i
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
Nice=19
|
||
|
|
IOSchedulingClass=idle
|
||
|
|
KillSignal=SIGINT
|
||
|
|
ExecStart=/usr/bin/zpool scrub %i
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
SCRUB
|
||
|
|
echo
|
||
|
|
|
||
|
|
# mkinitcpio
|
||
|
|
say as heading "Regenerating cpio image"
|
||
|
|
mkinitcpio -P
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Syslinux
|
||
|
|
say as heading "Configuring syslinux"
|
||
|
|
mkdir /boot/syslinux/
|
||
|
|
extlinux --install /boot/syslinux/
|
||
|
|
syslinux-install_update -i -a -m
|
||
|
|
# sed -i 's\linux ../vmlinuz-linux.*\linux ../vmlinuz-linux\g' /boot/syslinux/syslinux.cfg
|
||
|
|
# sed -i "s/APPEND root.*/APPEND zfs=bootfs ip=${primaryip}::${gateway}:${subnet}::${originaliface}:none rw quiet bgrt_disable/g" /boot/syslinux/syslinux.cfg
|
||
|
|
# sed -i 's/.*PROMPT.*/PROMPT 0/g' /boot/syslinux/syslinux.cfg
|
||
|
|
# sed -i 's/.*TIMEOUT.*/TIMEOUT 0/g' /boot/syslinux/syslinux.cfg
|
||
|
|
# sed -i 's/.*UI menu.*/#UI menu/g'
|
||
|
|
cat >/boot/syslinux/syslinux.cfg <<-syslinux
|
||
|
|
DEFAULT arch
|
||
|
|
PROMPT 0
|
||
|
|
TIMEOUT 0
|
||
|
|
|
||
|
|
MENU TITLE Arch Linux
|
||
|
|
MENU COLOR border 30;44 #40ffffff #a0000000 std
|
||
|
|
MENU COLOR title 1;36;44 #9033ccff #a0000000 std
|
||
|
|
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
|
||
|
|
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
|
||
|
|
MENU COLOR help 37;40 #c0ffffff #a0000000 std
|
||
|
|
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
|
||
|
|
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
|
||
|
|
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
|
||
|
|
MENU COLOR tabmsg 31;40 #30ffffff #00000000 std
|
||
|
|
|
||
|
|
LABEL arch
|
||
|
|
MENU LABEL Arch Linux
|
||
|
|
LINUX ../vmlinuz-linux
|
||
|
|
APPEND zfs=bootfs ip=${primaryip}::${gateway}:${subnet}::${originaliface}:none rw quiet bgrt_disable
|
||
|
|
INITRD ../initramfs-linux.img
|
||
|
|
LABEL hdt
|
||
|
|
MENU LABEL HDT (Hardware Detection Tool)
|
||
|
|
COM32 hdt.c32
|
||
|
|
LABEL reboot
|
||
|
|
MENU LABEL Reboot
|
||
|
|
COM32 reboot.c32
|
||
|
|
LABEL poweroff
|
||
|
|
MENU LABEL Poweroff
|
||
|
|
COM32 poweroff.c32
|
||
|
|
syslinux
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Locale
|
||
|
|
sed -i '/#en_US.UTF-8 UTF-8/ s/#//' /etc/locale.gen
|
||
|
|
locale-gen >/dev/null
|
||
|
|
echo 'LANG=en_US.UTF-8' >>/etc/locale.conf
|
||
|
|
say as heading "Locale configured"
|
||
|
|
|
||
|
|
# Time zone
|
||
|
|
ln -s -f /usr/share/zoneinfo/UTC /etc/localtime
|
||
|
|
hwclock --systohc --utc
|
||
|
|
say as heading "Time zone configured"
|
||
|
|
|
||
|
|
# Hostname
|
||
|
|
echo ${hostname} >/etc/hostname
|
||
|
|
cat >>/etc/hosts <<host
|
||
|
|
127.0.0.1 localhost
|
||
|
|
127.0.1.1 ${hostname}
|
||
|
|
host
|
||
|
|
say as heading "Hostname configured"
|
||
|
|
|
||
|
|
# User
|
||
|
|
useradd -m -g users -G wheel,docker -s /usr/bin/bash ${newuser} || die "User account creation has failed"
|
||
|
|
printf '%s\n' "${userpass}" "${userpass}" | passwd ${newuser} >/dev/null 2>&1
|
||
|
|
unset userpass userpass2
|
||
|
|
|
||
|
|
# Disable root account
|
||
|
|
passwd -l root >/dev/null 2>&1
|
||
|
|
|
||
|
|
# Sudoers
|
||
|
|
install -m 0440 /dev/stdin /etc/sudoers.d/01-DEFAULTS <<'DEFAULTS'
|
||
|
|
Defaults passwd_timeout=0
|
||
|
|
Defaults timestamp_type=global
|
||
|
|
Defaults insults
|
||
|
|
DEFAULTS
|
||
|
|
|
||
|
|
install -m 0440 /dev/stdin /etc/sudoers.d/02-COMMANDS <<'COMMANDS'
|
||
|
|
Cmnd_Alias POWER = /usr/bin/poweroff, /usr/bin/reboot
|
||
|
|
Cmnd_Alias ZFS = /usr/bin/zfs, /usr/bin/zpool
|
||
|
|
Cmnd_Alias FAIL2BAN = /usr/bin/fail2ban-client
|
||
|
|
Cmnd_Alias PACMAN = /usr/bin/pacman
|
||
|
|
Cmnd_Alias IPTABLES = /usr/bin/ufw
|
||
|
|
Cmnd_Alias MISC = /usr/bin/rsync
|
||
|
|
COMMANDS
|
||
|
|
|
||
|
|
install -m 0440 /dev/stdin /etc/sudoers.d/03-WHEEL <<'WHEEL'
|
||
|
|
%wheel ALL=(ALL:ALL) ALL
|
||
|
|
%wheel ALL=(ALL:ALL) NOPASSWD: POWER, ZFS, FAIL2BAN, PACMAN, IPTABLES, MISC
|
||
|
|
WHEEL
|
||
|
|
|
||
|
|
install -m 0440 /dev/stdin /etc/sudoers.d/.zz-NOPASSWD <<'NOPASSWD'
|
||
|
|
Defaults:${newuser} !authenticate
|
||
|
|
NOPASSWD
|
||
|
|
|
||
|
|
say as heading "Configured superuser and user"
|
||
|
|
|
||
|
|
# ZFS files
|
||
|
|
touch /zfs/snapshots/syu
|
||
|
|
chown ${newuser}:users /zfs/snapshots/syu
|
||
|
|
|
||
|
|
# Sysctl custom settings
|
||
|
|
cat >/etc/sysctl.d/zz-sysctl.conf <<- SYSCTL
|
||
|
|
net.core.netdev_max_backlog = 16384
|
||
|
|
net.core.somaxconn = 8192
|
||
|
|
net.core.rmem_default = 1048576
|
||
|
|
net.core.rmem_max = 16777216
|
||
|
|
net.core.wmem_default = 1048576
|
||
|
|
net.core.wmem_max = 16777216
|
||
|
|
net.core.optmem_max = 65536
|
||
|
|
net.ipv4.tcp_rmem = 4096 1048576 2097152
|
||
|
|
net.ipv4.tcp_wmem = 4096 65536 16777216
|
||
|
|
net.ipv4.udp_rmem_min = 8192
|
||
|
|
net.ipv4.udp_wmem_min = 8192
|
||
|
|
net.ipv4.tcp_fastopen = 3
|
||
|
|
net.ipv4.tcp_timestamps = 0
|
||
|
|
net.core.default_qdisc = cake
|
||
|
|
net.ipv4.tcp_congestion_control = bbr
|
||
|
|
|
||
|
|
# Swap
|
||
|
|
vm.swappiness=10
|
||
|
|
vm.vfs_cache_pressure=50
|
||
|
|
|
||
|
|
# Wireguard
|
||
|
|
net.ipv4.conf.all.forwarding = 1
|
||
|
|
net.ipv6.conf.all.forwarding = 1
|
||
|
|
SYSCTL
|
||
|
|
|
||
|
|
# zram
|
||
|
|
echo 'zram' >/etc/modules-load.d/zram.conf
|
||
|
|
echo 'options zram num_devices=1' >/etc/modprobe.d/zram.conf
|
||
|
|
echo 'KERNEL=="zram0", ATTR{comp_algorithm}="lz4", ATTR{disksize}="512M" RUN="/usr/bin/mkswap /dev/zram0", TAG+="systemd"' >/etc/udev/rules.d/99-zram.rules
|
||
|
|
|
||
|
|
# Docker
|
||
|
|
install /dev/stdin /usr/local/bin/update-dockerfiles <<'dockerfiles'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
for compose in $(find /docker -maxdepth 2 -type f -name docker-compose.yaml)
|
||
|
|
do
|
||
|
|
docker compose -f ${compose} pull
|
||
|
|
docker compose -f ${compose} up --detach
|
||
|
|
done
|
||
|
|
|
||
|
|
docker network prune -f
|
||
|
|
docker image prune -af
|
||
|
|
docker volume prune -af
|
||
|
|
dockerfiles
|
||
|
|
|
||
|
|
# Unbound
|
||
|
|
sed -i '/include-toplevel/c include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"' /etc/unbound/unbound.conf
|
||
|
|
mkdir /etc/unbound/unbound.conf.d/
|
||
|
|
tee /etc/unbound/unbound.conf.d/unbound.conf >/dev/null <<- unbound.conf
|
||
|
|
server:
|
||
|
|
# If no logfile is specified, syslog is used
|
||
|
|
# logfile: "/var/log/unbound/unbound.log"
|
||
|
|
verbosity: 0
|
||
|
|
|
||
|
|
interface: 0.0.0.0@53
|
||
|
|
do-ip4: yes
|
||
|
|
do-udp: yes
|
||
|
|
do-tcp: yes
|
||
|
|
|
||
|
|
# May be set to yes if you have IPv6 connectivity
|
||
|
|
do-ip6: yes
|
||
|
|
|
||
|
|
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
|
||
|
|
# Terredo tunnels your web browser should favor IPv4 for the same reasons
|
||
|
|
prefer-ip6: no
|
||
|
|
|
||
|
|
# Use this only when you downloaded the list of primary root servers!
|
||
|
|
# If you use the default dns-root-data package, unbound will find it automatically
|
||
|
|
root-hints: "/etc/unbound/root.hints"
|
||
|
|
|
||
|
|
# Trust glue only if it is within the server's authority
|
||
|
|
harden-glue: yes
|
||
|
|
|
||
|
|
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
|
||
|
|
harden-dnssec-stripped: yes
|
||
|
|
|
||
|
|
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
|
||
|
|
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
|
||
|
|
use-caps-for-id: no
|
||
|
|
|
||
|
|
# Reduce EDNS reassembly buffer size.
|
||
|
|
# IP fragmentation is unreliable on the Internet today, and can cause
|
||
|
|
# transmission failures when large DNS messages are sent via UDP. Even
|
||
|
|
# when fragmentation does work, it may not be secure; it is theoretically
|
||
|
|
# possible to spoof parts of a fragmented DNS message, without easy
|
||
|
|
# detection at the receiving end. Recently, there was an excellent study
|
||
|
|
# >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
|
||
|
|
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
|
||
|
|
# in collaboration with NLnet Labs explored DNS using real world data from the
|
||
|
|
# the RIPE Atlas probes and the researchers suggested different values for
|
||
|
|
# IPv4 and IPv6 and in different scenarios. They advise that servers should
|
||
|
|
# be configured to limit DNS messages sent over UDP to a size that will not
|
||
|
|
# trigger fragmentation on typical network links. DNS servers can switch
|
||
|
|
# from UDP to TCP when a DNS response is too big to fit in this limited
|
||
|
|
# buffer size. This value has also been suggested in DNS Flag Day 2020.
|
||
|
|
edns-buffer-size: 1232
|
||
|
|
|
||
|
|
# Perform prefetching of close to expired message cache entries
|
||
|
|
# This only applies to domains that have been frequently queried
|
||
|
|
prefetch: yes
|
||
|
|
|
||
|
|
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
|
||
|
|
num-threads: 2
|
||
|
|
|
||
|
|
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
|
||
|
|
so-rcvbuf: 1m
|
||
|
|
|
||
|
|
# Ensure privacy of local IP ranges
|
||
|
|
private-address: 10.6.22.0/24
|
||
|
|
private-address: fd6e:4f68:5f03:fffe::/64
|
||
|
|
|
||
|
|
# Only give access to recursion clients from LAN IPs
|
||
|
|
access-control: 10.6.22.0/24 allow
|
||
|
|
access-control: fd6e:4f68:5f03:fffe::/64 allow
|
||
|
|
|
||
|
|
# Hide server info from clients
|
||
|
|
hide-identity: yes
|
||
|
|
hide-version: yes
|
||
|
|
|
||
|
|
# Send minimum amount of information to upstream servers to enhance
|
||
|
|
# privacy (best privacy).
|
||
|
|
qname-minimisation: yes
|
||
|
|
|
||
|
|
# Enable ratelimiting of queries (per second) sent to nameserver for
|
||
|
|
# performing recursion. More queries are turned away with an error
|
||
|
|
# (servfail). This stops recursive floods (e.g., random query names), but
|
||
|
|
# not spoofed reflection floods. Cached responses are not rate limited by
|
||
|
|
# this setting. Experimental option.
|
||
|
|
ratelimit: 1000
|
||
|
|
|
||
|
|
# Use this certificate bundle for authenticating connections made to
|
||
|
|
# outside peers (e.g., auth-zone urls, DNS over TLS connections).
|
||
|
|
tls-cert-bundle: "/etc/ssl/certs/ca-certificates.crt"
|
||
|
|
tls-system-cert: yes
|
||
|
|
|
||
|
|
forward-zone:
|
||
|
|
# Forward all queries (except those in cache and local zone) to
|
||
|
|
# upstream recursive servers
|
||
|
|
name: "."
|
||
|
|
# Queries to this forward zone use TLS
|
||
|
|
forward-tls-upstream: yes
|
||
|
|
|
||
|
|
# https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers
|
||
|
|
|
||
|
|
## Cloudflare
|
||
|
|
forward-addr: 1.1.1.1@853#cloudflare-dns.com
|
||
|
|
forward-addr: 1.0.0.1@853#cloudflare-dns.com
|
||
|
|
|
||
|
|
## Quad9
|
||
|
|
# forward-addr: 9.9.9.9@853#dns.quad9.net
|
||
|
|
# forward-addr: 149.112.112.112@853#dns.quad9.net
|
||
|
|
|
||
|
|
remote-control:
|
||
|
|
# Enable remote control with unbound-control(8) here.
|
||
|
|
control-enable: no
|
||
|
|
|
||
|
|
# what interfaces are listened to for remote control.
|
||
|
|
# give 0.0.0.0 and ::0 to listen to all interfaces.
|
||
|
|
# set to an absolute path to use a unix local name pipe, certificates
|
||
|
|
# are not used for that, so key and cert files need not be present.
|
||
|
|
control-interface: 127.0.0.1
|
||
|
|
# control-interface: ::1
|
||
|
|
|
||
|
|
# port number for remote control operations.
|
||
|
|
control-port: 8953
|
||
|
|
unbound.conf
|
||
|
|
curl --silent --fail --output /etc/unbound/root.hints https://www.internic.net/domain/named.cache
|
||
|
|
tee /etc/systemd/resolved.conf.d/zz-unbound.conf >/dev/null <<- 'unbound.conf'
|
||
|
|
[Resolve]
|
||
|
|
DNS=127.0.0.1
|
||
|
|
DNS=::1
|
||
|
|
DNSStubListener=no
|
||
|
|
unbound.conf
|
||
|
|
|
||
|
|
# Global ssh config
|
||
|
|
cat >/etc/ssh/ssh_config.d/zz-homelab.conf <<- sshconfig
|
||
|
|
# Preferred ciphers
|
||
|
|
Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com
|
||
|
|
|
||
|
|
# Only use ipv4
|
||
|
|
AddressFamily inet
|
||
|
|
|
||
|
|
# Multiplex
|
||
|
|
ControlMaster auto
|
||
|
|
ControlPath ~/.ssh/sockets/%r@%h-%p
|
||
|
|
ControlPersist 10m
|
||
|
|
sshconfig
|
||
|
|
|
||
|
|
# Paccache hook
|
||
|
|
cat >/etc/pacman.d/hooks/zz-paccache.hook <<- PACCACHE
|
||
|
|
[Trigger]
|
||
|
|
Operation = Upgrade
|
||
|
|
Operation = Install
|
||
|
|
Operation = Remove
|
||
|
|
Type = Package
|
||
|
|
Target = *
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Description = Cleaning pacman cache...
|
||
|
|
When = PostTransaction
|
||
|
|
Exec = /usr/bin/paccache --remove
|
||
|
|
PACCACHE
|
||
|
|
|
||
|
|
# Locale.gen.pacnew hook
|
||
|
|
install /dev/stdin /local/hooks/localegen <<hook
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
if [ -f /etc/locale.gen.pacnew ]
|
||
|
|
then
|
||
|
|
sed -i '/#en_US.UTF-8 UTF-8/ s/#//' /etc/locale.gen.pacnew
|
||
|
|
mv /etc/locale.gen.pacnew /etc/locale.gen
|
||
|
|
locale-gen >/dev/null
|
||
|
|
fi
|
||
|
|
hook
|
||
|
|
# locale.gen.pacnew hook
|
||
|
|
cat >/etc/pacman.d/hooks/100-localegen.hook <<localegen
|
||
|
|
[Trigger]
|
||
|
|
Operation = Install
|
||
|
|
Operation = Upgrade
|
||
|
|
Type = Package
|
||
|
|
Target = glibc
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Description = Fixing locale.gen
|
||
|
|
When = PostTransaction
|
||
|
|
Exec = /local/hooks/localegen
|
||
|
|
localegen
|
||
|
|
|
||
|
|
# Environment
|
||
|
|
tee -a /etc/environment >/dev/null <<environment
|
||
|
|
EDITOR=vim
|
||
|
|
SUDO_EDITOR=vim
|
||
|
|
environment
|
||
|
|
|
||
|
|
# Permissions
|
||
|
|
chown -R ${newuser}:users /docker/
|
||
|
|
|
||
|
|
# Switch into new user
|
||
|
|
su ${newuser} <<"CHANGEUSER"
|
||
|
|
mkdir -p ~/.local/{bin,functions}
|
||
|
|
|
||
|
|
# Generate SSH identity
|
||
|
|
gensshid
|
||
|
|
cat /etc/tinyssh/root_key >~/.ssh/authorized_keys
|
||
|
|
|
||
|
|
cat >> ~/.bashrc <<- 'BASHRC'
|
||
|
|
|
||
|
|
# Custom shell color
|
||
|
|
PS1="$(tput setaf 8)[\u@\h \W \$?]\$$(tput sgr0) "
|
||
|
|
|
||
|
|
# Colored prompts
|
||
|
|
alias ll='ls --color=auto -l -a -h'
|
||
|
|
alias egrep='egrep --color=auto'
|
||
|
|
alias fgrep='fgrep --color=auto'
|
||
|
|
|
||
|
|
# Disable history
|
||
|
|
unset HISTFILE
|
||
|
|
rm -f ${HISTFILE}
|
||
|
|
history -c -w
|
||
|
|
BASHRC
|
||
|
|
|
||
|
|
# Startup script
|
||
|
|
cat > ~/.local/functions/init <<- 'init'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
if [ -f ~/.local/bin/startup ]
|
||
|
|
then
|
||
|
|
echo -e "\n\e[1;31mInit script hasn't been run yet, executing script before proceeding...\e[0m\n"
|
||
|
|
~/.local/bin/startup || exit 1
|
||
|
|
fi
|
||
|
|
init
|
||
|
|
|
||
|
|
install /dev/stdin ~/.local/bin/startup <<'EOF'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
|
||
|
|
## Tput codes
|
||
|
|
reset=$(tput sgr0)
|
||
|
|
red=${reset}$(tput setaf 1)
|
||
|
|
yellow=${reset}$(tput setaf 3)
|
||
|
|
|
||
|
|
# Exit function
|
||
|
|
trap '[ "${?}" -ne 77 ] || exit 77' ERR
|
||
|
|
function die
|
||
|
|
{
|
||
|
|
cat <<- abort
|
||
|
|
${red}
|
||
|
|
Error encountered for the following reason:
|
||
|
|
${yellow}
|
||
|
|
"${1}"
|
||
|
|
${red}
|
||
|
|
Script aborted...
|
||
|
|
${reset}
|
||
|
|
abort
|
||
|
|
exit 77
|
||
|
|
}
|
||
|
|
|
||
|
|
# Internet connection check
|
||
|
|
if ping -q -c 1 -W 3 archlinux.org >/dev/null
|
||
|
|
then
|
||
|
|
sudo timedatectl set-ntp true
|
||
|
|
else
|
||
|
|
die 'No internet connectivity detected, plug in an ethernet cable and try again'
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Journal
|
||
|
|
sudo systemctl -q restart systemd-journald.service
|
||
|
|
|
||
|
|
rm -f ${0} ~/.local/functions/init
|
||
|
|
echo -e 'Supplementary installer completed, reboot one last time\e[0m\n'
|
||
|
|
EOF
|
||
|
|
CHANGEUSER
|
||
|
|
|
||
|
|
# fail2ban
|
||
|
|
install /dev/stdin /usr/local/bin/fail2ban-jails <<'ALL-JAILS'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
JAILS=$(sudo fail2ban-client status | grep "Jail list" | sed -E 's/^[^:]+:[ \t]+//' | sed 's/,//g')
|
||
|
|
for JAIL in ${JAILS}
|
||
|
|
do
|
||
|
|
sudo fail2ban-client status ${JAIL}
|
||
|
|
done
|
||
|
|
ALL-JAILS
|
||
|
|
cat >/etc/fail2ban/jail.d/sshd.conf <<'SSHD'
|
||
|
|
[sshd]
|
||
|
|
enabled = true
|
||
|
|
filter = sshd
|
||
|
|
backend = systemd
|
||
|
|
maxretry = 5
|
||
|
|
findtime = 1d
|
||
|
|
bantime = 4w
|
||
|
|
ignoreip = 127.0.0.1/8
|
||
|
|
SSHD
|
||
|
|
|
||
|
|
# Nginx
|
||
|
|
sed '/^http {/a\
|
||
|
|
include http_redirect;\
|
||
|
|
include sites-enabled/\*.conf;\n\
|
||
|
|
types_hash_max_size 4096;\
|
||
|
|
server_names_hash_bucket_size 128;\n' \
|
||
|
|
-i /etc/nginx/nginx.conf
|
||
|
|
cat >/etc/nginx/http_redirect <<- 'redirect'
|
||
|
|
server {
|
||
|
|
listen 80;
|
||
|
|
server_name _;
|
||
|
|
if ($scheme = "http") {
|
||
|
|
return 301 https://$host$request_uri;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
redirect
|
||
|
|
cat >/etc/nginx/proxy_params <<- 'proxy_params'
|
||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
|
|
proxy_set_header X-Forwarded-Host $host;
|
||
|
|
proxy_set_header X-Forwarded-Port $server_port;
|
||
|
|
proxy_set_header X-Forwarded-Scheme $scheme;
|
||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
||
|
|
proxy_set_header Accept-Encoding "";
|
||
|
|
proxy_set_header Host $host;
|
||
|
|
|
||
|
|
proxy_next_upstream error timeout;
|
||
|
|
|
||
|
|
client_body_buffer_size 512k;
|
||
|
|
proxy_read_timeout 86400s;
|
||
|
|
client_max_body_size 0;
|
||
|
|
|
||
|
|
# Websocket
|
||
|
|
proxy_http_version 1.1;
|
||
|
|
proxy_cache_bypass $http_upgrade;
|
||
|
|
proxy_set_header Upgrade $http_upgrade;
|
||
|
|
proxy_set_header Connection "upgrade";
|
||
|
|
proxy_params
|
||
|
|
|
||
|
|
cat >/etc/nginx/http_upgrade <<- 'http_upgrade'
|
||
|
|
# Security
|
||
|
|
server_tokens off;
|
||
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
|
|
add_header X-Content-Type-Options "nosniff" always;
|
||
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
||
|
|
add_header Referrer-Policy "no-referrer" always;
|
||
|
|
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
||
|
|
add_header X-Robots-Tag "noindex, nofollow" always;
|
||
|
|
# CSP breaks some webapps
|
||
|
|
# add_header Content-Security-Policy "default-src 'self';" always;
|
||
|
|
|
||
|
|
# http2
|
||
|
|
http2 on;
|
||
|
|
|
||
|
|
# http3
|
||
|
|
# Open port 443/udp to use http3
|
||
|
|
# Add reuseport to ONLY ONE virtual host: listen 443 quic reuseport;
|
||
|
|
listen 443 quic;
|
||
|
|
add_header Alt-Svc 'h3=":443"; ma=86400';
|
||
|
|
quic_retry on;
|
||
|
|
http3 on;
|
||
|
|
|
||
|
|
# Certbot defaults
|
||
|
|
listen 443 ssl;
|
||
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||
|
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||
|
|
add_header Strict-Transport-Security "max-age=31536000" always;
|
||
|
|
http_upgrade
|
||
|
|
|
||
|
|
# Firewall
|
||
|
|
ufw allow ${port:-22}/tcp comment SSH >/dev/null
|
||
|
|
ufw allow 80/tcp comment HTTP >/dev/null
|
||
|
|
ufw allow 443 comment HTTPS/3 >/dev/null
|
||
|
|
ufw allow 51820/udp comment Wireguard >/dev/null
|
||
|
|
ufw allow from 10.6.22.0/24 proto udp to any port 53 comment Unbound >/dev/null
|
||
|
|
ufw allow from fd6e:4f68:5f03:fffe::1/64 proto udp to any port 53 comment Unbound >/dev/null
|
||
|
|
|
||
|
|
# Enable system services
|
||
|
|
systemctl -q enable \
|
||
|
|
systemd-resolved.service systemd-networkd.service sshd.service \
|
||
|
|
ufw.service unbound.service \
|
||
|
|
docker.service \
|
||
|
|
nginx.service \
|
||
|
|
fail2ban.service \
|
||
|
|
zfs.target zfs-import-cache.service zfs-mount.service zfs-import.target \
|
||
|
|
zfs-trim@zroot.timer zfs-scrub@zroot.timer \
|
||
|
|
certbot-renew.timer
|
||
|
|
|
||
|
|
# Pacman hooks
|
||
|
|
# pacman.conf hook
|
||
|
|
cat >/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 = /local/hooks/pacman.conf
|
||
|
|
pacman
|
||
|
|
install /dev/stdin /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' \
|
||
|
|
-e '/\[core\]/i [myvezfs]\
|
||
|
|
Server = https://repo.myvelabs.com/$repo\n' \
|
||
|
|
-i /etc/pacman.conf.pacnew
|
||
|
|
mv /etc/pacman.conf.pacnew /etc/pacman.conf
|
||
|
|
fi
|
||
|
|
hook
|
||
|
|
|
||
|
|
# mkinitcpio conf hook
|
||
|
|
cat >/etc/pacman.d/hooks/85-mkinitcpio.conf.hook <<preset
|
||
|
|
[Trigger]
|
||
|
|
Operation = Install
|
||
|
|
Operation = Upgrade
|
||
|
|
Type = Package
|
||
|
|
Target = mkinitcpio
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Description = Updating mkinitcpio.conf
|
||
|
|
When = PostTransaction
|
||
|
|
Exec = /local/hooks/mkinitcpio.conf
|
||
|
|
preset
|
||
|
|
install /dev/stdin /local/hooks/mkinitcpio.conf <<'hook'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
# Hooks
|
||
|
|
grep '^HOOKS' /etc/mkinitcpio.conf | sed 's/filesystems fsck/netconf tinyssh zfsencryptssh zfs filesystems/' >/etc/mkinitcpio.conf.d/zz-hooks.conf
|
||
|
|
|
||
|
|
# Linux preset
|
||
|
|
sed -e "s|%PKGBASE%|linux|g" \
|
||
|
|
-e "s/^fallback/#&/g" \
|
||
|
|
-e "s/ 'fallback'//" \
|
||
|
|
/usr/share/mkinitcpio/hook.preset >/etc/mkinitcpio.d/linux.preset
|
||
|
|
hook
|
||
|
|
|
||
|
|
# Persistent journal logging
|
||
|
|
cat >/etc/systemd/journald.conf.d/zz-journald.conf <<- eof
|
||
|
|
[Journal]
|
||
|
|
Storage=persistent
|
||
|
|
SystemMaxUse=100M
|
||
|
|
eof
|
||
|
|
|
||
|
|
# Custom pacman update wrapper
|
||
|
|
install /dev/stdin /local/bin/syu <<'syu'
|
||
|
|
#!/usr/bin/env bash
|
||
|
|
set -e
|
||
|
|
|
||
|
|
# Record current time
|
||
|
|
echo $(date "+%Y-%m-%d-%H:%M:%S") >/zfs/snapshots/syu
|
||
|
|
|
||
|
|
# Check for new packages and continue if found
|
||
|
|
newpkg+=($(checkupdates --nocolor | awk '{print $1}'))
|
||
|
|
if [ "${newpkg}" ]
|
||
|
|
then
|
||
|
|
# Sync pacman dbs
|
||
|
|
sudo pacman --ask 4 --sync --refresh >/dev/null
|
||
|
|
|
||
|
|
# Update archlinux-keyring first
|
||
|
|
if [[ ${newpkg[@]} =~ "archlinux-keyring" ]]
|
||
|
|
then
|
||
|
|
sudo pacman --ask 4 --sync --needed archlinux-keyring
|
||
|
|
echo
|
||
|
|
fi
|
||
|
|
if sudo pacman --ask 4 --sync --sysupgrade --needed
|
||
|
|
then
|
||
|
|
echo
|
||
|
|
sudo pacdiff
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
syu
|
||
|
|
CHROOT
|
||
|
|
|
||
|
|
## Pre and post update backup hooks
|
||
|
|
# Boot
|
||
|
|
cat >/mnt/etc/pacman.d/hooks/55-bootbackup_pre.hook <<pre
|
||
|
|
[Trigger]
|
||
|
|
Operation = Upgrade
|
||
|
|
Operation = Install
|
||
|
|
Operation = Remove
|
||
|
|
Type = Path
|
||
|
|
Target = usr/lib/modules/*/vmlinuz
|
||
|
|
Target = usr/lib/initcpio/*
|
||
|
|
Target = usr/lib/firmware/*
|
||
|
|
Target = usr/src/*/dkms.conf
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Depends = rsync
|
||
|
|
Description = Backing up pre /boot...
|
||
|
|
When = PreTransaction
|
||
|
|
Exec = /usr/bin/bash -c 'mount ${installation_disks}1; rsync -a --mkpath --delete /boot/ "/.boot/\$(cat /zfs/snapshots/syu)_pre"/'
|
||
|
|
AbortOnFail
|
||
|
|
pre
|
||
|
|
cat >/mnt/etc/pacman.d/hooks/95-bootbackup_post.hook <<post
|
||
|
|
[Trigger]
|
||
|
|
Operation = Upgrade
|
||
|
|
Operation = Install
|
||
|
|
Operation = Remove
|
||
|
|
Type = Path
|
||
|
|
Target = usr/lib/modules/*/vmlinuz
|
||
|
|
Target = usr/lib/initcpio/*
|
||
|
|
Target = usr/lib/firmware/*
|
||
|
|
Target = usr/src/*/dkms.conf
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Depends = rsync
|
||
|
|
Description = Backing up post /boot...
|
||
|
|
When = PostTransaction
|
||
|
|
Exec = /usr/bin/bash -c 'rsync -a --mkpath --delete /boot/ "/.boot/\$(cat /zfs/snapshots/syu)_post"/; /local/hooks/post-install-boot'
|
||
|
|
post
|
||
|
|
# zroot
|
||
|
|
cat >/mnt/etc/pacman.d/hooks/01-syu_pre.hook <<pre
|
||
|
|
[Trigger]
|
||
|
|
Type = Path
|
||
|
|
Operation = Upgrade
|
||
|
|
Operation = Install
|
||
|
|
Operation = Remove
|
||
|
|
Target = usr/lib/modules/*/vmlinuz
|
||
|
|
Target = usr/lib/initcpio/*
|
||
|
|
Target = usr/lib/firmware/*
|
||
|
|
Target = usr/src/*/dkms.conf
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Description = Creating pre zroot snapshot...
|
||
|
|
When = PreTransaction
|
||
|
|
Exec = /usr/bin/bash -c 'zfs snapshot zroot/ROOT@pre-\$(cat /zfs/snapshots/syu)'
|
||
|
|
AbortOnFail
|
||
|
|
pre
|
||
|
|
cat >/mnt/etc/pacman.d/hooks/zz-syu_post.hook <<post
|
||
|
|
[Trigger]
|
||
|
|
Type = Path
|
||
|
|
Operation = Upgrade
|
||
|
|
Operation = Install
|
||
|
|
Operation = Remove
|
||
|
|
Target = usr/lib/modules/*/vmlinuz
|
||
|
|
Target = usr/lib/initcpio/*
|
||
|
|
Target = usr/lib/firmware/*
|
||
|
|
Target = usr/src/*/dkms.conf
|
||
|
|
|
||
|
|
[Action]
|
||
|
|
Description = Creating post zroot snapshot...
|
||
|
|
When = PostTransaction
|
||
|
|
Exec = /usr/bin/bash -c 'zfs snapshot zroot/ROOT@post-\$(cat /zfs/snapshots/syu)'
|
||
|
|
post
|
||
|
|
|
||
|
|
# Generate fstab
|
||
|
|
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
|
||
|
|
|
||
|
|
# Add zram to fstab
|
||
|
|
echo '/dev/zram0 none swap defaults 0 0' >>/mnt/etc/fstab
|
||
|
|
echo
|
||
|
|
|
||
|
|
# Reboot only if script succeeded
|
||
|
|
if /usr/bin/bash -c 'arch-chroot /mnt uname -a' | grep -q Linux
|
||
|
|
then
|
||
|
|
until ! mount | grep -q /mnt/boot
|
||
|
|
do
|
||
|
|
umount /mnt/boot
|
||
|
|
done
|
||
|
|
zfs snapshot zroot/ROOT@fresh-installation
|
||
|
|
zpool export -a
|
||
|
|
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 run $(tput smso)startup$(tput rmso) in the terminal"
|
||
|
|
say as success in bold "Rebooting..."
|
||
|
|
echo
|
||
|
|
reboot
|
||
|
|
else
|
||
|
|
die 'Something does not feel right'
|
||
|
|
fi
|