diff --git a/setup/archiso/build.sh b/setup/archiso/build.sh index 62f3346..0179ff6 100755 --- a/setup/archiso/build.sh +++ b/setup/archiso/build.sh @@ -85,6 +85,7 @@ echo "Embedding installer scripts..." mkdir -p "$PROFILE/airootfs/root/installer" cp "$DOTFILES_DIR/setup/archbaseos-guided-install.sh" "$PROFILE/airootfs/root/installer/" cp "$DOTFILES_DIR/setup/arch-autoinstall.sh" "$PROFILE/airootfs/root/installer/" +cp "$DOTFILES_DIR/setup/reset-arch.sh" "$PROFILE/airootfs/root/installer/" chmod 755 \ "$PROFILE/airootfs/root/launch.sh" \ diff --git a/setup/archiso/overlay/airootfs/root/launch.sh b/setup/archiso/overlay/airootfs/root/launch.sh index e235889..5212471 100644 --- a/setup/archiso/overlay/airootfs/root/launch.sh +++ b/setup/archiso/overlay/airootfs/root/launch.sh @@ -34,6 +34,19 @@ fi loadkeys "$LIVE_KEYMAP" +# ── Action selection (skipped in auto mode) ────────────────────────────────── +if [[ "$MODE" != "auto" ]]; then + echo "" + echo "Select action:" + echo " 1) Install system (fresh install, erases disk)" + echo " 2) Reset system (wipe root, keep user data & passwords)" + read -rp "Choice [1]: " _ACTION_IDX + _ACTION_IDX="${_ACTION_IDX:-1}" + if [[ "$_ACTION_IDX" == "2" ]]; then + exec bash "$INSTALLER_DIR/reset-arch.sh" + fi +fi + case "$MODE" in auto) exec bash "$INSTALLER_DIR/arch-autoinstall.sh" "${@:2}" ;; guided) exec bash "$INSTALLER_DIR/archbaseos-guided-install.sh" ;; diff --git a/setup/archiso/overlay/packages.extra b/setup/archiso/overlay/packages.extra index e803fa6..0522978 100644 --- a/setup/archiso/overlay/packages.extra +++ b/setup/archiso/overlay/packages.extra @@ -2,6 +2,7 @@ git jq pam-u2f +libfido2 btop fastfetch openssh diff --git a/setup/reset-arch.sh b/setup/reset-arch.sh new file mode 100755 index 0000000..2d73dc5 --- /dev/null +++ b/setup/reset-arch.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash +# reset-arch.sh — Reset the root btrfs subvolume while preserving user home data. +# +# What this does: +# 1. Detects LUKS encryption; unlocks via FIDO2 token and/or passphrase +# 2. Saves user credentials and system config from the old @ subvolume +# 3. Clears app configs (~/.config) from @home, preserving auth keys (Yubico/) +# 4. Deletes and recreates the @ (root) btrfs subvolume +# 5. Reinstalls the base system via pacstrap +# 6. Restores credentials, PAM, fstab, mkinitcpio, GRUB config +# 7. Regenerates initramfs and GRUB menu from chroot so the system boots cleanly + +set -euo pipefail + +TMPDIR=$(mktemp -d /tmp/arch-reset.XXXXXX) +trap 'rm -rf "$TMPDIR"' EXIT + +pause() { read -rp "Press ENTER to continue..."; } + +echo "=======================================" +echo " M-Archy System Reset" +echo "=======================================" +echo "This will:" +echo " • Delete and recreate the root (@) btrfs subvolume" +echo " • Reinstall base system packages from scratch" +echo " • Clear all user ~/.config directories (auth keys preserved)" +echo " • Preserve home directories, passwords, and FIDO2 login keys" +echo "" + +# ── Required tools in live environment ────────────────────────────────────── +pacman -Syd --noconfirm cryptsetup btrfs-progs jq libfido2 + +# ── Drive selection ────────────────────────────────────────────────────────── +lsblk +echo "" +read -rp "Enter drive to reset (e.g., /dev/sda): " DRIVE +ROOT_PART="${DRIVE}2" +EFI_PART="${DRIVE}1" + +echo "" +echo "WARNING: The root subvolume on $ROOT_PART will be DELETED and reinstalled." +echo " User home directories will be preserved." +echo " App configs (~/.config) will be wiped (Yubico auth keys excepted)." +echo "" +read -rp "Type YES to continue: " _CONFIRM +[[ "$_CONFIRM" == "YES" ]] || { echo "Aborted."; exit 1; } + +# ── LUKS detection and unlock ──────────────────────────────────────────────── +MAPPER_DEV="$ROOT_PART" + +if cryptsetup isLuks "$ROOT_PART" 2>/dev/null; then + echo "" + echo "Partition $ROOT_PART is LUKS2-encrypted." + echo "Select unlock method:" + echo " 1) Try enrolled token (FIDO2/TPM2) first, fall back to passphrase [recommended]" + echo " 2) Passphrase only" + echo " 3) Enrolled token only (FIDO2/TPM2)" + read -rp "Choice [1]: " _UNLOCK + _UNLOCK="${_UNLOCK:-1}" + + case "$_UNLOCK" in + 1) + echo "Insert FIDO2 key if using one, then press ENTER..." + pause + if ! cryptsetup open --token-only "$ROOT_PART" cryptroot 2>/dev/null; then + echo "Token unlock failed — enter passphrase..." + cryptsetup open "$ROOT_PART" cryptroot + fi + ;; + 2) + cryptsetup open "$ROOT_PART" cryptroot + ;; + 3) + echo "Insert FIDO2 key and press ENTER..." + pause + cryptsetup open --token-only "$ROOT_PART" cryptroot + ;; + *) + echo "Invalid choice, using passphrase..." + cryptsetup open "$ROOT_PART" cryptroot + ;; + esac + MAPPER_DEV="/dev/mapper/cryptroot" + echo "Partition unlocked." +fi + +# ── Detect installed kernel from EFI partition ─────────────────────────────── +TMPBOOT=$(mktemp -d /tmp/arch-reset-boot.XXXXXX) +mount "$EFI_PART" "$TMPBOOT" +KERNEL_PKG="linux" +for _img in "$TMPBOOT"/vmlinuz-*; do + [[ -f "$_img" ]] && KERNEL_PKG=$(basename "$_img" | sed 's/^vmlinuz-//') && break +done +umount "$TMPBOOT"; rmdir "$TMPBOOT" +echo "Detected kernel: $KERNEL_PKG" + +# ── Detect GPU from live hardware ──────────────────────────────────────────── +GPU_INFO=$(lspci 2>/dev/null | grep -E "VGA|3D" || true) +GPU_PKGS="" +if echo "$GPU_INFO" | grep -qi nvidia; then GPU_PKGS="nvidia-open" +elif echo "$GPU_INFO" | grep -qi amd; then GPU_PKGS="xf86-video-amdgpu" +elif echo "$GPU_INFO" | grep -qi intel; then GPU_PKGS="xf86-video-intel" +fi + +# ── Mount btrfs top-level ──────────────────────────────────────────────────── +BTRFS_MNT="$TMPDIR/btrfs" +mkdir -p "$BTRFS_MNT" +mount -o subvolid=5 "$MAPPER_DEV" "$BTRFS_MNT" + +# ── Save critical configuration from @ ────────────────────────────────────── +echo "Saving system configuration..." +SAVED="$TMPDIR/saved" + +_save() { + local src="$BTRFS_MNT/@/etc/$1" + local dst="$SAVED/etc/$1" + [[ -e "$src" ]] || return 0 + mkdir -p "$(dirname "$dst")" + cp -a "$src" "$dst" +} + +_save passwd +_save shadow +_save group +_save gshadow +_save sudoers +_save sudoers.d +_save pam.d +_save hostname +_save locale.conf +_save locale.gen +_save vconsole.conf +_save fstab +_save mkinitcpio.conf +_save mkinitcpio.conf.d +_save default/grub +_save NetworkManager + +# Save timezone symlink target as plain text (the symlink itself can't cross roots) +{ readlink "$BTRFS_MNT/@/etc/localtime" 2>/dev/null || echo "/usr/share/zoneinfo/UTC"; } \ + > "$SAVED/timezone" + +# ── Clear ~/.config in @home (preserve auth-critical subdirs) ──────────────── +echo "Clearing user app configs..." +# Yubico/ holds U2F/FIDO2 PAM keys — deleting these would break FIDO2 login +PRESERVED_CONFIG_DIRS=("Yubico" "pam-u2f") + +for _homedir in "$BTRFS_MNT/@home"/*/; do + [[ -d "$_homedir" ]] || continue + _user=$(basename "$_homedir") + _cfgdir="$_homedir/.config" + [[ -d "$_cfgdir" ]] || continue + echo " Clearing ~/.config for: $_user" + # Top-level files in .config + find "$_cfgdir" -mindepth 1 -maxdepth 1 ! -type d -delete + # Subdirectories, skipping preserved ones + while IFS= read -r -d '' _subdir; do + _dname=$(basename "$_subdir") + _skip=false + for _keep in "${PRESERVED_CONFIG_DIRS[@]}"; do + [[ "$_dname" == "$_keep" ]] && _skip=true && break + done + $_skip || rm -rf "$_subdir" + done < <(find "$_cfgdir" -mindepth 1 -maxdepth 1 -type d -print0) +done + +# ── Delete @ and recreate fresh ────────────────────────────────────────────── +echo "Deleting root subvolume @..." +btrfs subvolume delete "$BTRFS_MNT/@" +echo "Creating fresh root subvolume @..." +btrfs subvolume create "$BTRFS_MNT/@" + +# ── Mount for installation ─────────────────────────────────────────────────── +umount "$BTRFS_MNT" +mount -o subvol=@ "$MAPPER_DEV" /mnt +mkdir -p /mnt/home +mount -o subvol=@home "$MAPPER_DEV" /mnt/home +mkdir -p /mnt/boot +mount "$EFI_PART" /mnt/boot + +# ── Pacstrap base system ───────────────────────────────────────────────────── +echo "Reinstalling base system (this will take a while)..." +# shellcheck disable=SC2086 +pacstrap /mnt \ + base base-devel "$KERNEL_PKG" linux-firmware vim zsh git networkmanager grub efibootmgr \ + btrfs-progs cryptsetup libfido2 pam-u2f sudo less jq $GPU_PKGS + +# ── Restore saved configuration ────────────────────────────────────────────── +echo "Restoring system configuration..." + +_restore() { + local src="$SAVED/etc/$1" + local dst="/mnt/etc/$1" + [[ -e "$src" ]] || return 0 + mkdir -p "$(dirname "$dst")" + cp -a "$src" "$dst" +} + +# Auth files — explicit permissions +[[ -f "$SAVED/etc/passwd" ]] && install -m 644 "$SAVED/etc/passwd" /mnt/etc/passwd +[[ -f "$SAVED/etc/group" ]] && install -m 644 "$SAVED/etc/group" /mnt/etc/group +[[ -f "$SAVED/etc/shadow" ]] && install -m 000 "$SAVED/etc/shadow" /mnt/etc/shadow +[[ -f "$SAVED/etc/gshadow" ]] && install -m 000 "$SAVED/etc/gshadow" /mnt/etc/gshadow + +_restore sudoers +_restore sudoers.d +_restore pam.d +_restore hostname +_restore locale.conf +_restore locale.gen +_restore vconsole.conf +_restore fstab +_restore mkinitcpio.conf +_restore mkinitcpio.conf.d +_restore default/grub +_restore NetworkManager + +TZ_TARGET=$(cat "$SAVED/timezone") +ln -sf "$TZ_TARGET" /mnt/etc/localtime + +# ── Chroot: regenerate initramfs, GRUB menu, services ─────────────────────── +echo "Finalizing inside chroot..." +arch-chroot /mnt /bin/bash <<'CHROOT_EOF' +set -euo pipefail + +locale-gen +hwclock --systohc +systemctl enable NetworkManager +mkinitcpio -P +grub-mkconfig -o /boot/grub/grub.cfg + +# Re-apply correct ownership for user home directories using restored UIDs +while IFS=: read -r _uname _ _uid _gid _ _home _; do + (( _uid >= 1000 )) && [[ -d "$_home" ]] && chown -R "${_uid}:${_gid}" "$_home" || true +done < /etc/passwd +CHROOT_EOF + +echo "" +echo "=======================================" +echo " Reset complete!" +echo "=======================================" +echo " umount -R /mnt && reboot"