#!/usr/bin/env bash set -a set -E # SSH public keys sshkeys='' ## Tput codes reset=$(tput sgr0) bold=$(tput bold) italic=$(tput sitm) blink=$(tput blink) black=${reset}$(tput setaf 0) red=${reset}$(tput setaf 1) green=${reset}$(tput setaf 2) yellow=${reset}$(tput setaf 3) blue=${reset}$(tput setaf 4) magenta=${reset}$(tput setaf 5) cyan=${reset}$(tput setaf 6) white=${reset}$(tput setaf 7) # Color codes function say { for format in ${@:2} do echo -n ${!format} done echo -e "${1}" echo -n ${reset} } function say_n { for format in ${@:2} do echo -n ${!format} done echo -e -n "${1}" echo -n ${reset} } # Exit function trap '[ "$?" -ne 77 ] || exit 77' ERR function die { cat <<- abort ${red} Error encountered for the following reason: ${yellow} "${1}" ${red} Script aborted... ${reset} abort exit 77 } # Internet connection check if nc -z -w 1 archlinux.org 443 >/dev/null 2>&1 || nc -z -w 1 google.com 443 >/dev/null 2>&1 then timedatectl set-ntp true else die 'No internet connectivity detected, plug in an ethernet cable or run \e[32miwd-connect\e[33m if using wifi and try again' fi function returnUUID { for partuuid in \ $(blkid -s UUID -o value ${1}) \ $(lsblk -d -n -o uuid ${1}) \ $(ls -l -a /dev/disk/by-uuid | grep "$(echo ${1} | sed 's\/dev\\')" | awk '{print $9}') do if [ ${partuuid} ] then echo ${partuuid} break else unset partuuid fi done if [ -z ${partuuid} ] then die 'Unable to determine partition UUID' fi } clear cat < ' kernel_choice echo if ! [[ ${kernel_choice} = [1-4] ]] then echo say 'Invalid selection, type an option from 1 to 4' red fi done case ${kernel_choice} in 1) linux_kernel='linux' ;; 2) linux_kernel='linux-lts' ;; 3) linux_kernel='linux-hardened' ;; 4) linux_kernel='linux-zen' ;; esac echo say " You have chosen ${green}${linux_kernel}${reset} as a kernel" reset echo # Unmount if mounted if findmnt '/mnt' | grep -q -w '/mnt' then umount -R /mnt fi # Shred installation drive say "Partitioning /dev/${installation_disk}" yellow bold wipefs -f -a /dev/${installation_disk} blkdiscard -f /dev/${installation_disk} if [ -d /sys/firmware/efi/efivars ] then parted --script --align=optimal /dev/${installation_disk} \ mklabel gpt \ mkpart primary fat32 1MiB 100MiB \ mkpart primary ${partition} 100MiB 100% \ set 1 esp on partprobe /dev/${installation_disk} export {efivol,bootvol}=/dev/$(lsblk -l -o name | grep "${installation_disk}".*1$) export {rootpart,rootvol}=/dev/$(lsblk -l -o name | grep "${installation_disk}".*2$) echo function bootLoader { # Systemd-boot say "Configuring systemd-boot" yellow bold bootctl --path=/boot install echo -e 'title Arch Linux linux /vmlinuz-'${linux_kernel}' initrd /initramfs-'${linux_kernel}'.img options quiet root=UUID='$(returnUUID ${rootpart})' rw' > /boot/loader/entries/arch.conf echo -e 'default arch timeout 0 console-mode max editor no' > /boot/loader/loader.conf systemdbootservice='systemd-boot-update.service' } else bios='syslinux' parted --script --align=optimal /dev/${installation_disk} \ mklabel msdos \ mkpart primary ${partition} 1MiB 100% \ set 1 boot on || die 'Disk partitioning failed' partprobe /dev/${installation_disk} export {rootpart,rootvol}=/dev/$(lsblk -l -o name | grep "${installation_disk}".*1$) echo function bootLoader { # Syslinux say "Configuring syslinux" yellow bold syslinux-install_update -i -a -m sed -i 's\LINUX ../vmlinuz-linux.*\LINUX ../vmlinuz-'${linux_kernel}'\g' /boot/syslinux/syslinux.cfg sed -i 's/APPEND root.*/APPEND root=UUID='"$(returnUUID ${rootpart})"' rw quiet/g' /boot/syslinux/syslinux.cfg sed -i 's/.*PROMPT.*/PROMPT 0/g' /boot/syslinux/syslinux.cfg sed -i 's/.*TIMEOUT.*/TIMEOUT 0/g' /boot/syslinux/syslinux.cfg sed -i 's/.*UI menu.*/#UI menu/g' /boot/syslinux/syslinux.cfg } fi say "Configuring volumes" yellow bold yes | mkfs.ext4 ${rootvol} mount ${rootvol} /mnt if [ -d /sys/firmware/efi/efivars ] then mkdir /mnt/boot yes | mkfs.fat -F 32 ${efivol} mount ${bootvol} /mnt/boot findmnt '/mnt/boot' | grep -q -w "${bootvol}" || die 'Boot partition has not been mounted properly' echo fi say "Installing Arch Linux base packages on /dev/${installation_disk}" yellow bold sed -i \ -e '/Color/c\Color' \ -e '/ParallelDownloads/c\ParallelDownloads = 10' \ -e 's#^\[core\]$\|^\[extra\]$#&\ CacheServer = http://192.168.122.1:9090#' \ /etc/pacman.conf # Temporarily disable mkinitcpio ln -s -f /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook pacstrap -K /mnt ${linux_kernel} qemu-guest-agent base mkinitcpio sudo \ ${bios} reflector openssh rsync \ wireguard-tools systemd-resolvconf \ pacman-contrib bash-completion vim genfstab -U -p /mnt >> /mnt/etc/fstab grep -q 'UUID' /mnt/etc/fstab || die # Make custom directories mkdir -p /mnt/etc/pacman.d/hooks /mnt/usr/bin/local # pacman.conf hook install /dev/stdin /mnt/usr/bin/local/hook-pacman.conf <<hook #!/usr/bin/env bash if [ -f /etc/pacman.conf.pacnew ] then sed -i \\ -e '/Color/c\Color' \\ -e '/ParallelDownloads/c\ParallelDownloads = 10' \\ -e 's#^\[core\]$\|^\[extra\]\$#&\\ CacheServer = http://192.168.122.1:9090#' /etc/pacman.conf.pacnew mv /etc/pacman.conf.pacnew /etc/pacman.conf fi hook cat >/mnt/etc/pacman.d/hooks/pacman.conf.hook <<pacman [Trigger] Operation = Install Operation = Upgrade Type = Package Target = pacman [Action] Description = Fixing pacman.conf When = PostTransaction Exec = /usr/bin/local/hook-pacman.conf pacman sed -i \ -e '/Color/c\Color' \ -e '/ParallelDownloads/c\ParallelDownloads = 10' \ -e 's#^\[core\]$\|^\[extra\]$#&\ CacheServer = http://192.168.122.1:9090#' \ /mnt/etc/pacman.conf # Manually configure mkinitcpio sed -e "s|%PKGBASE%|linux|g" \ -e "s/^fallback/#&/g" \ -e "s/ 'fallback'//" \ /mnt/usr/share/mkinitcpio/hook.preset >/mnt/etc/mkinitcpio.d/linux.preset rm /mnt/usr/share/mkinitcpio/hook.preset rsync -a /mnt/usr/lib/modules/*/vmlinuz /mnt/boot/vmlinuz-linux # mkinitcpio preset hook cat >/mnt/etc/pacman.d/hooks/linux.preset.hook <<preset [Trigger] Operation = Install Operation = Upgrade Type = Package Target = mkinitcpio [Action] Description = Updating mkinitcpio linux preset When = PostTransaction Exec = /usr/bin/local/hook-linux.preset preset install /dev/stdin /mnt/usr/bin/local/hook-linux.preset <<'hook' #!/usr/bin/env bash if [ -f /usr/share/mkinitcpio/hook.preset ] then sed \ -e "s|%PKGBASE%|linux|g" \ -e "s/^fallback/#&/g" \ -e "s/ 'fallback'//" \ /usr/share/mkinitcpio/hook.preset >/etc/mkinitcpio.d/linux.preset rm /usr/share/mkinitcpio/hook.preset fi hook arch-chroot /mnt mkinitcpio -P # Networking ln -s -f /run/systemd/resolve/stub-resolv.conf /mnt/etc/resolv.conf rsync -a /etc/systemd/network/20-ethernet.network /mnt/etc/systemd/network arch-chroot /mnt /bin/bash <<"SYU" # Install bootloader echo bootLoader echo # Locale sed -i '/#en_US.UTF-8 UTF-8/ s/#//' /etc/locale.gen locale-gen >/dev/null echo 'LANG=en_US.UTF-8' >>/etc/locale.conf say 'Locale configured' yellow bold # Time zone ln -s -f /usr/share/zoneinfo/UTC /etc/localtime hwclock --systohc --utc say 'Time zone configured' yellow bold # Hostname echo ${hostname} >/etc/hostname cat >>/etc/hosts <<HOSTS 127.0.0.1 localhost 127.0.1.1 ${hostname} HOSTS say 'Hostname configured' yellow bold # User and superuser useradd -m -g users -G wheel -s /bin/bash user || die if [ ${userpass} ] then printf '%s\n' ${userpass} ${userpass} | passwd user &>/dev/null echo '%wheel ALL=(ALL:ALL) ALL' > /etc/sudoers.d/wheel else echo -e '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/wheel mkdir -p /etc/systemd/system/getty@tty1.service.d echo '[Service] ExecStart= ExecStart=-/usr/bin/agetty --autologin user --noclear %I $TERM' > /etc/systemd/system/getty@tty1.service.d/override.conf fi if [ "$rootpass" ] then printf '%s\n' "$rootpass" "$rootpass" | passwd &>/dev/null fi say "Configured superuser and user" yellow bold echo # Sysctl custom settings echo -e 'net.core.netdev_max_backlog = 16384 net.core.somaxconn = 8192 net.core.rmem_default = 1048576 net.core.rmem_max = 16777216 net.core.wmem_default = 1048576 net.core.wmem_max = 16777216 net.core.optmem_max = 65536 net.ipv4.tcp_rmem = 4096 1048576 2097152 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.udp_rmem_min = 8192 net.ipv4.udp_wmem_min = 8192 net.ipv4.tcp_fastopen = 3 net.ipv4.tcp_timestamps = 0 net.core.default_qdisc = cake net.ipv4.tcp_congestion_control = bbr' > /etc/sysctl.d/99-sysctl.conf # iptables install /dev/stdin /usr/bin/local/iptables.hook <<IPTABLES #!/usr/bin/env bash iptables -N TCP iptables -N UDP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT iptables -P INPUT DROP iptables -A INPUT -s 192.168.122.0/24 -j ACCEPT iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m conntrack --ctstate INVALID -j DROP iptables -A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable iptables-save -f \${1} IPTABLES /usr/bin/local/iptables.hook /etc/iptables/iptables.rules # iptables hook install /dev/stdin /usr/bin/local/hook-iptables.rules <<hook #!/usr/bin/env bash if [ -f /etc/iptables/iptables.rules.pacnew ] then /usr/bin/local/iptables.hook /etc/iptables/iptables.rules.pacnew mv /etc/iptables/iptables.rules.pacnew /etc/iptables/iptables.rules fi hook cat >/etc/pacman.d/hooks/iptables.rules.hook <<iptables [Trigger] Operation = Install Operation = Upgrade Type = Package Target = iptables* [Action] Description = Fixing iptables rules When = PostTransaction Exec = /usr/bin/local/hook-iptables.rules iptables # Paccache hook cat >/etc/pacman.d/hooks/paccache.hook <<'paccache' [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Package Target = * [Action] Description = Cleaning pacman cache... When = PostTransaction Exec = /usr/bin/paccache -r paccache # locale.gen.pacnew hook install /dev/stdin /usr/bin/local/hook-localegen <<hook #!/usr/bin/env bash if [ -f /etc/locale.gen.pacnew ] then sed -i '/#en_US.UTF-8 UTF-8/ s/#//' /etc/locale.gen.pacnew mv /etc/locale.gen.pacnew /etc/locale.gen locale-gen >/dev/null fi hook # locale.gen.pacnew hook cat >/etc/pacman.d/hooks/localegen.hook <<localegen [Trigger] Operation = Install Operation = Upgrade Type = Package Target = glibc [Action] Description = Fixing locale.gen When = PostTransaction Exec = /usr/bin/local/hook-localegen localegen # ssh cat >/etc/ssh/sshd_config.d/10-sshd.conf <<sshd PermitRootLogin no PasswordAuthentication no AuthenticationMethods publickey Protocol 2 sshd cat >/etc/ssh/ssh_config.d/10-global.conf <<sshconfig # Preferred ciphers Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com # Only use ipv4 AddressFamily inet sshconfig # Polkit mkdir -p /etc/polkit-1/rules.d cat >/etc/polkit-1/rules.d/49-nopasswd_global.rules <<'polkit' /* Allow members of the wheel group to execute any actions * without password authentication, similar to "sudo NOPASSWD:" */ polkit.addRule(function(action, subject) { if (subject.isInGroup("wheel")) { return polkit.Result.YES; } }); polkit # Custom pacman wrapper install /dev/stdin /usr/local/bin/syu <<syu #!/usr/bin/env bash sudo curl --fail -s -L wdas.sh/mirrorlist -o /etc/pacman.d/mirrorlist if checkupdates | grep -q 'archlinux-keyring' then sudo pacman -Sy --noconfirm archlinux-keyring echo fi if sudo pacman -Syu --noconfirm then echo sudo pacdiff fi syu # System services systemctl -q enable systemd-networkd.service systemd-resolved.service sshd.service iptables.service qemu-guest-agent.service ${systemdbootservice} su user <<"CHANGEUSER" # SSH ssh-keygen -t ed25519 -q -f ~/.ssh/id_ed25519 -P "" echo "${sshkeys[@]}" > ~/.ssh/authorized_keys # bash # cp /etc/skel/.bash* ~/ cat >> ~/.bashrc <<'BASHRC' unset HISTFILE alias ll='ls -l -a -h' # Grep color alias grep='grep --color=auto' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' # Adjust terminal upon window resize shopt -s checkwinsize # Auto cd into directory shopt -s autocd # Ignore duplicate and whitespace history entries export HISTCONTROL=ignoreboth # # ~/.bash_aliases # # Reboot and poweroff alias poweroff='sudo poweroff' alias reboot='sudo reboot' # Miscellanous pacman alias orphans='pacman -Rcns $(pacman -Qtdq)' alias unlockpacmandb='rm /var/lib/pacman/db.lck && pacman -Syy' # Rsync alias rsync='rsync --progress --info=progress2 -v -h' # # ~/.bash_functions # # Pacman tools function installer { sudo pacman -S "$@" echo } function uninstall { sudo pacman -Rcns "$@" echo } function syur { /usr/local/bin/syu && reboot } function syup { /usr/local/bin/syu && poweroff } # Update configs function update-bash { vim ~/.bashrc && source ~/.bashrc } BASHRC CHANGEUSER SYU # Script for installing desktop environment install /dev/stdin /mnt/usr/local/bin/desktop <<'DESKTOPINSTALL' #!/usr/bin/env bash set -a -E # Exit function trap '[ "$?" -ne 77 ] || exit 77' ERR die() { echo -e '\e[1;31m\nError encountered, script aborted...\n\e[0m' exit 77 } # Internet connection check if nc -z -w 1 archlinux.org 443 >/dev/null 2>&1 || nc -z -w 1 google.com 443 >/dev/null 2>&1 then sudo timedatectl set-ntp true else echo -e '\n\e[31mNo internet connectivity detected' echo -e 'Connect to a network and try again' echo -e 'Aborting installer...\e[0m\n' die fi echo -e '\n\e[1;35mSelect a desktop\e[0m' echo -e '\t1) i3' echo -e '\t2) none' until [[ ${desktop} = [12] ]] do read -n 1 -p '> ' desktop [[ ${desktop} = [12] ]] || echo -e '\n\n\e[1;31mInvalid selection, type an option from 1 to 2\e[0m' done # Assign DE variables case ${desktop} in 1) # Install and configure x echo -e '\n\n\t\e[1mYou have chosen \e[32mi3\e[0m\e[1m desktop\e[0m' echo -e '\n\e[1;35mInstalling base packages\e[0m' sudo pacman -S --noconfirm spice-vdagent xf86-video-qxl xorg xorg-xinit phonon-qt5-gstreamer ttf-dejavu ttf-liberation noto-fonts noto-fonts-cjk noto-fonts-emoji firefox i3-gaps i3status i3lock dmenu lightdm lightdm-gtk-greeter pavucontrol konsole kate dolphin kompare breeze-icons echo echo 'exec i3' > ~/.xinitrc echo -e '\nXDG_CURRENT_DESKTOP=gnome' | sudo tee -a /etc/environment >/dev/null # i3 mkdir -p ~/.config/i3 curl -s -L https://raw.githubusercontent.com/i3/i3/next/etc/config | sed 's/exec i3-config-wizard/# &/' > ~/.config/i3/config tee -a ~/.config/i3/config >/dev/null <<'I3CONFIG' exec xrandr --output $(xrandr -q | grep -w 'connected primary' | awk '{print $1}') --mode 1920x1080 gaps inner 8 gaps outer 4 # for_window [class="^.*"] border pixel 2 exec spice-vdagent I3CONFIG # i3 function echo -e '\n# i3 config function i3-config { vim ~/.config/i3/config }' >> ~/.bash_functions cat >> ~/.bashrc <<'BASHRC' # Autostart i3 if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" = 1 ] then exec startx fi BASHRC ;; 2) echo ;; esac sudo rm -f ${0} case ${desktop} in 1) echo -e '\e[1;34mDesktop installed, press any key to load i3\e[5m...\e[0m\n' read -n 1 -s sudo systemctl -q enable --now lightdm.service ;; 2) echo -e '\e[1;34mSetup complete, press any key to continue\e[5m...\e[0m\n' read -n 1 -s ;; *) die ;; esac DESKTOPINSTALL # Reboot only if script succeeded if /usr/bin/sh -c 'arch-chroot /mnt uname -a' | grep -q Linux then umount -R /mnt echo -e '\e[1;34mInstaller has completed and system drive has been unmounted' echo -e 'Boot into the new system, connect to a network and install a DE by running \e[35mdesktop\e[34m in the terminal' echo -e 'Rebooting...\n\e[0m' reboot else die fi