#!/bin/sh # Setup an archlinux system to a media [ "$USER" = root ] || exec sudo "$0" "$@" version='yoda-0.3' help() { echo 'usage: yoda [-CV] [-u user] dev Install arch to a bootable dev. Media dev is entirely erased then provisionned with an EFI boot partition and a root partition. The following steps are performed: 0: prepare device (erase/randomize). Long. 1: partition a 256MB GPT boot and a root partition (the rest) 2: encrypt root partition (dm-crypt + LUKS) 3: format partitions 4: mount partitions 5: install phase 1 (pacstrap) 6: install phase 2 (chroot actions) options: -C crypt root partition with dm-crypt+LUKS -S steps skips some steps (i.e. -S 02 to avoid crypt) -u user create user login and transfer ssh credentials -V print version ' } die() { echo "$0: fatal: $@" >&2; exit 1; } # return 1 in any of arg is to be skipped, 0 otherwise skip() { for i; do case $toskip in (*$i*) return 0;; esac done return 1 } # return 1 if any of the required commands is missing prereq() { ok='ok' for cmd in cryptsetup dd fdisk mkfs.fat mkfs.ext4 mkfs.xfs mount pacstrap genfstab arch-chroot do command -v "$cmd" >/dev/null || { ok=''; echo "$cmd not found"; } done test "$ok" } # Step 0: Prepare by wiping all data and randomize (very long). step0() { cryptsetup open --type plain -d /dev/urandom "$dev" to_be_wiped dd if=/dev/zero of=/dev/mapper/to_be_wiped bs=1M status=progress || true cryptsetup close to_be_wiped } # Step 1: Partitions: GPT boot EFI 256 MB, encrypted root: all the rest. step1() { fdisk "$dev" << \EOT g n +256M t 1 n p w EOT } # Step 2: Encrypt root partition. step2() { rootpart="/dev/mapper/$par" cryptsetup -y -v luksFormat ${dev}${ppref}2 cryptsetup open ${dev}${ppref}2 $par } # Step 3: Format partitions. step3() { mkfs.fat -F32 "${dev}${ppref}1" mkfs.xfs "$rootpart" } # Step 4: Mount partitions. step4() { mount "$rootpart" /mnt mkdir /mnt/boot mount ${dev}${ppref}1 /mnt/boot trap cleanup EXIT } # Step 5: install some packages. step5() { #pacstrap /mnt base linux linux-firmware vi wireless_tools wpa_supplicant pacstrap /mnt base mkinitcpio genfstab -U /mnt > /mnt/etc/fstab } # Step 6: further configs step6() { # Remove root password. sed -i -r 's/^root:[^:]+:/root::/' /mnt/etc/shadow # Configure initramfs skip 2 && hooks="base udev autodetect modconf block filesystems keyboard fsck" || hooks="base systemd keyboard sd-vconsole modconf block autodetect sd-encrypt filesystems fsck" cat > /mnt/etc/mkinitcpio.conf << EOT MODULES=() BINARIES=() FILES=() HOOKS=($hooks) EOT cat > /mnt/etc/mkinitcpio.d/linux.preset << \EOT # mkinitcpio preset file for the 'linux' package ALL_config="/etc/mkinitcpio.conf" ALL_kver="/boot/vmlinuz-linux" PRESETS=('default') #default_config="/etc/mkinitcpio.conf" default_image="/boot/initramfs-linux.img" #default_options="" EOT cat > /mnt/etc/hosts << \EOT 127.0.0.1 localhost ::1 localhost 127.0.1.1 yoda.localdomain yoda EOT cat > /mnt/etc/locale.gen << \EOT en_US.UTF-8 UTF-8 en_GB.UTF-8 UTF-8 fr_FR.UTF-8 UTF-8 EOT echo yoda > /mnt/etc/hostname echo 'KEYMAP=fr-latin1' > /mnt/etc/vconsole.conf echo 'LANG=en_GB.UTF-8' > /mnt/etc/locale.conf ln -sf /usr/share/zoneinfo/Europe/Paris /mnt/etc/localtime cat > /mnt/etc/systemd/network/20-wired.network << \EOT [Match] Name=e* [Network] DHCP=ipv4 [DHCP] RouteMetric=10 EOT cat > /mnt/etc/systemd/network/25-wireless.network << \EOT [Match] Name=wl* [Network] DHCP=ipv4 [DHCP] RouteMetric=20 EOT cat > /mnt/etc/systemd/resolved.conf << \EOT [Resolve] DNSSEC=no DNSOverTLS=no EOT packages="linux linux-firmware intel-ucode amd-ucode sof-firmware \ xfsprogs dosfstools sudo vi wireless_tools wpa_supplicant openssh arch-install-scripts" arch-chroot /mnt << EOT locale-gen pacman --noconfirm -S $packages systemctl enable systemd-networkd systemctl enable systemd-resolved systemctl enable wpa_supplicant@wlan0 systemctl enable sshd bootctl --path=/boot install EOT cp "$myself" /mnt/usr/bin/yoda if [ "$user" ]; then useradd -R /mnt -m "$user" cp -a "/home/$user/.ssh" "/mnt/home/$user/.ssh" echo "$user ALL=(ALL) ALL" > "/mnt/etc/sudoers.d/01_$user" fi mkdir -p /mnt/etc/wpa_supplicant cat /etc/wpa_supplicant/wpa_supplicant*.conf > /mnt/etc/wpa_supplicant/wpa_supplicant-wlan0.conf cat > /mnt/boot/loader/loader.conf << \EOT default arch timeout 4 console-mode max EOT if skip 2; then uid=$(blkid -p -s UUID -o value "${dev}${ppref}2") rdopt="root=/dev/disk/by-uuid/$uid" else uid=$(blkid -p -s UUID -o value "${dev}${ppref}2") rdopt="rd.luks.name=$uid=root root=/dev/mapper/root" fi cat > /mnt/boot/loader/entries/arch.conf << EOT title Yoda Arch Linux linux /vmlinuz-linux initrd /intel-ucode.img initrd /amd-ucode.img initrd /initramfs-linux.img options $rdopt net.ifnames=0 rw EOT } cleanup() { echo "Flushing and unmounting $dev" umount /mnt/boot /mnt cryptsetup close "$par" echo "$dev is ready" } # Main toskip='02' while getopts :CFH:S:u:V opt; do case $opt in (C) toskip=$(echo "$toskip" | sed 's/[02]//g') ;; (S) toskip="$toskip$OPTARG" ;; (u) user=$OPTARG;; (V) echo $version && exit ;; (*) help; exit 1 ;; esac done shift $((OPTIND - 1)) [ "$1" ] && dev=$1 || die 'no device' par=${dev##*/}root case $dev in (*nvme*) ppref='p';; esac rootpart="${dev}${ppref}2" myself=$(type -p $0) prereq || exit 1 echo -n "Device $dev will be completely erased. Continue ? Y/N " read -r resp && [ "$resp" = 'Y' ] || exit skip 0 || step0 skip 1 || step1 skip 2 || step2 skip 3 || step3 skip 4 || step4 skip 5 || step5 skip 6 || step6 # vim: ts=2 sw=2 et