forked from classy-giraffe/easy-arch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
easy-arch.sh
executable file
·489 lines (426 loc) · 17.2 KB
/
easy-arch.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
#!/usr/bin/env -S bash -e
# Fixing annoying issue that breaks GitHub Actions
# shellcheck disable=SC2001
# Cleaning the TTY.
clear
# Cosmetics (colours for text).
BOLD='\e[1m'
BRED='\e[91m'
BBLUE='\e[34m'
BGREEN='\e[92m'
BYELLOW='\e[93m'
RESET='\e[0m'
# Pretty print (function).
info_print () {
echo -e "${BOLD}${BGREEN}[ ${BYELLOW}•${BGREEN} ] $1${RESET}"
}
# Pretty print for input (function).
input_print () {
echo -ne "${BOLD}${BYELLOW}[ ${BGREEN}•${BYELLOW} ] $1${RESET}"
}
# Alert user of bad input (function).
error_print () {
echo -e "${BOLD}${BRED}[ ${BBLUE}•${BRED} ] $1${RESET}"
}
# Virtualization check (function).
virt_check () {
hypervisor=$(systemd-detect-virt)
case $hypervisor in
kvm ) info_print "KVM has been detected, setting up guest tools."
pacstrap /mnt qemu-guest-agent &>/dev/null
systemctl enable qemu-guest-agent --root=/mnt &>/dev/null
;;
vmware ) info_print "VMWare Workstation/ESXi has been detected, setting up guest tools."
pacstrap /mnt open-vm-tools >/dev/null
systemctl enable vmtoolsd --root=/mnt &>/dev/null
systemctl enable vmware-vmblock-fuse --root=/mnt &>/dev/null
;;
oracle ) info_print "VirtualBox has been detected, setting up guest tools."
pacstrap /mnt virtualbox-guest-utils &>/dev/null
systemctl enable vboxservice --root=/mnt &>/dev/null
;;
microsoft ) info_print "Hyper-V has been detected, setting up guest tools."
pacstrap /mnt hyperv &>/dev/null
systemctl enable hv_fcopy_daemon --root=/mnt &>/dev/null
systemctl enable hv_kvp_daemon --root=/mnt &>/dev/null
systemctl enable hv_vss_daemon --root=/mnt &>/dev/null
;;
esac
}
# Selecting a kernel to install (function).
kernel_selector () {
info_print "List of kernels:"
info_print "1) Stable: Vanilla Linux kernel with a few specific Arch Linux patches applied"
info_print "2) Hardened: A security-focused Linux kernel"
info_print "3) Longterm: Long-term support (LTS) Linux kernel"
info_print "4) Zen Kernel: A Linux kernel optimized for desktop usage"
input_print "Please select the number of the corresponding kernel (e.g. 1): "
read -r kernel_choice
case $kernel_choice in
1 ) kernel="linux"
return 0;;
2 ) kernel="linux-hardened"
return 0;;
3 ) kernel="linux-lts"
return 0;;
4 ) kernel="linux-zen"
return 0;;
* ) error_print "You did not enter a valid selection, please try again."
return 1
esac
}
# Selecting a way to handle internet connection (function).
network_selector () {
info_print "Network utilities:"
info_print "1) IWD: Utility to connect to networks written by Intel (WiFi-only, built-in DHCP client)"
info_print "2) NetworkManager: Universal network utility (both WiFi and Ethernet, highly recommended)"
info_print "3) wpa_supplicant: Utility with support for WEP and WPA/WPA2 (WiFi-only, DHCPCD will be automatically installed)"
info_print "4) dhcpcd: Basic DHCP client (Ethernet connections or VMs)"
info_print "5) I will do this on my own (only advanced users)"
input_print "Please select the number of the corresponding networking utility (e.g. 1): "
read -r network_choice
if ! ((1 <= network_choice <= 5)); then
error_print "You did not enter a valid selection, please try again."
return 1
fi
return 0
}
# Installing the chosen networking method to the system (function).
network_installer () {
case $network_choice in
1 ) info_print "Installing and enabling IWD."
pacstrap /mnt iwd >/dev/null
systemctl enable iwd --root=/mnt &>/dev/null
;;
2 ) info_print "Installing and enabling NetworkManager."
pacstrap /mnt networkmanager >/dev/null
systemctl enable NetworkManager --root=/mnt &>/dev/null
;;
3 ) info_print "Installing and enabling wpa_supplicant and dhcpcd."
pacstrap /mnt wpa_supplicant dhcpcd >/dev/null
systemctl enable wpa_supplicant --root=/mnt &>/dev/null
systemctl enable dhcpcd --root=/mnt &>/dev/null
;;
4 ) info_print "Installing dhcpcd."
pacstrap /mnt dhcpcd >/dev/null
systemctl enable dhcpcd --root=/mnt &>/dev/null
esac
}
# User enters a password for the LUKS Container (function).
lukspass_selector () {
input_print "Please enter a password for the LUKS container (you're not going to see the password): "
read -r -s password
if [[ -z "$password" ]]; then
echo
error_print "You need to enter a password for the LUKS Container, please try again."
return 1
fi
echo
input_print "Please enter the password for the LUKS container again (you're not going to see the password): "
read -r -s password2
echo
if [[ "$password" != "$password2" ]]; then
error_print "Passwords don't match, please try again."
return 1
fi
return 0
}
# Setting up a password for the user account (function).
userpass_selector () {
input_print "Please enter name for a user account (enter empty to not create one): "
read -r username
if [[ -z "$username" ]]; then
return 0
fi
input_print "Please enter a password for $username (you're not going to see the password): "
read -r -s userpass
if [[ -z "$userpass" ]]; then
echo
error_print "You need to enter a password for $username, please try again."
return 1
fi
echo
input_print "Please enter the password again (you're not going to see it): "
read -r -s userpass2
echo
if [[ "$userpass" != "$userpass2" ]]; then
echo
error_print "Passwords don't match, please try again."
return 1
fi
return 0
}
# Setting up a password for the root account (function).
rootpass_selector () {
input_print "Please enter a password for the root user (you're not going to see it): "
read -r -s rootpass
if [[ -z "$rootpass" ]]; then
echo
error_print "You need to enter a password for the root user, please try again."
return 1
fi
echo
input_print "Please enter the password again (you're not going to see it): "
read -r -s rootpass2
echo
if [[ "$rootpass" != "$rootpass2" ]]; then
error_print "Passwords don't match, please try again."
return 1
fi
return 0
}
# Microcode detector (function).
microcode_detector () {
CPU=$(grep vendor_id /proc/cpuinfo)
if [[ "$CPU" == *"AuthenticAMD"* ]]; then
info_print "An AMD CPU has been detected, the AMD microcode will be installed."
microcode="amd-ucode"
else
info_print "An Intel CPU has been detected, the Intel microcode will be installed."
microcode="intel-ucode"
fi
}
# User enters a hostname (function).
hostname_selector () {
input_print "Please enter the hostname: "
read -r hostname
if [[ -z "$hostname" ]]; then
error_print "You need to enter a hostname in order to continue."
return 1
fi
return 0
}
# User chooses the locale (function).
locale_selector () {
input_print "Please insert the locale you use (format: xx_XX. Enter empty to use en_US, or \"/\" to search locales): " locale
read -r locale
case "$locale" in
'') locale="en_US.UTF-8"
info_print "$locale will be the default locale."
return 0;;
'/') sed -E '/^# +|^#$/d;s/^#| *$//g;s/ .*/ (Charset:&)/' /etc/locale.gen | less -M
clear
return 1;;
*) if ! grep -q "^#\?$(sed 's/[].*[]/\\&/g' <<< "$locale") " /etc/locale.gen; then
error_print "The specified locale doesn't exist or isn't supported."
return 1
fi
return 0
esac
}
# User chooses the console keyboard layout (function).
keyboard_selector () {
input_print "Please insert the keyboard layout to use in console (enter empty to use US, or \"/\" to look up for keyboard layouts): "
read -r kblayout
case "$kblayout" in
'') kblayout="us"
info_print "The standard US keyboard layout will be used."
return 0;;
'/') localectl list-keymaps
clear
return 1;;
*) if ! localectl list-keymaps | grep -Fxq "$kblayout"; then
error_print "The specified keymap doesn't exist."
return 1
fi
info_print "Changing console layout to $kblayout."
loadkeys "$kblayout"
return 0
esac
}
# Welcome screen.
echo -ne "${BOLD}${BYELLOW}
======================================================================
███████╗ █████╗ ███████╗██╗ ██╗ █████╗ ██████╗ ██████╗██╗ ██╗
██╔════╝██╔══██╗██╔════╝╚██╗ ██╔╝ ██╔══██╗██╔══██╗██╔════╝██║ ██║
█████╗ ███████║███████╗ ╚████╔╝█████╗███████║██████╔╝██║ ███████║
██╔══╝ ██╔══██║╚════██║ ╚██╔╝ ╚════╝██╔══██║██╔══██╗██║ ██╔══██║
███████╗██║ ██║███████║ ██║ ██║ ██║██║ ██║╚██████╗██║ ██║
╚══════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
======================================================================
${RESET}"
info_print "Welcome to easy-arch, a script made in order to simplify the process of installing Arch Linux."
# Setting up keyboard layout.
until keyboard_selector; do : ; done
# Choosing the target for the installation.
info_print "Available disks for the installation:"
PS3="Please select the number of the corresponding disk (e.g. 1): "
select ENTRY in $(lsblk -dpnoNAME|grep -P "/dev/sd|nvme|vd");
do
DISK="$ENTRY"
info_print "Arch Linux will be installed on the following disk: $DISK"
break
done
# Setting up LUKS password.
until lukspass_selector; do : ; done
# Setting up the kernel.
until kernel_selector; do : ; done
# User choses the network.
until network_selector; do : ; done
# User choses the locale.
until locale_selector; do : ; done
# User choses the hostname.
until hostname_selector; do : ; done
# User sets up the user/root passwords.
until userpass_selector; do : ; done
until rootpass_selector; do : ; done
# Warn user about deletion of old partition scheme.
input_print "This will delete the current partition table on $DISK once installation starts. Do you agree [y/N]?: "
read -r disk_response
if ! [[ "${disk_response,,}" =~ ^(yes|y)$ ]]; then
error_print "Quitting."
exit
fi
info_print "Wiping $DISK."
wipefs -af "$DISK" &>/dev/null
sgdisk -Zo "$DISK" &>/dev/null
# Creating a new partition scheme.
info_print "Creating the partitions on $DISK."
parted -s "$DISK" \
mklabel gpt \
mkpart ESP fat32 1MiB 1025MiB \
set 1 esp on \
mkpart CRYPTROOT 1025MiB 100% \
ESP="/dev/disk/by-partlabel/ESP"
CRYPTROOT="/dev/disk/by-partlabel/CRYPTROOT"
# Informing the Kernel of the changes.
info_print "Informing the Kernel about the disk changes."
partprobe "$DISK"
# Formatting the ESP as FAT32.
info_print "Formatting the EFI Partition as FAT32."
mkfs.fat -F 32 "$ESP" &>/dev/null
# Creating a LUKS Container for the root partition.
info_print "Creating LUKS Container for the root partition."
echo -n "$password" | cryptsetup luksFormat "$CRYPTROOT" -d - &>/dev/null
echo -n "$password" | cryptsetup open "$CRYPTROOT" cryptroot -d -
BTRFS="/dev/mapper/cryptroot"
# Formatting the LUKS Container as BTRFS.
info_print "Formatting the LUKS container as BTRFS."
mkfs.btrfs "$BTRFS" &>/dev/null
mount "$BTRFS" /mnt
# Creating BTRFS subvolumes.
info_print "Creating BTRFS subvolumes."
subvols=(snapshots var_pkgs var_log home root srv)
for subvol in '' "${subvols[@]}"; do
btrfs su cr /mnt/@"$subvol" &>/dev/null
done
# Mounting the newly created subvolumes.
umount /mnt
info_print "Mounting the newly created subvolumes."
mountopts="ssd,noatime,compress-force=zstd:3,discard=async"
mount -o "$mountopts",subvol=@ "$BTRFS" /mnt
mkdir -p /mnt/{home,root,srv,.snapshots,var/{log,cache/pacman/pkg},boot}
for subvol in "${subvols[@]:2}"; do
mount -o "$mountopts",subvol=@"$subvol" "$BTRFS" /mnt/"${subvol//_//}"
done
chmod 750 /mnt/root
mount -o "$mountopts",subvol=@snapshots "$BTRFS" /mnt/.snapshots
mount -o "$mountopts",subvol=@var_pkgs "$BTRFS" /mnt/var/cache/pacman/pkg
chattr +C /mnt/var/log
mount "$ESP" /mnt/boot/
# Checking the microcode to install.
microcode_detector
# Pacstrap (setting up a base sytem onto the new root).
info_print "Installing the base system (it may take a while)."
pacstrap -K /mnt base "$kernel" "$microcode" linux-firmware "$kernel"-headers sbctl btrfs-progs grub grub-btrfs rsync efibootmgr snapper reflector snap-pac zram-generator sudo &>/dev/null
# Setting up the hostname.
echo "$hostname" > /mnt/etc/hostname
# Generating /etc/fstab.
info_print "Generating a new fstab."
genfstab -U /mnt >> /mnt/etc/fstab
# Configure selected locale and console keymap
sed -i "/^#$locale/s/^#//" /mnt/etc/locale.gen
echo "LANG=$locale" > /mnt/etc/locale.conf
echo "KEYMAP=$kblayout" > /mnt/etc/vconsole.conf
# Setting hosts file.
info_print "Setting hosts file."
cat > /mnt/etc/hosts <<EOF
127.0.0.1 localhost
::1 localhost
127.0.1.1 $hostname.localdomain $hostname
EOF
# Virtualization check.
virt_check
# Setting up the network.
network_installer
# Configuring /etc/mkinitcpio.conf.
info_print "Configuring /etc/mkinitcpio.conf."
cat > /mnt/etc/mkinitcpio.conf <<EOF
HOOKS=(systemd autodetect keyboard sd-vconsole modconf block sd-encrypt filesystems)
EOF
# Setting up LUKS2 encryption in grub.
info_print "Setting up grub config."
UUID=$(blkid -s UUID -o value $CRYPTROOT)
sed -i "\,^GRUB_CMDLINE_LINUX=\"\",s,\",&rd.luks.name=$UUID=cryptroot root=$BTRFS," /mnt/etc/default/grub
# Configuring the system.
info_print "Configuring the system (timezone, system clock, initramfs, Snapper, GRUB)."
arch-chroot /mnt /bin/bash -e <<EOF
# Setting up timezone.
ln -sf /usr/share/zoneinfo/$(curl -s http://ip-api.com/line?fields=timezone) /etc/localtime &>/dev/null
# Setting up clock.
hwclock --systohc
# Generating locales.
locale-gen &>/dev/null
# Create SecureBoot keys.
# This isn't strictly necessary, linux-hardened preset expects it and mkinitcpio will fail without it
sbctl create-keys
# Generating a new initramfs.
mkinitcpio -P &>/dev/null
# Snapper configuration.
umount /.snapshots
rm -r /.snapshots
snapper --no-dbus -c root create-config /
btrfs subvolume delete /.snapshots &>/dev/null
mkdir /.snapshots
mount -a &>/dev/null
chmod 750 /.snapshots
# Installing GRUB.
grub-install --target=x86_64-efi --efi-directory=/boot/ --bootloader-id=GRUB &>/dev/null
# Creating grub config file.
grub-mkconfig -o /boot/grub/grub.cfg &>/dev/null
EOF
# Setting root password.
info_print "Setting root password."
echo "root:$rootpass" | arch-chroot /mnt chpasswd
# Setting user password.
if [[ -n "$username" ]]; then
echo "%wheel ALL=(ALL:ALL) ALL" > /mnt/etc/sudoers.d/wheel
info_print "Adding the user $username to the system with root privilege."
arch-chroot /mnt useradd -m -G wheel -s /bin/bash "$username"
info_print "Setting user password for $username."
echo "$username:$userpass" | arch-chroot /mnt chpasswd
fi
# Boot backup hook.
info_print "Configuring /boot backup when pacman transactions are made."
mkdir /mnt/etc/pacman.d/hooks
cat > /mnt/etc/pacman.d/hooks/50-bootbackup.hook <<EOF
[Trigger]
Operation = Upgrade
Operation = Install
Operation = Remove
Type = Path
Target = usr/lib/modules/*/vmlinuz
[Action]
Depends = rsync
Description = Backing up /boot...
When = PostTransaction
Exec = /usr/bin/rsync -a --delete /boot /.bootbackup
EOF
# ZRAM configuration.
info_print "Configuring ZRAM."
cat > /mnt/etc/systemd/zram-generator.conf <<EOF
[zram0]
zram-size = min(ram, 8192)
EOF
# Pacman eye-candy features.
info_print "Enabling colours, animations, and parallel downloads for pacman."
sed -Ei 's/^#(Color)$/\1\nILoveCandy/;s/^#(ParallelDownloads).*/\1 = 10/' /mnt/etc/pacman.conf
# Enabling various services.
info_print "Enabling Reflector, automatic snapshots, BTRFS scrubbing and systemd-oomd."
services=(reflector.timer snapper-timeline.timer snapper-cleanup.timer [email protected] [email protected] [email protected] btrfs-scrub@\\x2esnapshots.timer grub-btrfsd.service systemd-oomd)
for service in "${services[@]}"; do
systemctl enable "$service" --root=/mnt &>/dev/null
done
# Finishing up.
info_print "Done, you may now wish to reboot (further changes can be done by chrooting into /mnt)."
exit