Automated Arch Install

Based on the last post, I created a a script to automate my Arch installations.


Note: This is very new and highly experimental. Please use at your own risk.


Download Arch

Download the ISO for Arch and copy it to a USB key, CD or whatever you need. As I am installing it on to a VM, it was easy, I just needed to mount it. This ISO is the latest version at the time of writing, no doubt it will be updated by the time you read this, but it really doesn’t matter because Arch is designed to be easily updatable.

wget ca.us.mirror.archlinux-br.org/iso/2020.07.01/archlinux-2020.07.01-x86_64.iso

The full list of mirrors is available at https://www.archlinux.org/download/

Booting Arch

Boot the ISO and select the highlighted option.

Once Booted, you will be presented with a command prompt. No installer, text or graphical. Welcome to the wonders of Arch!

Network Access

We need the DHCP assigned IP address and we need to verify everything is working correctly.

# Display the IP address
ip a

# Verify internet access
ping -c 3 8.8.8.8
ping www.google.com

Start SSH

The Automated installations via the scripts is easier via SSH. You could copy them across using SCP but pasting them into your SSH terminal is way easier.

# set a root password
passwd root

# start ssh
systemctl list-unit-files -t service | grep ssh
systemctl start sshd

Connect to your Arch installer from you desktop

ssh root@[ip address]

Copy over the script

touch archinstall.sh
chmod 777 archinstall.sh
nano archinstall.sh

Copy the following script and paste it into the editor

#!/bin/bash
# //----------------------------------------------------------------------------
# // archinstall.sh
# //----------------------------------------------------------------------------
# // my script to automate arch installations
# //----------------------------------------------------------------------------
# // Gareth Jones - gareth@gareth.com
# //----------------------------------------------------------------------------


# //----------------------------------------------------------------------------
# // The following commands need to be entered at the console to enable SSH
# //----------------------------------------------------------------------------
#
# root@archiso ~ # setfont latarcyrheb-sun32  # only needed if too small
# root@archiso ~ # ip a
# root@archiso ~ # ping -c 3 8.8.8.8
# root@archiso ~ # ping www.google.com
# root@archiso ~ # passwd root
# root@archiso ~ # systemctl list-unit-files -t service | grep ssh
# root@archiso ~ # systemctl start sshd
# root@archiso ~ # touch archinstall.sh
# root@archiso ~ # chmod 777 archinstall.sh
#
# //----------------------------------------------------------------------------
# // ssh is now enabled, you can now connect remotely
# // ssh root@[ip address]
# // nano archinstall.sh
# // paste in the contents of this file and execute it
# //----------------------------------------------------------------------------

# //----------------------------------------------------------------------------
# // Globabl variables
# //----------------------------------------------------------------------------
WAITFORIT="FALSE" # set to true to debug

# //----------------------------------------------------------------------------
# // Function : pause()
# //----------------------------------------------------------------------------
# // Purpose  : Utility function to pause with a message
# //----------------------------------------------------------------------------
function pause() {
    if [ "$WAITFORIT" == "TRUE" ]; then
        read -p "$*"        
    fi
}

# //----------------------------------------------------------------------------
# // Function : phase1()
# //----------------------------------------------------------------------------
# // Purpose  : This phase runs on first boot of the arch.iso
# //----------------------------------------------------------------------------
function phase1() {
    # this is a hack
    P1="1"
    P2="2"
    P3="3"
    P4="4"

    # get disk name
    cat /proc/partitions
    read -p "Enter the device name ... " READ

    DISK=$READ

    # overwrite it with random data
    pause 'Wiping Disk [Enter]'
    badblocks -c 10240 -s -w -t random -v /dev/$DISK

    pause 'Create Partitions [Enter]'
    parted --script /dev/$DISK \
        mklabel gpt \
        mkpart ESP fat32 1MiB 200MiB \
        set 1 boot on \
        name 1 efi \
        mkpart primary 200MiB 800MiB \
        name 2 boot \
        mkpart primary 800MiB 32Gib \
        name 3 swap \
        mkpart primary 32GiB 100% \
        name 3 btrfs \
        print \
        quit


    # create LUKS volume
    pause 'Create LUKS volume [Enter]'
    cryptsetup luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sha256 --use-random /dev/$DISK$P4

    # open the root luks volume
    pause 'Open LUKS volume [Enter]'
    cryptsetup luksOpen /dev/$DISK$P4 cryptroot

    # format Partitions
    pause 'Format Partitions [Enter]'
    mkfs.fat -F32 /dev/$DISK$P1
    mkfs.ext4 /dev/$DISK$P2
    mkswap /dev/$DISK$P3
    mkfs.btrfs /dev/mapper/cryptroot

    # mount the root filesystem
    pause 'Mount root filesystem [Enter]'
    mount -o noatime,compress=lzo,discard,ssd,defaults /dev/mapper/cryptroot /mnt

    # create the subvolumes
    pause 'Create subvolumes [Enter]'
    cd /mnt
    btrfs subvolume create __active
    btrfs subvolume create __active/rootvol
    btrfs subvolume create __active/home
    btrfs subvolume create __active/var
    btrfs subvolume create __snapshots
    btrfs subvolume create __snapshots/root
    btrfs subvolume create __snapshots/home
    btrfs subvolume create __snapshots/var
    cd 
    umount /mnt

    # mount the subvolumes
    pause 'Mount subvolumes [Enter]'
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__active/rootvol /dev/mapper/cryptroot /mnt
    mkdir /mnt/.snapshots
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__snapshots/root /dev/mapper/cryptroot /mnt/.snapshots
    mkdir /mnt/{home,var,boot}
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__active/home /dev/mapper/cryptroot /mnt/home
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__active/var /dev/mapper/cryptroot /mnt/var
    mkdir /mnt/home/.snapshots
    mkdir /mnt/var/.snapshots
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__snapshots/home /dev/mapper/cryptroot /mnt/home/.snapshots
    mount -o noatime,compress=lzo,discard,ssd,defaults,subvol=__snapshots/var /dev/mapper/cryptroot /mnt/var/.snapshots
    sync

    # mount other partitions
    pause 'Mount other partitions [Enter]'
    mount /dev/$DISK$P2 /mnt/boot
    mkdir /mnt/boot/efi
    mount /dev/$DISK$P1 /mnt/boot/efi
    swapon /dev/$DISK$P3
    swapon -a ; swapon -s

    # install Arch Linux
    pause 'Pacstrap [Enter]'
    pacstrap /mnt base base-devel linux linux-firmware nano btrfs-progs efibootmgr grub networkmanager openssh git --noconfirm

    # generate /etc/fstab
    pause 'Generate /etc/fstab [Enter]'
    genfstab -p -U /mnt >> /mnt/etc/fstab

    # copy script to /mnt ready to be run after chroot
    cp archinstall.sh /mnt/root/

    # chroot
    pause 'About to chroot after which script will terminate. Please re-run script for phase2 [Enter]'
    arch-chroot /mnt
}

# //----------------------------------------------------------------------------
# // Function : phase2()
# //----------------------------------------------------------------------------
# // Purpose  : This phase runs after we chroot
# //----------------------------------------------------------------------------
function phase2() {
    # set the timezone & hardware clock
    pause 'Set the timezone & hardware clock [Enter]'
    ln -s /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
    hwclock --systohc --utc

    # Generate the required locales
    pause 'Generated the required locales [Enter]'
    cp /etc/locale.gen /etc/local.gen.bak
    echo "en_US.UTF-8 UTF-8" > /etc/locale.gen
    locale-gen

    # hostname
    pause 'Hostname & Hosts [Enter]'
    read -p "Enter your hostname : " MYHOST
    echo $MYHOST > /etc/hostname
    echo "127.0.0.1	localhost"                       >> /etc/hosts
    echo "::1		localhost"                       >> /etc/hosts
    echo "127.0.1.1	iamgroot.localdomain	$MYHOST" >> /etc/hosts
    cat /etc/hosts

    # mkinitcpio
    pause 'mkinitcpio, modify hooks [Enter]'
    cp /etc/mkinitcpio.conf /etc/mkinitcpio.conf.bak
    sed -i 's/HOOKS=(base\ udev\ autodetect\ modconf\ block\ filesystems\ keyboard\ fsck)/HOOKS="base\ udev\ autodetect\ modconf\ block\ encrypt\ filesystems\ keyboard\ fsck"/' /etc/mkinitcpio.conf
    mkinitcpio -p linux

    # set the root password
    echo 'Enter a new root password.'
    passwd root

    # autostart network manager & sshd
    systemctl enable NetworkManager.service
    systemctl enable sshd.service

    # install grub
    pause 'install grub [Enter]'
    grub-install --target=x86_64-efi --efi-directory=/boot/efi --recheck

    # configure grub to support LUKS kernel parameters
    pause 'configure grub to support LUKS kernel parameters [Enter]'
    cp /etc/default/grub /etc/default/grub.bak
    sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 quiet"/GRUB_CMDLINE_LINUX_DEFAULT="cryptdevice=\/dev\/sda4:cryptroot\ root=\/dev\/mapper\/cryptroot\ rootflags=subvol=__active\/rootvol\ quiet"/' /etc/default/grub

    # generate grub.cfg file:
    grub-mkconfig -o /boot/grub/grub.cfg
    grub-mkconfig -o /boot/efi/EFI/arch/grub.cfg
    mkdir /boot/efi/EFI/BOOT
    cp /boot/efi/EFI/arch/grubx64.efi /boot/efi/EFI/BOOT/BOOTX64.EFI

    # Allow wheel users to SUDO
    pause 'Allow wheel users to SUDO [Enter]'
    echo "%wheel ALL=(ALL) ALL" | (EDITOR="tee -a" visudo)

    # Create User Account
    pause 'Create user account [Enter]'
    read -p "Enter your username: " USERNAME
    useradd -m -G wheel $USERNAME
    passwd $USERNAME

    # copy script to user folder ready for phase3
    cp archinstall.sh /home/$USERNAME

    # exit and reboot
    echo 'About to exit script. Time to reboot and login as a user.'
    echo 'Type exit [Enter] to exit CHROOT.'
    echo 'Type reboot [Enter] to reboot.'
    echo 'After rebooting ssh %USERNAME@<IP ADDRESS>.'
    echo 'Remember - You will need to enter your LUKS password at the console to boot.'
    pause 'Press [Enter]'
    sync
}

# //----------------------------------------------------------------------------
# // Function : phase3()
# //----------------------------------------------------------------------------
# // Purpose  : This phase runs in the end user account, on first boot
# //----------------------------------------------------------------------------
function phase3() {
    git clone https://aur.archlinux.org/yay.git
    cd yay/
    makepkg -si --noconfirm
    cd ..
    sudo rm -dR yay/
}


# //----------------------------------------------------------------------------
# // Function : main()
# //----------------------------------------------------------------------------
# // Purpose  : Controls which script runs, needs to be automated
# //----------------------------------------------------------------------------

# ask for phase number 
read -p "Enter phase number (1-3) : " PHASE

# select script
if [ "$PHASE" == "1" ]; then
    phase1
elif [ "$PHASE" == "2" ]; then
    phase2
elif [ "$PHASE" == "3" ]; then
    phase3
else
    echo "Error: Incorrect phase number entered"
fi

Note: The process is highly automated. If you need to debug, change the following global variable to TRUE to get the script to pause between each command and allow debugging.

WAITFORIT="TRUE" # set to true to debug

Running Phase 1

./archinstall.sh

The script is multi-functional – the same script is used by all phases of the installation.

You will be prompted for the installation phase.
Press 1 [Enter] to continue.

Next we need to know the device name of your installation hard disk. A list of all the available partitions is displayed and you will be prompted for input. Normally the entry required is sda.

After this you will only be prompted to confirm creation of the LUKS volume [YES] and the password (three times – twice to create and once to open).

The final command the script runs is to chroot to the newly installed operating system. After which the script terminates (I am yet to find a way around this). Now it is time to initiate phase 2.


Running Phase 2

./archinstall.sh

You will be prompted for the installation phase.
Press 2 [Enter] to continue.

This phase of the script will also prompt you for the following items –

  1. The hostname.
  2. The root password (twice).
  3. The user account name.
  4. The user password (twice).

At the end of the script you are returned to the prompt.
Type exit [Enter] to exit CHROOT.
Type reboot [Enter] to reboot.


Running Phase 3

Connect to the server using your user account.

ssh [username]@[ip address]

Initiate the script

./archinstall.sh

You will be prompted for the installation phase.
Press 3 [Enter] to continue.

Job done.

1 Comment

This still works, perfectly. Did not have one issue with the directions here. Thank you for your work and expertise.

Leave a Reply