From e25dd231cb55cba6ad0e3ba5b962e40135cd8743 Mon Sep 17 00:00:00 2001 From: The_miro Date: Mon, 18 May 2026 15:24:29 +0200 Subject: [PATCH] installer: add no-encryption option and auto LUKS backup key Both arch-autoinstall.sh and archbaseos-guided-install.sh now ask whether to enable disk encryption. If skipped, btrfs is formatted directly on the root partition with an appropriate plain GRUB cmdline (root=UUID=... rootflags=subvol=@). When encryption is chosen, a 64-byte random key is generated, enrolled as a second LUKS keyslot, and written to /_LUKS_BACKUP_KEY inside the new system (mode 400, root-owned, inside the encrypted container). Also fixes: duplicate 'encrypt' hook in original mkinitcpio HOOKS strings, missing KERNEL export into arch-autoinstall chroot heredoc. Co-Authored-By: Claude Sonnet 4.6 --- setup/arch-autoinstall.sh | 304 ++++++++++++++++++++--------- setup/archbaseos-guided-install.sh | 241 +++++++++++++++++------ 2 files changed, 388 insertions(+), 157 deletions(-) diff --git a/setup/arch-autoinstall.sh b/setup/arch-autoinstall.sh index 4fd7d61..29799d7 100755 --- a/setup/arch-autoinstall.sh +++ b/setup/arch-autoinstall.sh @@ -1,11 +1,18 @@ #!/usr/bin/env bash +# arch-autoinstall.sh — automated Arch Linux base installer +# +# If /answerfile.json exists (e.g. embedded in the ISO via build.sh --preconf), +# all prompts are answered from it. Missing fields fall back to interactive prompts. +# +# Answerfile fields: drive, kernel, hostname, username, encrypt, fido2_root, +# fido2_user, run_tui (password always prompted interactively) + set -euo pipefail ############################################ # LOGGING ############################################ LOGFILE="$HOME/arch-autoinstall.log" - { echo echo "############################################" @@ -13,16 +20,53 @@ LOGFILE="$HOME/arch-autoinstall.log" echo "############################################" echo } >> "$LOGFILE" - exec > >(tee -a "$LOGFILE") 2>&1 +############################################ +# ANSWERFILE +############################################ +ANSWERFILE="${ANSWERFILE:-/answerfile.json}" +AF_MODE=false +[[ -f "$ANSWERFILE" ]] && AF_MODE=true + +af_get() { + # af_get [default] + local val + val=$(jq -r "${1} // empty" "$ANSWERFILE" 2>/dev/null || true) + if [[ -z "$val" ]]; then printf '%s' "${2:-}"; else printf '%s' "$val"; fi +} + +af_bool() { + # Returns YES or NO from a JSON boolean field + local val; val=$(jq -r "${1} // false" "$ANSWERFILE" 2>/dev/null || true) + [[ "$val" == "true" ]] && echo "YES" || echo "NO" +} + +get_mac_suffix() { + local mac + mac=$(ip link show 2>/dev/null \ + | awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}') + printf '%s' "${mac//:/}" +} + +if $AF_MODE; then + echo "Answerfile detected: $ANSWERFILE" + # Ensure jq is available + command -v jq &>/dev/null || pacman -Sy --noconfirm jq +fi ############################################ # SAFETY WARNING ############################################ -echo "WARNING: This will ERASE ALL DATA on the selected drive!" -read -rp "Type 'YES' to continue: " confirm -[[ "$confirm" == "YES" ]] || { echo "Aborted."; exit 1; } +if $AF_MODE; then + echo "WARNING: Automated install — all data on $(af_get '.drive' '/dev/?') will be ERASED." + echo "Proceeding in 5 seconds... (Ctrl-C to abort)" + sleep 5 +else + echo "WARNING: This will ERASE ALL DATA on the selected drive!" + read -rp "Type 'YES' to continue: " confirm + [[ "$confirm" == "YES" ]] || { echo "Aborted."; exit 1; } +fi ############################################ # REQUIRED PACKAGES FOR INSTALL ENVIRONMENT @@ -30,10 +74,57 @@ read -rp "Type 'YES' to continue: " confirm pacman -Sy --noconfirm parted cryptsetup libfido2 pam-u2f ############################################ -# DISK SELECTION +# DRIVE SELECTION ############################################ lsblk -read -rp "Enter target drive (e.g., /dev/sda): " DRIVE +if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then + DRIVE=$(af_get '.drive') + echo "Drive (from answerfile): $DRIVE" +else + read -rp "Enter target drive (e.g., /dev/sda): " DRIVE +fi + +############################################ +# USER INPUT +############################################ +if $AF_MODE; then + KERNEL=$(af_get '.kernel' 'linux') + RAW_HOSTNAME=$(af_get '.hostname' '') + if [[ -n "$RAW_HOSTNAME" ]]; then + HOSTNAME="${RAW_HOSTNAME}-$(get_mac_suffix)" + else + HOSTNAME="arch" + fi + USERNAME=$(af_get '.username' '') + ENCRYPT_DISK=$(af_bool '.encrypt') + FIDO_ROOT=$(af_bool '.fido2_root') + FIDO_USER=$(af_bool '.fido2_user') + RUN_TUI=$(af_bool '.run_tui') + echo "Kernel: $KERNEL" + echo "Hostname: $HOSTNAME" + echo "Username: $USERNAME" + echo "Encrypt: $ENCRYPT_DISK / FIDO2 root: $FIDO_ROOT / FIDO2 user: $FIDO_USER" +else + read -rp "Enter kernel package (e.g., linux, linux-lts): " KERNEL + read -rp "Enter hostname: " HOSTNAME + read -rp "Enter username: " USERNAME + read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK + FIDO_ROOT="NO" + if [[ "$ENCRYPT_DISK" == "YES" ]]; then + read -rp "Enable FIDO2 unlocking for root partition? (YES/NO): " FIDO_ROOT + fi + read -rp "Enable FIDO2 authentication for user login? (YES/NO): " FIDO_USER +fi + +# Password always prompted — never stored in answerfile +read -rsp "Enter password for $USERNAME: " USERPASS; echo +[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; } + +if ! $AF_MODE; then + read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI_IN + _RUN_TUI_IN="${_RUN_TUI_IN:-YES}" + [[ "${_RUN_TUI_IN^^}" == "YES" ]] && RUN_TUI="YES" || RUN_TUI="NO" +fi ############################################ # RAM / PARTITION SIZING @@ -44,7 +135,6 @@ SWAP_SIZE="${RAM_GB}GiB" DISK_SIZE=$(lsblk -b -dn -o SIZE "$DRIVE") DISK_GIB=$((DISK_SIZE / 1024 / 1024 / 1024)) - ROOT_GIB=$((DISK_GIB - RAM_GB - 15)) echo "Partition plan:" @@ -73,44 +163,74 @@ mkswap "$SWAP_PART" swapon "$SWAP_PART" ############################################ -# ASK ABOUT FIDO2 LUKS ENROLLMENT +# ENCRYPTION (OPTIONAL) ############################################ -read -rp "Enable FIDO2 unlocking for root partition? (YES/NO): " FIDO_ROOT +LUKS_BACKUP_KEY="" # path to key file, set only when encryption is active -############################################ -# LUKS ENCRYPT ROOT -############################################ -echo "Encrypting root partition..." -cryptsetup -v luksFormat "$ROOT_PART" -cryptsetup open "$ROOT_PART" cryptroot +if [[ "$ENCRYPT_DISK" == "YES" ]]; then + echo "Encrypting root partition..." + cryptsetup -v luksFormat "$ROOT_PART" + cryptsetup open "$ROOT_PART" cryptroot -############################################ -# OPTIONAL FIDO2 ENROLLMENT -############################################ -if [[ "$FIDO_ROOT" == "YES" ]]; then - echo "Insert FIDO2 key for LUKS and touch when prompted..." - systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no + # ── Auto-generate backup LUKS key ────────────────────────────────────────── + # A random key is enrolled as a second LUKS slot so recovery is possible + # without the primary passphrase. It is written to /_LUKS_BACKUP_KEY in the + # new system (inside the encrypted container) where only root can read it. + LUKS_BACKUP_KEY=$(mktemp /tmp/luks-backup-key.XXXXXX) + dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0 > "$LUKS_BACKUP_KEY" + echo "Enrolling auto-generated backup LUKS key..." + cryptsetup luksAddKey "$ROOT_PART" "$LUKS_BACKUP_KEY" + + # ── Optional FIDO2 enrollment ───────────────────────────────────────────── + if [[ "$FIDO_ROOT" == "YES" ]]; then + echo "Insert FIDO2 key for LUKS and touch when prompted..." + systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no + fi + + ############################################ + # BTRFS ON ENCRYPTED ROOT + ############################################ + mkfs.btrfs /dev/mapper/cryptroot + mount /dev/mapper/cryptroot /mnt + btrfs subvolume create /mnt/@ + btrfs subvolume create /mnt/@home + umount /mnt + + mount -o subvol=@ /dev/mapper/cryptroot /mnt + mkdir -p /mnt/home + mount -o subvol=@home /dev/mapper/cryptroot /mnt/home + +else + echo "Skipping encryption — formatting root directly." + + ############################################ + # BTRFS ON UNENCRYPTED ROOT + ############################################ + mkfs.btrfs "$ROOT_PART" + mount "$ROOT_PART" /mnt + btrfs subvolume create /mnt/@ + btrfs subvolume create /mnt/@home + umount /mnt + + mount -o subvol=@ "$ROOT_PART" /mnt + mkdir -p /mnt/home + mount -o subvol=@home "$ROOT_PART" /mnt/home fi -############################################ -# BTRFS SUBVOLUMES -############################################ -mkfs.btrfs /dev/mapper/cryptroot -mount /dev/mapper/cryptroot /mnt -btrfs subvolume create /mnt/@ -btrfs subvolume create /mnt/@home -umount /mnt - -mount -o subvol=@ /dev/mapper/cryptroot /mnt -mkdir /mnt/home -mount -o subvol=@home /dev/mapper/cryptroot /mnt/home -mkdir /mnt/boot +mkdir -p /mnt/boot mount "$BOOT_PART" /mnt/boot +# Place backup key inside the new system (only accessible when disk is unlocked) +if [[ -n "$LUKS_BACKUP_KEY" ]]; then + install -m 400 "$LUKS_BACKUP_KEY" /mnt/_LUKS_BACKUP_KEY + rm -f "$LUKS_BACKUP_KEY" + echo "Backup LUKS key written to /_LUKS_BACKUP_KEY in new system." +fi + ############################################ # GPU DETECTION ############################################ -GPU_INFO=$(lspci | grep -E "VGA|3D") +GPU_INFO=$(lspci | grep -E "VGA|3D" || true) GPU_PKGS="" if echo "$GPU_INFO" | grep -qi "NVIDIA"; then GPU_PKGS="nvidia nvidia-utils" @@ -119,39 +239,36 @@ elif echo "$GPU_INFO" | grep -qi "AMD"; then elif echo "$GPU_INFO" | grep -qi "Intel"; then GPU_PKGS="xf86-video-intel" fi - -echo "Detected GPU: $GPU_INFO" - -############################################ -# USER INPUT -############################################ -read -rp "Enter kernel package (e.g., linux, linux-lts): " KERNEL -read -rp "Enter hostname: " HOSTNAME -read -rp "Enter username: " USERNAME -read -rsp "Enter password for $USERNAME: " USERPASS -echo -read -rp "Enable FIDO2 authentication for user login? (YES/NO): " FIDO_USER +echo "Detected GPU: ${GPU_INFO:-none}" ############################################ # BASE INSTALL ############################################ +# shellcheck disable=SC2086 pacstrap -K /mnt base base-devel "$KERNEL" linux-firmware vim bash zsh git less btop fastfetch \ - networkmanager grub cryptsetup libfido2 pam-u2f efibootmgr sudo btrfs-progs lvm2 $GPU_PKGS + networkmanager grub cryptsetup libfido2 pam-u2f efibootmgr sudo btrfs-progs lvm2 jq $GPU_PKGS ############################################ # FSTAB ############################################ genfstab -U /mnt >> /mnt/etc/fstab +############################################ +# COPY ANSWERFILE INTO NEW SYSTEM +############################################ +if $AF_MODE; then + install -m 644 "$ANSWERFILE" /mnt/answerfile.json +fi + ############################################ # PASS VARIABLES INTO CHROOT ############################################ -export HOSTNAME USERNAME USERPASS ROOT_PART FIDO_ROOT FIDO_USER +export HOSTNAME USERNAME USERPASS ROOT_PART KERNEL FIDO_ROOT FIDO_USER ENCRYPT_DISK ############################################ # CHROOT CONFIGURATION ############################################ -arch-chroot /mnt /bin/bash <<'EOF' +arch-chroot /mnt /bin/bash <<'CHROOT_EOF' set -euo pipefail # Locale @@ -175,10 +292,12 @@ echo "%wheel ALL=(ALL:ALL) ALL" >> /etc/sudoers ################################################### # INITRAMFS CONFIG ################################################### -if [[ "$FIDO_ROOT" == "YES" ]]; then - sed -i 's/^HOOKS=.*/HOOKS=(base btrfs udev systemd microcode kms autodetect modconf consolefont block sd-encrypt encrypt lvm2 filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf +if [[ "$ENCRYPT_DISK" == "YES" && "$FIDO_ROOT" == "YES" ]]; then + sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf +elif [[ "$ENCRYPT_DISK" == "YES" ]]; then + sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf else - sed -i 's/^HOOKS=.*/HOOKS=(base btrfs udev systemd microcode kms autodetect modconf consolefont block encrypt encrypt lvm2 filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf + sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck)/' /etc/mkinitcpio.conf fi mkinitcpio -p "$KERNEL" @@ -187,49 +306,46 @@ mkinitcpio -p "$KERNEL" # GRUB CONFIG ################################################### UUID=$(blkid -s UUID -o value "$ROOT_PART") - -if [[ "$FIDO_ROOT" == "YES" ]]; then - KERNEL_CMD="rd.luks.name=${UUID}=cryptroot root=/dev/mapper/cryptroot" +if [[ "$ENCRYPT_DISK" == "YES" ]]; then + if [[ "$FIDO_ROOT" == "YES" ]]; then + KERNEL_CMD="rd.luks.name=${UUID}=cryptroot root=/dev/mapper/cryptroot" + else + KERNEL_CMD="cryptdevice=UUID=${UUID}:cryptroot root=/dev/mapper/cryptroot" + fi else - KERNEL_CMD="cryptdevice=UUID=${UUID}:cryptroot root=/dev/mapper/cryptroot" + KERNEL_CMD="root=UUID=${UUID} rootflags=subvol=@" fi sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$KERNEL_CMD\"|" /etc/default/grub -grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=M-Archy-GRUB-CuIn +grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=M-Archy-GRUB grub-mkconfig -o /boot/grub/grub.cfg ################################################### # USER FIDO2 LOGIN ################################################### if [[ "$FIDO_USER" == "YES" ]]; then - mkdir -p /home/$USERNAME/.config/Yubico + mkdir -p "/home/$USERNAME/.config/Yubico" echo "Insert FIDO2 key for user login and touch when prompted..." - sudo -u "$USERNAME" pamu2fcfg -u "$USERNAME" > /home/$USERNAME/.config/Yubico/u2f_keys - chown "$USERNAME":"$USERNAME" /home/$USERNAME/.config/Yubico/u2f_keys - + sudo -u "$USERNAME" pamu2fcfg -u "$USERNAME" > "/home/$USERNAME/.config/Yubico/u2f_keys" + chown "$USERNAME":"$USERNAME" "/home/$USERNAME/.config/Yubico/u2f_keys" echo "auth required pam_u2f.so" >> /etc/pam.d/system-local-login fi - ################################################### -# CLONE DOTFILES FOR POST-INSTALL SETUP +# CLONE DOTFILES ################################################### echo "Cloning dotfiles..." -git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git /home/$USERNAME/Dotfiles \ - && chown -R $USERNAME:$USERNAME /home/$USERNAME/Dotfiles \ +git clone https://git.abdelbaki.eu/The_miro/Dotfiles.git "/home/$USERNAME/Dotfiles" \ + && chown -R "$USERNAME":"$USERNAME" "/home/$USERNAME/Dotfiles" \ || echo "Warning: dotfiles clone failed — clone manually after first boot." -EOF +CHROOT_EOF ############################################ -# DOTFILES SETUP (in-chroot, optional) +# DOTFILES TUI SETUP (in-chroot, optional) ############################################ -echo -read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI -_RUN_TUI="${_RUN_TUI:-YES}" - -if [[ "${_RUN_TUI^^}" == "YES" ]]; then +if [[ "${RUN_TUI^^}" == "YES" ]]; then echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \ | arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null @@ -241,43 +357,47 @@ if [[ "${_RUN_TUI^^}" == "YES" ]]; then arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd fi +# Remove answerfile from new system after setup is complete (contains sensitive paths/config) +if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then + rm -f /mnt/answerfile.json +fi + ############################################ -# SUMMARY OUTPUT (NEW) +# SUMMARY ############################################ echo echo "############################################" echo " INSTALL SUMMARY" echo "############################################" -echo "Drive: $DRIVE" -echo "Boot partition: $BOOT_PART" -echo "Root partition: $ROOT_PART" -echo "Swap partition: $SWAP_PART" +echo "Drive: $DRIVE" +echo "Boot partition: $BOOT_PART" +echo "Root partition: $ROOT_PART" +echo "Swap partition: $SWAP_PART" echo -echo "Hostname: $HOSTNAME" -echo "Username: $USERNAME" -echo "Kernel: $KERNEL" -echo "GPU detected: $GPU_INFO" +echo "Hostname: $HOSTNAME" +echo "Username: $USERNAME" +echo "Kernel: $KERNEL" +echo "GPU detected: ${GPU_INFO:-none}" echo +echo "Disk encryption: $ENCRYPT_DISK" echo "FIDO2 root unlock: $FIDO_ROOT" echo "FIDO2 user login: $FIDO_USER" +[[ "$ENCRYPT_DISK" == "YES" ]] && echo "LUKS backup key: /_LUKS_BACKUP_KEY (in new system)" echo echo "Boot size: $BOOT_SIZE" echo "Root size: ${ROOT_GIB}GiB" echo "Swap size: $SWAP_SIZE" echo -echo "Log file saved to: $LOGFILE" +echo "Log file: $LOGFILE" echo "############################################" echo -cp $LOGFILE /mnt/boot/ +cp "$LOGFILE" /mnt/boot/ 2>/dev/null || true -############################################ -# DONE -############################################ -echo "Installation complete! You can now unmount and reboot." -echo -if [[ "${_RUN_TUI^^}" != "YES" ]]; then +echo "Installation complete! Unmount and reboot:" +echo " umount -R /mnt && reboot" +if [[ "${RUN_TUI^^}" != "YES" ]]; then + echo echo "After first boot, login as $USERNAME and run:" echo " ~/Dotfiles/setup/tui-install.sh" fi - diff --git a/setup/archbaseos-guided-install.sh b/setup/archbaseos-guided-install.sh index e8cb1e8..5974a1d 100755 --- a/setup/archbaseos-guided-install.sh +++ b/setup/archbaseos-guided-install.sh @@ -1,8 +1,13 @@ #!/usr/bin/env bash +# archbaseos-guided-install.sh — guided (dialog-based) Arch Linux base installer +# +# If /answerfile.json exists (e.g. embedded via build.sh --preconf), all prompts +# are answered from it. Missing fields fall back to interactive prompts. + set -euo pipefail ############################################ -# Helper Functions +# Helper Functions ############################################ confirm() { @@ -23,20 +28,57 @@ pause() { } ############################################ -# Begin +# ANSWERFILE ############################################ +ANSWERFILE="${ANSWERFILE:-/answerfile.json}" +AF_MODE=false +[[ -f "$ANSWERFILE" ]] && AF_MODE=true -echo "== Arch Linux FIDO2-Ready Installer ==" +af_get() { + local val + val=$(jq -r "${1} // empty" "$ANSWERFILE" 2>/dev/null || true) + if [[ -z "$val" ]]; then printf '%s' "${2:-}"; else printf '%s' "$val"; fi +} +af_bool() { + local val; val=$(jq -r "${1} // false" "$ANSWERFILE" 2>/dev/null || true) + [[ "$val" == "true" ]] && echo "YES" || echo "NO" +} + +get_mac_suffix() { + local mac + mac=$(ip link show 2>/dev/null \ + | awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}') + printf '%s' "${mac//:/}" +} + +if $AF_MODE; then + echo "== Arch Linux Guided Installer (answerfile mode) ==" + command -v jq &>/dev/null || pacman -Sy --noconfirm jq +else + echo "== Arch Linux FIDO2-Ready Installer ==" +fi + +############################################ +# Begin +############################################ lsblk -DRIVE=$(ask "Enter install drive (e.g., /dev/sda)") -confirm "$DRIVE" || exit 1 + +if $AF_MODE && [[ -n "$(af_get '.drive')" ]]; then + DRIVE=$(af_get '.drive') + echo "Drive (from answerfile): $DRIVE" + echo "WARNING: All data on $DRIVE will be erased. Proceeding in 5 seconds..." + sleep 5 +else + DRIVE=$(ask "Enter install drive (e.g., /dev/sda)") + confirm "$DRIVE" || exit 1 +fi # Required packages -pacman -Syd --noconfirm parted cryptsetup libfido2 pam-u2f systemd-ukify +pacman -Syd --noconfirm parted cryptsetup libfido2 pam-u2f systemd-ukify jq ############################################ -# Partitioning +# Partitioning ############################################ RAM_GB=$(free --giga | awk '/Mem/ {print $2}') @@ -68,53 +110,101 @@ mkswap "$SWAP_PART" swapon "$SWAP_PART" ############################################ -# Encryption +# User input ############################################ - -echo -read -rp "Enable FIDO2 for unlocking root? (YES/NO): " ENABLE_FIDO_ROOT - -echo "Formatting LUKS2 root..." -cryptsetup luksFormat "$ROOT_PART" --type luks2 -cryptsetup open "$ROOT_PART" cryptroot - -if [[ $ENABLE_FIDO_ROOT == "YES" ]]; then - echo "Enroll FIDO2 key for LUKS2" - pause - systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no +if $AF_MODE; then + KERNEL=$(af_get '.kernel' 'linux') + RAW_HOSTNAME=$(af_get '.hostname' '') + if [[ -n "$RAW_HOSTNAME" ]]; then + HOSTNAME="${RAW_HOSTNAME}-$(get_mac_suffix)" + else + HOSTNAME="arch" + fi + USERNAME=$(af_get '.username' '') + ENCRYPT_DISK=$(af_bool '.encrypt') + ENABLE_FIDO_ROOT=$(af_bool '.fido2_root') + ENABLE_FIDO_USER=$(af_bool '.fido2_user') + RUN_TUI=$(af_bool '.run_tui') + echo "Kernel: $KERNEL / Hostname: $HOSTNAME / Username: $USERNAME" + echo "Encrypt: $ENCRYPT_DISK / FIDO2 root: $ENABLE_FIDO_ROOT / FIDO2 user: $ENABLE_FIDO_USER" +else + KERNEL=$(ask "Kernel (linux, linux-lts, linux-zen)") + HOSTNAME=$(ask "Hostname") + USERNAME=$(ask "Username") + read -rp "Enable disk encryption? (YES/NO): " ENCRYPT_DISK + ENABLE_FIDO_ROOT="NO" + if [[ "$ENCRYPT_DISK" == "YES" ]]; then + read -rp "Enable FIDO2 for unlocking root? (YES/NO): " ENABLE_FIDO_ROOT + fi + read -rp "Enable FIDO2 for user login? (YES/NO): " ENABLE_FIDO_USER fi -# Add fallback password -echo "Adding fallback LUKS password (recommended)" -cryptsetup luksAddKey "$ROOT_PART" +read -rsp "Password for $USERNAME: " USERPASS; echo +[[ -z "$USERPASS" ]] && { echo "Error: password cannot be empty."; exit 1; } ############################################ -# Filesystem +# Encryption (optional) ############################################ +LUKS_BACKUP_KEY="" -mkfs.btrfs /dev/mapper/cryptroot +if [[ "$ENCRYPT_DISK" == "YES" ]]; then + echo "Formatting LUKS2 root..." + cryptsetup luksFormat "$ROOT_PART" --type luks2 + cryptsetup open "$ROOT_PART" cryptroot -mount /dev/mapper/cryptroot /mnt -btrfs subvolume create /mnt/@ -btrfs subvolume create /mnt/@home -umount /mnt + # ── Auto-generate backup LUKS key ───────────────────────────────────────── + LUKS_BACKUP_KEY=$(mktemp /tmp/luks-backup-key.XXXXXX) + dd if=/dev/urandom bs=64 count=1 2>/dev/null | base64 -w0 > "$LUKS_BACKUP_KEY" + echo "Enrolling auto-generated backup LUKS key..." + cryptsetup luksAddKey "$ROOT_PART" "$LUKS_BACKUP_KEY" -mount -o subvol=@ /dev/mapper/cryptroot /mnt -mkdir -p /mnt/home -mount -o subvol=@home /dev/mapper/cryptroot /mnt/home + if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then + echo "Enroll FIDO2 key for LUKS2" + pause + systemd-cryptenroll "$ROOT_PART" --fido2-device=auto --fido2-with-client-pin=no + fi + + mkfs.btrfs /dev/mapper/cryptroot + mount /dev/mapper/cryptroot /mnt + btrfs subvolume create /mnt/@ + btrfs subvolume create /mnt/@home + umount /mnt + + mount -o subvol=@ /dev/mapper/cryptroot /mnt + mkdir -p /mnt/home + mount -o subvol=@home /dev/mapper/cryptroot /mnt/home + +else + echo "Skipping encryption — formatting root directly." + + mkfs.btrfs "$ROOT_PART" + mount "$ROOT_PART" /mnt + btrfs subvolume create /mnt/@ + btrfs subvolume create /mnt/@home + umount /mnt + + mount -o subvol=@ "$ROOT_PART" /mnt + mkdir -p /mnt/home + mount -o subvol=@home "$ROOT_PART" /mnt/home +fi mkdir -p /mnt/boot mount "$EFI_PART" /mnt/boot -############################################ -# Base System Install -############################################ +# Place backup key inside the new system (readable only by root, inside LUKS container) +if [[ -n "$LUKS_BACKUP_KEY" ]]; then + install -m 400 "$LUKS_BACKUP_KEY" /mnt/_LUKS_BACKUP_KEY + rm -f "$LUKS_BACKUP_KEY" + echo "Backup LUKS key written to /_LUKS_BACKUP_KEY in new system." +fi -GPU_INFO=$(lspci | grep -E "VGA|3D") +############################################ +# Base System Install +############################################ +GPU_INFO=$(lspci | grep -E "VGA|3D" || true) GPU_PKGS="" if echo "$GPU_INFO" | grep -qi nvidia; then - #GPU_PKGS="nvidia nvidia-utils" GPU_PKGS="nvidia-open" elif echo "$GPU_INFO" | grep -qi amd; then GPU_PKGS="xf86-video-amdgpu" @@ -122,34 +212,35 @@ elif echo "$GPU_INFO" | grep -qi intel; then GPU_PKGS="xf86-video-intel" fi -KERNEL=$(ask "Kernel (linux, linux-lts, linux-zen)") -HOSTNAME=$(ask "Hostname") -USERNAME=$(ask "Username") -read -rsp "Password for $USERNAME: " USERPASS; echo - -read -rp "Enable FIDO2 for user login? (YES/NO): " ENABLE_FIDO_USER - +# shellcheck disable=SC2086 pacstrap /mnt \ base base-devel "$KERNEL" linux-firmware vim zsh git networkmanager grub efibootmgr \ - btrfs-progs cryptsetup libfido2 pam-u2f sudo less "$GPU_PKGS" + btrfs-progs cryptsetup libfido2 pam-u2f sudo less jq $GPU_PKGS genfstab -U /mnt >> /mnt/etc/fstab ############################################ -# CHROOT Configuration +# COPY ANSWERFILE INTO NEW SYSTEM ############################################ +if $AF_MODE; then + install -m 644 "$ANSWERFILE" /mnt/answerfile.json +fi +############################################ +# CHROOT Configuration +############################################ ROOT_UUID=$(blkid -s UUID -o value "$ROOT_PART") - arch-chroot /mnt /usr/bin/env \ HOSTNAME="$HOSTNAME" \ USERNAME="$USERNAME" \ USERPASS="$USERPASS" \ + ENCRYPT_DISK="$ENCRYPT_DISK" \ ENABLE_FIDO_ROOT="$ENABLE_FIDO_ROOT" \ ENABLE_FIDO_USER="$ENABLE_FIDO_USER" \ ROOT_UUID="$ROOT_UUID" \ - /bin/bash <<'EOF' + ROOT_PART="$ROOT_PART" \ + /bin/bash <<'CHROOT_EOF' set -euo pipefail @@ -176,19 +267,26 @@ chown -R "$USERNAME:$USERNAME" "/home/$USERNAME" echo "%wheel ALL=(ALL) ALL" >> /etc/sudoers -# Initramfs + GRUB for FIDO2 -if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then - sed -i 's/^HOOKS=.*/HOOKS=(base systemd autodetect modconf block sd-encrypt filesystems keyboard fsck)/' /etc/mkinitcpio.conf +# Initramfs +if [[ "$ENCRYPT_DISK" == "YES" && "$ENABLE_FIDO_ROOT" == "YES" ]]; then + sed -i 's/^HOOKS=.*/HOOKS=(base udev systemd autodetect microcode modconf kms consolefont block sd-encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf +elif [[ "$ENCRYPT_DISK" == "YES" ]]; then + sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block encrypt lvm2 btrfs filesystems keyboard keymap fsck)/' /etc/mkinitcpio.conf else - sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)/' /etc/mkinitcpio.conf + sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect microcode modconf kms consolefont block btrfs filesystems keyboard fsck)/' /etc/mkinitcpio.conf fi mkinitcpio -P -if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then - GRUB_CMDLINE="rd.luks.name=$ROOT_UUID=cryptroot rd.luks.options=fido2-device=auto root=/dev/mapper/cryptroot" +# GRUB +if [[ "$ENCRYPT_DISK" == "YES" ]]; then + if [[ "$ENABLE_FIDO_ROOT" == "YES" ]]; then + GRUB_CMDLINE="rd.luks.name=$ROOT_UUID=cryptroot rd.luks.options=fido2-device=auto root=/dev/mapper/cryptroot" + else + GRUB_CMDLINE="cryptdevice=UUID=$ROOT_UUID:cryptroot root=/dev/mapper/cryptroot" + fi else - GRUB_CMDLINE="cryptdevice=UUID=$ROOT_UUID:cryptroot root=/dev/mapper/cryptroot" + GRUB_CMDLINE="root=UUID=${ROOT_UUID} rootflags=subvol=@" fi sed -i "s|^GRUB_CMDLINE_LINUX=.*|GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE\"|" /etc/default/grub @@ -199,23 +297,26 @@ grub-mkconfig -o /boot/grub/grub.cfg # User login FIDO2 if [[ "$ENABLE_FIDO_USER" == "YES" ]]; then echo "Enrolling FIDO2 for user login" - mkdir -p /home/$USERNAME/.config/Yubico - chown $USERNAME:$USERNAME /home/$USERNAME/.config/Yubico + mkdir -p "/home/$USERNAME/.config/Yubico" + chown "$USERNAME:$USERNAME" "/home/$USERNAME/.config/Yubico" sudo -u "$USERNAME" bash -c "pamu2fcfg >> /home/$USERNAME/.config/Yubico/u2f_keys" echo "auth required pam_u2f.so" >> /etc/pam.d/system-auth fi - -EOF +CHROOT_EOF ############################################ -# DOTFILES SETUP (in-chroot, optional) +# DOTFILES SETUP (in-chroot, optional) ############################################ -echo -read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _RUN_TUI -_RUN_TUI="${_RUN_TUI:-YES}" +if $AF_MODE; then + _DO_TUI="${RUN_TUI}" +else + read -rp "Run dotfiles TUI setup inside chroot now? [YES/no]: " _TUI_IN + _TUI_IN="${_TUI_IN:-YES}" + [[ "${_TUI_IN^^}" == "YES" ]] && _DO_TUI="YES" || _DO_TUI="NO" +fi -if [[ "${_RUN_TUI^^}" == "YES" ]]; then +if [[ "${_DO_TUI^^}" == "YES" ]]; then echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" \ | arch-chroot /mnt tee /etc/sudoers.d/99-setup-nopasswd > /dev/null @@ -227,10 +328,20 @@ if [[ "${_RUN_TUI^^}" == "YES" ]]; then arch-chroot /mnt rm -f /etc/sudoers.d/99-setup-nopasswd fi +# Remove answerfile from new system after setup completes +if $AF_MODE && [[ -f /mnt/answerfile.json ]]; then + rm -f /mnt/answerfile.json +fi + echo "Installation complete!" echo " umount -R /mnt && reboot" -if [[ "${_RUN_TUI^^}" != "YES" ]]; then +if [[ "${_DO_TUI^^}" != "YES" ]]; then echo echo "After first boot, login as ${USERNAME} and run:" echo " ~/Dotfiles/setup/tui-install.sh" fi +if [[ "$ENCRYPT_DISK" == "YES" ]]; then + echo + echo "LUKS backup key stored at /_LUKS_BACKUP_KEY in the new system." + echo "Keep this file secure — it can unlock the root partition." +fi