Add bootstrap.sh
This commit is contained in:
parent
c5fba6aae1
commit
2f8514e70d
1 changed files with 328 additions and 0 deletions
328
bootstrap.sh
Normal file
328
bootstrap.sh
Normal file
|
@ -0,0 +1,328 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Arch Linux installation
|
||||
#
|
||||
# Bootable USB:
|
||||
# - [Download](https://archlinux.org/download/) ISO and GPG files
|
||||
# - Verify the ISO file: `$ pacman-key -v archlinux-<version>-x86_64.iso.sig`
|
||||
# - Create a bootable USB with: `# dd if=archlinux*.iso of=/dev/sdX && sync`
|
||||
#
|
||||
# UEFI setup:
|
||||
#
|
||||
# - Set boot mode to UEFI, disable Legacy mode entirely.
|
||||
# - Temporarily disable Secure Boot.
|
||||
# - Make sure a strong UEFI administrator password is set.
|
||||
# - Delete preloaded OEM keys for Secure Boot, allow custom ones.
|
||||
# - Set SATA operation to AHCI mode.
|
||||
#
|
||||
# Run installation:
|
||||
#
|
||||
# - Connect to wifi via: `# iwctl station wlan0 connect WIFI-NETWORK`
|
||||
# - Run: `# bash <(curl -sL https://link.rafe.li/dot)`
|
||||
#
|
||||
# WARNING: this script will destroy data on the selected disk.
|
||||
#
|
||||
|
||||
set -uo pipefail
|
||||
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
|
||||
|
||||
exec 1> >(tee "stdout.log")
|
||||
exec 2> >(tee "stderr.log" >&2)
|
||||
|
||||
export SNAP_PAC_SKIP=y
|
||||
|
||||
# Dialog
|
||||
BACKTITLE="Arch Linux installation"
|
||||
|
||||
get_input() {
|
||||
title="$1"
|
||||
description="$2"
|
||||
|
||||
input=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --inputbox "$description" 0 0)
|
||||
echo "$input"
|
||||
}
|
||||
|
||||
get_password() {
|
||||
title="$1"
|
||||
description="$2"
|
||||
while : ; do
|
||||
init_pass=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --passwordbox "$description" 0 0)
|
||||
: "${init_pass:?dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --msgbox "Password cannot be empty.\nTry again." 0 0}"
|
||||
|
||||
test_pass=$(dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --passwordbox "$description again" 0 0)
|
||||
if [[ "$init_pass" != "$test_pass" ]]; then
|
||||
dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --msgbox "Passwords did not match.\nTry again." 0 0
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo "$init_pass"
|
||||
}
|
||||
|
||||
get_choice() {
|
||||
title="$1"
|
||||
description="$2"
|
||||
shift 2
|
||||
options=("$@")
|
||||
dialog --clear --stdout --backtitle "$BACKTITLE" --title "$title" --menu "$description" 0 0 0 "${options[@]}"
|
||||
}
|
||||
|
||||
|
||||
echo -e "\n### Checking UEFI boot mode"
|
||||
if [ ! -f /sys/firmware/efi/fw_platform_size ]; then
|
||||
echo >&2 "You must boot in UEFI mode to continue"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo -e "\n### Ensure the system clock is accurate"
|
||||
timedatectl set-ntp true
|
||||
hwclock --systohc --utc
|
||||
|
||||
echo -e "\n### Setting keyboard layout to de-latin1"
|
||||
loadkeys de-latin1
|
||||
|
||||
echo -e "\n### Installing additional tools"
|
||||
pacman -Sy --noconfirm --needed git reflector terminus-font dialog wget
|
||||
|
||||
echo -e "\n### HiDPI screens"
|
||||
noyes=("Yes" "The font is too small" "No" "The font size is just fine")
|
||||
hidpi=$(get_choice "Font size" "Is your screen HiDPI?" "${noyes[@]}") || exit 1
|
||||
clear
|
||||
[[ "$hidpi" == "Yes" ]] && font="ter-132n" || font="ter-716n"
|
||||
setfont "$font"
|
||||
|
||||
hostname=$(get_input "Hostname" "Enter hostname") || exit 1
|
||||
clear
|
||||
: "${hostname:?"hostname cannot be empty"}"
|
||||
|
||||
user=$(get_input "User" "Enter username") || exit 1
|
||||
clear
|
||||
: "${user:?"user cannot be empty"}"
|
||||
|
||||
password=$(get_password "User" "Enter password") || exit 1
|
||||
clear
|
||||
: "${password:?"password cannot be empty"}"
|
||||
|
||||
devicelist=$(lsblk -dplnx size -o name,size | grep -Ev "boot|rpmb|loop" | tac | tr '\n' ' ')
|
||||
read -r -a devicelist <<< "$devicelist"
|
||||
|
||||
device=$(get_choice "Installation" "Select installation disk" "${devicelist[@]}") || exit 1
|
||||
|
||||
clear
|
||||
|
||||
echo -e "\n### Setting up fastest mirrors"
|
||||
reflector --country 'Germany,France,' --protocol https --sort rate --save /etc/pacman.d/mirrorlist
|
||||
|
||||
echo -e "\n### Setting up partitions"
|
||||
umount -R /mnt 2> /dev/null || true
|
||||
cryptsetup luksClose luks 2> /dev/null || true
|
||||
|
||||
lsblk -plnx size -o name "${device}" | xargs -n1 wipefs --all
|
||||
sgdisk --clear "${device}" --new 1::-551MiB "${device}" --new 2::0 --typecode 2:ef00 "${device}"
|
||||
sgdisk --change-name=1:primary --change-name=2:ESP "${device}"
|
||||
|
||||
part_root="$(ls "${device}"* | grep -E "^${device}p?1$")"
|
||||
part_boot="$(ls "${device}"* | grep -E "^${device}p?2$")"
|
||||
|
||||
echo -e "\n### Formatting partitions"
|
||||
mkfs.vfat -n "EFI" -F 32 "${part_boot}"
|
||||
echo -n "${password}" | cryptsetup luksFormat --type luks2 --pbkdf argon2id --label luks "${part_root}"
|
||||
echo -n "${password}" | cryptsetup luksOpen --allow-discards --persistent "${part_root}" luks
|
||||
mkfs.btrfs -L btrfs /dev/mapper/luks
|
||||
|
||||
echo -e "\n### Setting up BTRFS subvolumes"
|
||||
mount /dev/mapper/luks /mnt
|
||||
btrfs subvolume create /mnt/root
|
||||
btrfs subvolume create /mnt/home
|
||||
btrfs subvolume create /mnt/pkgs
|
||||
btrfs subvolume create /mnt/aurbuild
|
||||
btrfs subvolume create /mnt/archbuild
|
||||
btrfs subvolume create /mnt/docker
|
||||
btrfs subvolume create /mnt/logs
|
||||
btrfs subvolume create /mnt/temp
|
||||
btrfs subvolume create /mnt/swap
|
||||
btrfs subvolume create /mnt/snapshots
|
||||
umount /mnt
|
||||
|
||||
mount -o noatime,compress=zstd,subvol=root /dev/mapper/luks /mnt
|
||||
mkdir -p /mnt/{mnt/btrfs-root,efi,home,var/{cache/pacman,log,tmp,lib/{aurbuild,archbuild,docker}},swap,.snapshots}
|
||||
mount "${part_boot}" /mnt/efi
|
||||
mount -o noatime,compress=zstd,subvol=/ /dev/mapper/luks /mnt/mnt/btrfs-root
|
||||
mount -o noatime,compress=zstd,subvol=home /dev/mapper/luks /mnt/home
|
||||
mount -o noatime,compress=zstd,subvol=pkgs /dev/mapper/luks /mnt/var/cache/pacman
|
||||
mount -o noatime,compress=zstd,subvol=aurbuild /dev/mapper/luks /mnt/var/lib/aurbuild
|
||||
mount -o noatime,compress=zstd,subvol=archbuild /dev/mapper/luks /mnt/var/lib/archbuild
|
||||
mount -o noatime,compress=zstd,subvol=docker /dev/mapper/luks /mnt/var/lib/docker
|
||||
mount -o noatime,compress=zstd,subvol=logs /dev/mapper/luks /mnt/var/log
|
||||
mount -o noatime,compress=zstd,subvol=temp /dev/mapper/luks /mnt/var/tmp
|
||||
mount -o noatime,compress=zstd,subvol=swap /dev/mapper/luks /mnt/swap
|
||||
mount -o noatime,compress=zstd,subvol=snapshots /dev/mapper/luks /mnt/.snapshots
|
||||
|
||||
echo -e "\n### Configuring custom repo"
|
||||
mkdir "/mnt/var/cache/pacman/${user}-local"
|
||||
|
||||
# if [[ "${user}" == "maximbaz" && "${hostname}" == "home-"* ]]; then
|
||||
# wget -m -nH -np -q --show-progress --progress=bar:force --reject='index.html*' --cut-dirs=2 -P "/mnt/var/cache/pacman/${user}-local" 'https://pkgbuild.com/~maximbaz/repo/'
|
||||
# rename -- 'maximbaz.' "${user}-local." "/mnt/var/cache/pacman/${user}-local"/*
|
||||
# else
|
||||
repo-add "/mnt/var/cache/pacman/${user}-local/${user}-local.db.tar"
|
||||
# fi
|
||||
|
||||
if ! grep "${user}" /etc/pacman.conf > /dev/null; then
|
||||
cat >> /etc/pacman.conf << EOF
|
||||
|
||||
[${user}-local]
|
||||
Server = file:///mnt/var/cache/pacman/${user}-local
|
||||
|
||||
[maximbaz]
|
||||
Server = https://pkgbuild.com/~maximbaz/repo
|
||||
|
||||
[options]
|
||||
CacheDir = /mnt/var/cache/pacman/pkg
|
||||
CacheDir = /mnt/var/cache/pacman/${user}-local
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo -e "\n### Installing packages"
|
||||
kernel_packages=(
|
||||
"linux"
|
||||
"linux-headers"
|
||||
"linux-lts"
|
||||
"linux-firmware"
|
||||
"intel-ucode"
|
||||
)
|
||||
fs_packages=(
|
||||
"btrfs-progs"
|
||||
"dosfstools"
|
||||
"e2fsprogs"
|
||||
)
|
||||
network_packages=(
|
||||
"iwd"
|
||||
"systemd-resolvconf"
|
||||
)
|
||||
basic_packages=(
|
||||
"man-db"
|
||||
"man-pages"
|
||||
"pacman-contrib"
|
||||
"neovim"
|
||||
"bash-completion"
|
||||
"git"
|
||||
"rsync"
|
||||
"openssh"
|
||||
"htop"
|
||||
"fzf"
|
||||
"sudo"
|
||||
)
|
||||
all_packages=(
|
||||
${kernel_packages[@]}
|
||||
${fs_packages[@]}
|
||||
${network_packages[@]}
|
||||
${basic_packages[@]}
|
||||
)
|
||||
|
||||
pacstrap /mnt base base-devel arch-secure-boot ${all_packages[@]}
|
||||
|
||||
echo -e "\n### Generating base config files"
|
||||
echo "cryptdevice=PARTLABEL=primary:luks:allow-discards root=LABEL=btrfs rootflags=subvol=root rw quiet mem_sleep_default=deep" > /mnt/etc/kernel/cmdline
|
||||
|
||||
genfstab -L /mnt >> /mnt/etc/fstab
|
||||
|
||||
echo "FONT=$font" > /mnt/etc/vconsole.conf
|
||||
echo "KEYMAP=de-latin1" >> /mnt/etc/vconsole.conf
|
||||
echo "${hostname}" > /mnt/etc/hostname
|
||||
sed -i 's/^#en_US\.UTF-8/en_US\.UTF-8/' /mnt/etc/locale.gen
|
||||
sed -i 's/^#de_DE\.UTF-8/de_DE\.UTF-8/' /mnt/etc/locale.gen
|
||||
echo "LANG=en_US.UTF-8" > /mnt/etc/locale.conf
|
||||
arch-chroot /mnt locale-gen
|
||||
|
||||
ln -sf /usr/share/zoneinfo/Europe/Berlin /mnt/etc/localtime
|
||||
|
||||
echo "$hostname" > /mnt/etc/hostname
|
||||
echo -e "127.0.0.1\tlocalhost" >>/mnt/etc/hosts
|
||||
echo -e "127.0.1.1\t$hostname" >>/mnt/etc/hosts
|
||||
echo -e "\n::1\tlocalhost" >>/mnt/etc/hosts
|
||||
|
||||
# Propagate the systemd-resolved managed configuration to all clients (stub mode)
|
||||
ln -sf /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf
|
||||
|
||||
cat >/mnt/etc/systemd/network/20-wired.network <<EOF
|
||||
[Match]
|
||||
Name=en*
|
||||
|
||||
[Network]
|
||||
DHCP=yes
|
||||
|
||||
[DHCPv4]
|
||||
RouteMetric=10
|
||||
UseDomains=true
|
||||
|
||||
[IPv6AcceptRA]
|
||||
RouteMetric=10
|
||||
UseDomains=yes
|
||||
EOF
|
||||
|
||||
cat >/mnt/etc/systemd/network/25-wireless.network <<EOF
|
||||
[Match]
|
||||
Name=wl*
|
||||
|
||||
[Network]
|
||||
DHCP=yes
|
||||
|
||||
[DHCPv4]
|
||||
RouteMetric=20
|
||||
UseDomains=true
|
||||
|
||||
[IPv6AcceptRA]
|
||||
RouteMetric=20
|
||||
UseDomains=yes
|
||||
EOF
|
||||
|
||||
mkdir -p /mnt/etc/iwd
|
||||
cat >/mnt/etc/iwd/main.conf <<EOF
|
||||
[General]
|
||||
EnableNetworkConfiguration=true
|
||||
|
||||
[Network]
|
||||
EnableIPv6=true
|
||||
EOF
|
||||
|
||||
arch-chroot /mnt systemctl enable systemd-timesyncd fstrim.timer systemd-networkd systemd-resolved iwd
|
||||
|
||||
cat >/mnt/etc/mkinitcpio.conf <<EOF
|
||||
MODULES=(i915)
|
||||
BINARIES=(/usr/bin/btrfs)
|
||||
FILES=()
|
||||
HOOKS=(base consolefont udev autodetect keyboard keymap modconf block encrypt filesystems fsck shutdown)
|
||||
EOF
|
||||
|
||||
arch-chroot /mnt mkinitcpio -p linux
|
||||
arch-chroot /mnt arch-secure-boot initial-setup
|
||||
|
||||
echo -e "\n### Configuring swap file"
|
||||
swap_size=$(free --mebi | awk '/Mem:/ {print $2}')
|
||||
swap_end=$(( $swap_size + 129 + 1 ))MiB
|
||||
truncate -s 0 /mnt/swap/swapfile
|
||||
chattr +C /mnt/swap/swapfile
|
||||
btrfs property set /mnt/swap/swapfile compression none
|
||||
fallocate -l $swap_end /mnt/swap/swapfile
|
||||
chmod 600 /mnt/swap/swapfile
|
||||
mkswap /mnt/swap/swapfile
|
||||
echo "/swap/swapfile none swap defaults 0 0" >> /mnt/etc/fstab
|
||||
|
||||
# sudo
|
||||
sed -i 's/# \(%wheel ALL=(ALL:ALL) ALL\)/\1/' /mnt/etc/sudoers
|
||||
|
||||
echo -e "\n### Creating user"
|
||||
arch-chroot /mnt useradd -m "$user"
|
||||
for group in wheel network video audio input storage power; do
|
||||
arch-chroot /mnt groupadd -rf "$group"
|
||||
arch-chroot /mnt gpasswd -a "$user" "$group"
|
||||
done
|
||||
echo "$user:$password" | arch-chroot /mnt chpasswd
|
||||
# disable root login
|
||||
arch-chroot /mnt passwd -dl root
|
||||
|
||||
echo -e "\n### Setting permissions on the custom repo"
|
||||
arch-chroot /mnt chown -R "$user:$user" "/var/cache/pacman/${user}-local/"
|
||||
|
||||
echo -e "\n### Reboot now, and after power off remember to unplug the installation USB"
|
Loading…
Reference in a new issue