1
\$\begingroup\$

I'm trying to automate my Arch Linux installation using a bash script. The script would allow me to choose between installing Arch on a single SSD or using a combination of SSD (for root and home) and HDD (for specific user directories like Downloads, Documents, etc.). I'd like some feedback on my approach and any potential improvements.

Here are the key features of my script:

  1. Allows selection of filesystem (btrfs, ext4, or LUKS encryption)
  2. Supports both BIOS and UEFI systems
  3. Detects and installs appropriate graphics drivers
  4. Sets up GRUB bootloader with a custom theme
  5. Configures user accounts and sudo access
  6. Handles different partition schemes based on disk selection

I'm particularly interested in feedback on:

  • The overall structure and flow of the script
  • Handling of different filesystem options, especially LUKS encryption
  • The approach to setting up the secondary HDD for user directories
  • Any potential security issues or best practices I might have missed
  • Ways to make the script more robust and error-resistant

Snippets:

...
select_option() {
 local options=("$@")
 local num_options=${#options[@]}
 local selected=0
 local last_selected=-1
 while true; do
 if [ "$last_selected" -ne -1 ]; then
 printf "033円[%sA" "$num_options"
 fi
 if [ "$last_selected" -eq -1 ]; then
 printf "%b\n" "Please select an option using the arrow keys and Enter:"
 fi
 for i in "${!options[@]}"; do
 if [ "$i" -eq "$selected" ]; then
 printf "> %s\n" "${options[$i]}"
 else
 printf " %s\n" "${options[$i]}"
 fi
 done
 last_selected=$selected
 read -r -n1 key
 case $key in
 A) # Up arrow
 selected=$((selected - 1))
 if [ "$selected" -lt 0 ]; then
 selected=$((num_options - 1))
 fi
 ;;
 B) # Down arrow
 selected=$((selected + 1))
 if [ "$selected" -ge "$num_options" ]; then
 selected=0
 fi
 ;;
 '') # Enter key
 break
 ;;
 esac
 done
 return "$selected"
}
...
...
filesystem() {
 printf "%b\n" "
 Please Select your file system for both boot and root
 "
 options=("btrfs" "ext4" "luks" "exit")
 select_option "${options[@]}"
 case $? in
 0) FS=btrfs;;
 1) FS=ext4;;
 2) 
 set_password "LUKS_PASSWORD"
 FS=luks
 ;;
 3) exit ;;
 *) printf "%b\n" "Wrong option please select again"; filesystem;;
 esac
 if [ -n "$SECONDARY_DISK" ]; then
 printf "%b\n" "
 Formatting secondary disk with the same filesystem...
 "
 case $FS in
 btrfs) mkfs.btrfs -L DATA "$SECONDARY_DISK" -f ;;
 ext4) mkfs.ext4 -L DATA "$SECONDARY_DISK" ;;
 luks) 
 printf "%s" "$LUKS_PASSWORD" | cryptsetup -y -v luksFormat "$SECONDARY_DISK" -
 printf "%s" "$LUKS_PASSWORD" | cryptsetup open "$SECONDARY_DISK" DATA -
 mkfs.btrfs -L DATA /dev/mapper/DATA
 ;;
 esac
 fi
}
createsubvolumes() {
 btrfs subvolume create /mnt/@
 btrfs subvolume create /mnt/@home
 btrfs subvolume create /mnt/@var
 btrfs subvolume create /mnt/@tmp
 btrfs subvolume create /mnt/@.snapshots
}
mountallsubvol() {
 mount -o "$MOUNT_OPTIONS",subvol=@home "$partition3" /mnt/home
 mount -o "$MOUNT_OPTIONS",subvol=@tmp "$partition3" /mnt/tmp
 mount -o "$MOUNT_OPTIONS",subvol=@var "$partition3" /mnt/var
 mount -o "$MOUNT_OPTIONS",[email protected] "$partition3" /mnt/.snapshots
 if [ -n "$SECONDARY_DISK" ]; then
 mkdir -p /mnt/mnt/data
 if [ "$FS" = "luks" ]; then
 mount -o "$MOUNT_OPTIONS" /dev/mapper/DATA /mnt/mnt/data
 else
 mount -o "$MOUNT_OPTIONS" "$SECONDARY_DISK" /mnt/mnt/data
 fi
 mkdir -p /mnt/mnt/data/{Downloads,Documents,Music,Pictures,Videos,Public}
 fi
}
...

Are there any improvements or considerations I should keep in mind for this approach? Any potential pitfalls or edge cases I should be aware of?

Full script link is here.

asked Oct 14, 2024 at 20:10
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

interaction vs CLI

Suppose I decided to create a host, then in the course of tinkering with it I trashed it, so now I want to rebuild from scratch in the identical way.

I worry about reproducibility. Maybe I remember exactly what I originally entered? Or went to the trouble of recording it in a paper notebook?

Consider breaking this into a pair of programs:

  1. front end interactive script which prompts and then writes a .json file
  2. CLI script which reads the JSON config and performs the indicated actions

That way I have JSON notes automatically written down for me. And I can version control them, and share them with colleagues.

modulo

Thank you for the ANSI CSI escape sequence comments, such as "up arrow".

 selected=$((selected - 1))
 if [ "$selected" -lt 0 ]; then
 selected=$((num_options - 1))
 fi

Clearly this works. But prefer to use bash's % modulo operator.

secrets

 set_password "LUKS_PASSWORD"

Secrets don't belong in source code, which after all will wind up in a git repo.

Prefer to read credential material from a JSON config file, an env var, or even a command-line argument.

mkfs

We see "Formatting secondary disk" and then it happens. Very CLI style, no y-or-n "are you sure?" prompt, that's cool.

Maybe sleep 3 so we have a chance to curse and hit CTRL/C ?

Maybe probe the hardware to validate that partition in some way?

Maybe attempt to mount it, and if successful

  • don't nuke it, or
  • verify someone performed touch PLEASE-DESTROY.txt in that FS ?

I don't know what the appropriate level of paranoia is. I'm just making suggestions that it might be slightly higher than the current level of zero.

 printf "%s" "$LUKS_PASSWORD" | cryptsetup ...
 printf "%s" "$LUKS_PASSWORD" | cryptsetup ...

Hmmm, looks like I failed to correctly interpret what that set_password line was about. Oh, well!

mountpoint

 mkdir -p /mnt/mnt/data

I'm sure that's correct. I'm just surprised. Perhaps it merits an explanatory # comment.

lint

In someplace like https://github.com/fam007e/fun007/tree/master/ArchInstallScript you might consider adding a Makefile with a recipe which will e.g. run shellcheck in response to make lint. (And/or create a GitHub Action to do that, not unlike how you attest build provenance.)

automated tests

This seems like a high stakes script which a given individual might seldom run. The sort of thing that keeps you on the edge of your seat.

There is a very nice beginning of a README file; thank you.

It could benefit from some further details on "preparing storage", even if that is just an example transcript from a successful run. British highway signs go to some trouble to announce, "approaching city X", "a few miles to X", then you hit the roundabout, then a sign reassures you, "really close to X now!". Users like to know what to expect from a "dangerous" script, and like to be able to verify "what is normal output?" after running it.

VM

Here is the biggest gap I noticed. Suppose I have a filesystem with a ton of disk space, which I don't plan to partition. Making a Docker image with one FS or another seems like a natural use case. Consider writing up an example of using this script in a containerized setting.

answered Oct 15, 2024 at 1:45
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.