Arch Linux automated install script

2022-11-06

Manual install becomes boring after a while. Especially when creating multiple virtual machines.

Featuring:

  • luks encrypted volume
  • UEFI systemd-boot
  • passwordless sudo for main user
  • ansible ready (my tool of choice when it comes to post install config)
  • shellcheck approved

There are four things that demands caution:

  1. This script is destructive, and I mean it - it will wipe your device clean (DISK variable), so you better verify it line by line before actually using it.
  2. Passwords in plaintext = bad security practice. Goto 3.
  3. Primary purpose is bootstraping virtual machines. Running it on physical machines/production is definitely possible, but I don't encourage it.
  4. Don't ask me to restore your backups, which you forgot to make before running this script. I'm not responsible for misusing the power of convenience and time saving this script provides.
#!/usr/bin/env bash

set -e

export DISK="/dev/sda"
export DEV_EFI="$DISK"1
export DEV_CRYPT="$DISK"2
export HOSTNAME="arch-vm"
export LOCALE_LANG="en_US.UTF-8"
export LOCALE_TIME="pl_PL.UTF-8"
export TIMEZONE="Europe/Warsaw"
export USERNAME="archimedes"
export GREEN="\033[0;32m"

export LUKS_PASSWORD="test"
export USER_PASSWORD="test"
export ROOT_PASSWORD="test"

main(){

  if [ "$1" == "chroot" ]; then
    chroot
  fi

  read -r -p "Arch Install Script, proceed? (y/n)" choice
  case "$choice" in
  y | Y) base ;;
  n | N) exit ;;
  *) exit ;;
  esac
}

base() {

  echo -e "${GREEN}>>> Preliminary setup"
  tput sgr0

  timedatectl set-ntp true
  loadkeys pl

  echo -e "${GREEN}>>> Creating Partitions"
  tput sgr0

  parted -s "$DISK" mklabel gpt
  parted -s "$DISK" mkpart primary fat32 1MiB 512MiB
  parted -s "$DISK" set 1 esp on
  parted -s "$DISK" mkpart primary ext4 512MiB 100%

  echo -e "${GREEN}>>> Partition created"
  echo -e "${GREEN}>>> Setup luks on lvm"
  tput sgr0

  echo -n "$LUKS_PASSWORD" | cryptsetup luksFormat "$DEV_CRYPT" -
  echo -n "$LUKS_PASSWORD" | cryptsetup luksOpen "$DEV_CRYPT" lvm -
  pvcreate -f /dev/mapper/lvm
  vgcreate arch /dev/mapper/lvm
  lvcreate -l +100%FREE arch -n root
  mkfs.ext4 /dev/mapper/arch-root
  mount /dev/mapper/arch-root /mnt
  mkdir /mnt/boot
  mkfs.fat -F32 "$DEV_EFI"
  mount "$DEV_EFI" /mnt/boot

  echo -e "${GREEN}>>> Pacstrap"
  tput sgr0

  pacstrap -K /mnt base linux linux-firmware lvm2 man-db man-pages texinfo ansible git networkmanager zsh sudo

  echo -e "${GREEN}>>> Fstab"
  tput sgr0
  genfstab -U /mnt >> /mnt/etc/fstab

  echo -e "${GREEN}>>> Copy bootstrap into mount and exec arch-chroot"
  tput sgr0

  cp "${0##*/}" /mnt

  echo -e "${GREEN}>>> Chroot"
  tput sgr0

  arch-chroot /mnt /"${0##*/}" chroot

  umount -R /mnt
  reboot
}

chroot() {
  echo -e "${GREEN}>>> Timezone n hostname setup"
  tput sgr0

  ln -sf /usr/share/zoneinfo/"$TIMEZONE" /etc/localtime
  hwclock --systohc

  echo "$LOCALE_LANG" UTF-8 >>/etc/locale.gen
  echo "$LOCALE_TIME" UTF-8 >>/etc/locale.gen
  locale-gen
  echo LANG="$LOCALE_LANG" >/etc/locale.conf
  echo LC_TIME="$LOCALE_TIME" >>/etc/locale.conf

  echo "$HOSTNAME" >/etc/hostname

  echo -e "${GREEN}>>> mkinitcpio hooks"
  tput sgr0

  sed -i 's/^HOOKS=.*/HOOKS=(base udev autodetect modconf block keyboard encrypt lvm2 filesystems keyboard fsck)/' /etc/mkinitcpio.conf
  mkinitcpio -p linux

  echo -e "${GREEN}>>> systemd-boot"
  tput sgr0

  bootctl install

  # no microcode, consider adding it when non-VM environment
  DEVCRYPT_UUID="$(blkid -o value -s UUID "$DEV_CRYPT")"

  {
    echo title Arch Linux
    echo linux /vmlinuz-linux
    echo initrd /initramfs-linux.img
    echo options cryptdevice=UUID="$DEVCRYPT_UUID":lvm root=/dev/mapper/arch-root rw
  } >>/boot/loader/entries/arch.conf

  # services
  systemctl enable NetworkManager.service
  systemctl enable NetworkManager-wait-online.service

  # user
  useradd -u 1000 -m -G wheel -s /usr/bin/zsh "$USERNAME"

  echo -e "${GREEN}>>> Sudo config"
  echo '%wheel ALL=(ALL) NOPASSWD: ALL' | EDITOR='tee -a' visudo

  echo -e "${GREEN}>>> $USERNAME password"
  tput sgr0
  echo "$USERNAME:$USER_PASSWORD" | chpasswd

  echo -e "${GREEN}>>> Root password"
  tput sgr0
  echo "root:$ROOT_PASSWORD" | chpasswd

  echo -e "${GREEN}>>> Leaving chroot n rebooting"
  tput sgr0
  exit
}

main "$@"; exit