diff --git a/Misc/Arch-LuksCrypt-Install.bash b/Misc/Arch-LuksCrypt-Install.bash new file mode 100644 index 0000000..11dd14d --- /dev/null +++ b/Misc/Arch-LuksCrypt-Install.bash @@ -0,0 +1,375 @@ +#!/bin/bash + +set -eo pipefail + +confirmation() { + local message + message="${1}" + + local choice + + while :; do + printf "%s (Y/n) " "${message}" + read -r choice + case "${choice}" in + y | Y) + return 0 + ;; + n | N) + return 1 + ;; + *) printf "\nInput must be either y, Y, n, or N" ;; + esac + done +} + +print-break() { + local green= + green="$(tput setaf 2)" + local reset + reset="$(tput sgr0)" + printf "${green}%.s─${reset}" $(seq 1 "$(tput cols)") +} + +print-title() { + local title + title="${*}" + local bold + bold="$(tput bold)" + local underline + underline="$(tput smul)" + + local cyan + cyan="$(tput setaf 6)" + local reset + reset="$(tput sgr0)" + + printf "%s%s%s%s%s\n" "${bold}" "${underline}" "${cyan}" "${title}" "${reset}" >&2 +} + +echo-rgb() { + # Echo a colored string to the terminal based on rgb values + # + # Positional Arguments: + # + # message + # - The message to be printed to stdout + # red + # - The red value from 0 to 255 + # green + # - The green value from 0 to 255 + # blue + # - The blue value from 0 to 255 + # + # Usage: + # echo-rgb "Yep" 10 8 30 + # + # POSIX Compliant: + # N/A + # + + local red + local green + local blue + local input + + input="${1}" + red="${2}" + green="${3}" + blue="${4}" + + printf "\e[0;38;2;%s;%s;%sm%s\e[m\n" "${red}" "${green}" "${blue}" "${input}" +} + +important() { + echo-rgb "${1}" 135 195 255 +} + +log() { + # Print a message and send it to stdout or stderr depending upon log level, also configurable with debug etc. + # + # Arguments: + # level + # - The log level, defined within a case check in this function + # message + # - The info message + # line_number + # - The line number of the calling function (${LINNO}) + # + # Usage: + # log "info" "Could not find that directory" + # + # POSIX Compliant: + # Yes + # + + # Set debug status depending if a global debug variable has been set to either 1 or 0 + local debug + if [ ${DEBUG} ]; then + debug=${DEBUG} + else + debug=0 + fi + + local FORMAT + FORMAT="[$(echo-rgb "$(date +%Y-%m-%dT%H:%M:%S)" 180 140 255)]" + + # Convert the level to uppercase + local level + level=$(echo "${1}" | tr '[:lower:]' '[:upper:]') + + local message + message="${2}" + + case "${level}" in + INFO) + # Output all info log levels to stdout + printf "${FORMAT}[$(echo-rgb "INFO" 0 140 255)] %s\n" "${message}" >&2 + return 0 + ;; + WARN | WARNING) + # Output all warning log levels to stdout + printf "${FORMAT}[$(echo-rgb "WARNING" 255 255 0)] %s\n" "${message}" >&2 + return 0 + ;; + DEBUG) + # Output all debug log levels to stdout + if [ "${DEBUG}" ]; then + printf "${FORMAT}[$(echo-rgb "DEBUG" 0 160 110)] %s\n" "${message}" >&2 + fi + return 0 + ;; + ERROR) + # Output all error log levels to stderr + printf "${FORMAT}[$(echo-rgb "ERROR" 255 0 0)] %s\n" "${message}" >&2 + return 0 + ;; + # Further log levels can be added by extending this switch statement with more comparisons + + *) # Default case, no matches + # Returns non-zero code as an improper log option was passed, this helps with using `set -e` + printf "${FORMAT}[ERROR] %s\n" "Invalid log level passed, received level \"${level}\" with message \"${message}\"" >&2 + return 1 + ;; + esac +} + +get-available-disks() { + local available_disks=() + + local disk_line + local disk + while read -r; do + # tr is used to change multiple whitespaces into a single space for processing by cut + disk_line="$(printf "%s" "${REPLY}" | tr -s " ")" + disk=$(printf "%s" "${disk_line}" | cut -d " " -f2) + if [[ "${disk}" = "disk" ]]; then + available_disks+=("$(printf "/dev/%s" "${disk_line}" | cut -d " " -f1)") + fi + done <<<"$(lsblk --output NAME,TYPE)" + + printf "%s" "${available_disks[@]}" +} + +get-disk-partitions() { + local disk="${1}" + + if [[ -z "${disk}" ]]; then + log "error" "No disk passed!" + return 1 + fi + + if ! blkid | grep "${disk}" >/dev/null; then + return 1 + fi + local partitions + IFS=$'\n' partitions=("$(blkid -o device | grep "${disk}")") + printf "%s" "${partitions[@]}" +} + +check-connectivity() { + if ! host "google.com" >/dev/null; then + log "error" "Unable to reach google, network is down. This script requires network connectivity with active DNS resolution" + return 1 + fi +} + +select-item() { + local item_name="${1}" + shift + local items + # shellcheck disable=2206 + items=($@) + local item_selected + while :; do + local count=1 + for item in "${items[@]}"; do + printf "%s %s\n" "${count}.)" "$(important "${item}")" >&2 + count=$((count + 1)) + done + count=1 + + read -rp "Select a ${item_name} number: " item_selected + case "${item_selected}" in + '' | *[!0-9]*) + log "error" "A number was not passed" + continue + ;; + esac + + for item in "${items[@]}"; do + # printf -- "--> %s | %s\n" "${item_selected}" "${count}" + if [[ ${count} -eq ${item_selected} ]]; then + printf "%s" "${item}" + return 0 + fi + count="$((count + 1))" + done + log "error" "Invalid selection made, received: \"${item_selected}\"" + done +} + +partition-disk() { + local disk="${1}" + ( + echo n + echo "${BOOT_PARTITION_NUMBER}" + echo + echo +512M + echo ef00 + echo n + echo "${ROOT_PARTITION_NUMBER}" + echo + echo + echo 8300 + echo w + echo Y + ) | gdisk "${disk}" >/dev/null + mkfs.vfat -F32 -n EFI "${disk}"*"${BOOT_PARTITION_NUMBER}" +} + +main() { + local ARCH_TIMEZONE="CST" + local LOCALE="en_US.UTF-8 UTF-8" + local LANG="en_US.UTF-8" + local DISK_PARTITION_LETTER="" + local HOSTNAME="Arch-Linux" + local MICROCODE="amd" + + clear + if ! check-connectivity; then + exit 1 + fi + + local selected_disk + local i_selected_disk + while :; do + print-title "Select the installation disk" + selected_disk="$(select-item "disk" "$(get-available-disks)")" + i_selected_disk="$(important "${selected_disk}")" + if confirmation "Is ${i_selected_disk} the correct disk to install to?"; then + break + fi + done + printf "Disk set as %s\n" "${i_selected_disk}" >&2 + print-break + + print-title "Partition Creation" + export BOOT_PARTITION_NUMBER=120 + export ROOT_PARTITION_NUMBER=121 + if ! confirmation "Begin installation of Arch Linux to ${i_selected_disk}?"; then + printf "Install request denied, exiting..." >&2 + exit 0 + fi + printf "Writing new partitions to %s\n" "${i_selected_disk}" >&2 + partition-disk "${selected_disk}" + printf "Successfully wrote partitions to %s\n" "${i_selected_disk}" >&2 + local boot_partion="${selected_disk}${DISK_PARTITION_LETTER}${BOOT_PARTITION_NUMBER}" + local root_partition="${selected_disk}${DISK_PARTITION_LETTER}${ROOT_PARTITION_NUMBER}" + print-break + + print-title "Partition Encryption" + printf "Encrypting %s\n" "$(important "${root_partition}")" >&2 + cryptsetup -y -v luksFormat "${root_partition}" || exit + printf "Opening encrypted device %s\n" \ + "${i_selected_disk}" >&2 + cryptsetup open "${root_partition}" cryptroot || exit + mkfs.fat -F 32 "${boot_partion}" + mkfs.ext4 "/dev/mapper/cryptroot" + print-break + + print-title "Configure Swap" + local mem_amount_kb + mem_amount_kb="$(grep MemTotal /proc/meminfo | tr -s " " | cut -d " " -f2)" + mem_amount_mb=$((mem_amount_kb / 1024)) + # Space to swap is taken from fedora guidelines: + # https://docs.fedoraproject.org/en-US/Fedora/22/html/Installation_Guide/sect-installation-gui-manual-partitioning-recommended.html + if ((mem_amount_mb < 2048)); then + mem_amount_kb=$((mem_amount_kb * 3 / 1024)) + elif ((mem_amount_mb < 8192)); then + mem_amount_kb=$((mem_amount_kb * 2 / 1024)) + elif ((mem_amount_mb < 65536)); then + mem_amount_kb=$((mem_amount_kb * 3 / 2 / 1024)) + fi + printf "Creating swap file with memory amount: %s mebibytes\n" "$(important "${mem_amount_kb}")" + dd if=/dev/zero of=/mnt/swapfile bs=1M count="${mem_amount_kb}" status=progress + chmod 600 /mnt/swapfile + mkswap /mnt/swapfile + swapon /mnt/swapfile + printf "Finished creating swapfile\n" >&2 + + print-title "Install Arch Into Encrypted Volume" + printf "Mounting Needed Directores\n" >&2 + mount /dev/mapper/cryptroot /mnt + mkdir /mnt/boot + mount "${boot_partion}" /mnt/boot + printf "Doing Pacstrap\n" >&2 + pacstrap /mnt linux linux-firmware base base-devel grub efibootmgr vim git "${MICROCODE}"-ucode networkmanager + genfstab -U /mnt >>/mnt/etc/fstab + print-break + + print-title "Configure System" + printf "Entering Chroot\n" >&2 + arch-chroot /mnt <<-__END_CHROOT_CMDS__ + printf "Setting default password as %s\n" "$(important "toor")" + printf "toor\ntoor\n" | passwd + + printf "Linking timezone as %s\n" "$(important "${ARCH_TIMEZONE}")" >&2 + ln -sf "/usr/share/zoneinfo/${ARCH_TIMEZONE}" "/etc/localtime" + + printf "Setting Hardware Clock\n" >&2 + hwclock --systohc + + printf "Setting LOCALE to %s\n" "$(important "${LOCALE}")" >&2 + printf "%s\n" "${LOCALE}" >/etc/locale.gen + + printf "Generting Locale\n" >&2 + locale-gen + + printf "Setting LANG to %s\n" "$(important "${LANG}")" >&2 + printf "%s\n" "${LANG}" >>/etc/locale.conf + + printf "Setting HOSTNAME to %s\n" "$(important "${HOSTNAME}")" + printf "%s\n" "${HOSTNAME}" >>/etc/hostname + + printf "Set mkinitcipio hooks\n" >&2 + sed -i 's/HOOKS=.*/HOOKS=(base udev autodetect keyboard modconf block encrypt filesystems fsck)/' /etc/mkinitcpio.conf + mkinitcpio -P + + printf "Configure Grub\n" >&2 + yes | pacman -S grub efibootmgr intel-ucode amd-ucode + sed -i + "s/GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=$(blkid -s UUID -o value "${root_partition}"):cryptroot\"/" /etc/default/grub + grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB + grub-mkconfig -o /boot/grub/grub.cfg + + printf "Setting up networkmanager\n" >&2 + yes | pacman -S networkmanager + systemctl enable NetworkManager + __END_CHROOT_CMDS__ + print-break + printf "%s\n\tUsername: root\n\tPassword: toor" "$(important "Finished with installation, login is:")" + +} + +main