#!/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--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 --latest 5 --save /etc/pacman.d/mirrorlist echo -e "\n### Setting up partitions" umount -R /mnt 2> /dev/null || true swapoff --all cryptsetup close swap 2> /dev/null || true cryptsetup luksClose luks 2> /dev/null || true sgdisk --zap-all "${device}" sgdisk --clear \ --new=1:0:+550MiB --typecode=1:ef00 --change-name=1:EFI \ --new=2:0:+8GiB --typecode=2:8200 --change-name=2:cryptswap \ --new=3:0:0 --typecode=3:8300 --change-name=3:cryptsystem \ "${device}" sleep 5 echo -e "\n### Formatting partitions" # Boot partition mkfs.fat -F 32 -n "EFI" /dev/disk/by-partlabel/EFI # Swap partition cryptsetup open --type plain --key-file /dev/urandom /dev/disk/by-partlabel/cryptswap swap mkswap -L swap /dev/mapper/swap swapon -L swap # System partition echo -n "${password}" | cryptsetup luksFormat --type luks2 --pbkdf argon2id "/dev/disk/by-partlabel/cryptsystem" echo -n "${password}" | cryptsetup luksOpen --allow-discards --persistent "/dev/disk/by-partlabel/cryptsystem" system mkfs.btrfs --force --label system /dev/mapper/system echo -e "\n### Setting up BTRFS subvolumes" o=defaults,x-mount.mkdir o_btrfs=$o,compress=zstd,ssd,noatime mount -t btrfs LABEL=system /mnt btrfs subvolume create /mnt/root btrfs subvolume create /mnt/home btrfs subvolume create /mnt/snapshots 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/tmp umount -R /mnt mount -t btrfs -o subvol=root,$o_btrfs LABEL=system /mnt mount -t btrfs -o subvol=home,$o_btrfs LABEL=system /mnt/home mount -t btrfs -o subvol=snapshots,$o_btrfs LABEL=system /mnt/.snapshots mount -t btrfs -o subvol=pkgs,$o_btrfs LABEL=system /mnt/var/cache/pacman mount -t btrfs -o subvol=aurbuild,$o_btrfs LABEL=system /mnt/var/lib/aurbuild mount -t btrfs -o subvol=archbuild,$o_btrfs LABEL=system /mnt/var/lib/archbuild mount -t btrfs -o subvol=docker,$o_btrfs LABEL=system /mnt/var/lib/docker mount -t btrfs -o subvol=logs,$o_btrfs LABEL=system /mnt/var/log mount -t btrfs -o subvol=tmp,$o_btrfs LABEL=system /mnt/var/tmp mount -o $o LABEL=EFI /mnt/efi 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/x86_64/ [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" "networkmanager" ) basic_packages=( "man-db" "man-pages" "pacman-contrib" "neovim" "bash-completion" "git" "rsync" "openssh" "htop" "fzf" "sudo" #"pipewire" #"pipewire-pulse" #"pipewire-jack" #"wireplumber" #"firefox" #"firefox-i18n-de" #"firefox-ublock-origin" #"firefox-dark-reader" #"aurutils" # from maximbaz repo #"devtools" # tools for aurutils #"docbook-xls" # depenency of plymouth-git #"efitools" # provides KeyTool #"libfido2" # for systemd-cryptenroll #"bluez" #"bluez-utils" #"usbutils" # for lsusb ) all_packages=( ${kernel_packages[@]} ${fs_packages[@]} ${network_packages[@]} ${basic_packages[@]} ) pacstrap /mnt base base-devel arch-secure-boot chezmoi ${all_packages[@]} echo -e "\n### Generating base config files" genfstab -L -p /mnt >> /mnt/etc/fstab sed -i "s+LABEL=swap+/dev/mapper/swap+" /mnt/etc/fstab echo "cryptswap /dev/disk/by-partlabel/cryptswap /dev/urandom swap,cipher=aes-xts-plain64,size=256" >> /mnt/etc/crypttab systemd-nspawn -bD /mnt sed -i 's/^#en_US\.UTF-8/en_US\.UTF-8/' /etc/locale.gen sed -i 's/^#de_DE\.UTF-8/de_DE\.UTF-8/' /etc/locale.gen locale-gen systemd-firstboot \ --locale="en_US.UTF-8" \ --keymap="de-latin1" \ --timezone="Europe/Berlin" \ --hostname="${hostname}" \ --setup-machine-id echo "FONT=$font" > /etc/vconsole.conf echo -e "127.0.0.1\tlocalhost" >> /etc/hosts echo -e "127.0.1.1\t$hostname" >> /etc/hosts echo -e "\n::1\tlocalhost" >> /etc/hosts timedatectl set-ntp 1 echo -e "\n### Creating user" useradd -m "$user" for group in wheel network video audio input storage power; do groupadd -rf "$group" gpasswd -a "$user" "$group" done echo "$user:$password" | chpasswd echo -e "\n### Disabling root login" passwd -dl root echo -e "\n### Setting permissions on the custom repo" chown -R "$user:$user" "/var/cache/pacman/${user}-local/" #echo -e "\n### Cloning dotfiles and running initial setup" #sudo -u $user sh -c 'chezmoi init --apply https://code.strobeto.de/strobeltobias/dotfiles.git && chezmoi state delete-bucket --bucket=scriptState' cat > /etc/NetworkManager/conf.d/wifi_backend.conf <