#!/usr/bin/env bash revision=0.1a set -a set -E echo # Environment variables tee -a /etc/environment >/dev/null <<- environment EDITOR=vim SUDO_EDITOR=vim environment # Global bashrc tee -a /etc/skel/.bashrc >/dev/null <<- 'bashglobal' # Source bash functions if [ -d ~/.local/functions ] then for file in $(find ~/.local/functions -type f) do . ${file} done fi # Add local functions folder to path export PATH=${PATH}:${HOME}/.local/bin:/zfs/bin:/opt/local/bin export SUDO_PROMPT=$'\a'"$(tput rev)[sudo] password for %p:$(tput sgr0)"' ' # Colored prompts alias diff='diff --color=auto' alias ip='ip -color=auto' export LESS='-R --use-color -Dd+r$Du+b$' # Adjust terminal upon window resize shopt -s checkwinsize # Auto cd into directory shopt -s autocd # Enable tab complete for sudo complete -c -f sudo # Ignore duplicate and whitespace history entries export HISTCONTROL=ignoreboth # # ~/.bash_aliases # # ZFS/btrfs alias zfs='sudo zfs' alias zpool='sudo zpool' alias btrfs='sudo btrfs' # Shutdown reboot alias poweroff='sudo poweroff' alias reboot='sudo reboot' # Text editors alias v='vim' alias sv='sudo vim' # Clear bash history alias clearhistory='rm ${HISTFILE}; history -c -w' # Miscellanous pacman alias orphans='sudo pacman -Rcns $(pacman -Qtdq)' alias unlockpacman='sudo rm /var/lib/pacman/db.lck && sudo pacman -Syyu' # Rsync alias rsync='rsync -v -h --progress --info=progress2 --partial --append-verify' # --log-file= # --remove-source-files # # ~/.bash_functions # # Pacman tools function installer { /opt/local/bin/cacheserver sudo pacman -S ${@} echo } function uninstall { sudo pacman -Rcns ${@} echo } function mirrors { echo sudo reflector --country CA,US --age 24 --latest 20 --protocol https --fastest 25 --sort rate --save /etc/pacman.d/mirrorlist echo cat /etc/pacman.d/mirrorlist echo } function syur { /opt/local/bin/syu && reboot } function syup { /opt/local/bin/syu && poweroff } # Update bash function update-bash { vim ~/.bashrc && source ~/.bashrc } bashglobal # Root bashrc rsync -a /etc/skel/.bashrc ~/ mkdir -p ~/.local/functions/ cat > ~/.local/functions/bashrc <<- 'bashrc' #!/usr/bin/env bash # Root shell color PS1="$(tput setaf 1)[\u@\h \W \$?]\$$(tput sgr0) " # Colored prompts alias ll='ls --color=auto -l -a -h' alias egrep='egrep --color=auto' alias fgrep='fgrep --color=auto' # Disable history unset HISTFILE rm -f ${HISTFILE} history -c -w bashrc # Locale sed '/#en_US.UTF-8 UTF-8/ s/#//' -i /etc/locale.gen locale-gen >/dev/null echo 'LANG=en_US.UTF-8' >>/etc/locale.conf say as heading 'Locale configured' # Time zone if ls -l /dev/disk/* | grep -q 'VBOX\|virtio\|QEMU' then ln -s -f $(find /usr/share/zoneinfo/ | shuf -n 1) /etc/localtime else ln -s -f /usr/share/zoneinfo/UTC /etc/localtime fi hwclock --systohc --utc say as heading 'Time zone configured' # Hostname echo ${hostname} >/etc/hostname cat >>/etc/hosts </dev/null 2>&1 unset userpass userpass2 # Disable root account passwd -l root >/dev/null 2>&1 # Sudoers install -m 0440 /dev/stdin /etc/sudoers.d/01-DEFAULTS <<- DEFAULTS Defaults passwd_timeout=0 Defaults timestamp_type=global Defaults insults DEFAULTS install -m 0440 /dev/stdin /etc/sudoers.d/02-COMMANDS <<- COMMANDS Cmnd_Alias POWER = /usr/bin/poweroff, /usr/bin/reboot Cmnd_Alias ZFS = /usr/bin/zfs, /usr/bin/zpool Cmnd_Alias BTRFS = /usr/bin/btrfs, /usr/bin/timeshift, /usr/bin/timeshift-gtk, /usr/bin/timeshift-launcher Cmnd_Alias QEMU = /usr/bin/virsh, /usr/bin/qemu-system-x86_64, /usr/bin/virt-install Cmnd_Alias FAIL2BAN = /usr/bin/fail2ban-client Cmnd_Alias ARCHISO = /opt/local/bin/mkairgap, /opt/local/bin/mkiso Cmnd_Alias PACMAN = /usr/bin/pacman -Sy Cmnd_Alias IPTABLES = /usr/bin/iptables, /usr/bin/iptables-save Cmnd_Alias MISC = /usr/bin/rsync COMMANDS install -m 0440 /dev/stdin /etc/sudoers.d/03-WHEEL <<- WHEEL %wheel ALL=(ALL:ALL) ALL %wheel ALL=(ALL:ALL) NOPASSWD: POWER, ZFS, BTRFS, QEMU, FAIL2BAN, ARCHISO, PACMAN, IPTABLES, MISC WHEEL install -m 0440 /dev/stdin /etc/sudoers.d/.zz-NOPASSWD <<- NOPASSWD Defaults:${username} !authenticate NOPASSWD case ${filesystem} in zfs) # ZFS setup zpool set cachefile=/etc/zfs/zpool.cache zroot zgenhostid $(hostid) # ZFS files touch /zfs/snapshot-syu chown ${username}:users /zfs/snapshot-syu # Trim zroot monthly cat >/etc/systemd/system/zfs-trim@.timer <<- 'TRIM' [Unit] Description=Monthly zpool trim on %i [Timer] OnCalendar=monthly AccuracySec=1h Persistent=true [Install] WantedBy=multi-user.target TRIM cat >/etc/systemd/system/zfs-trim@.service <<- 'TRIM' [Unit] Description=zpool trim on %i Documentation=man:zpool-trim(8) Requires=zfs.target After=zfs.target ConditionACPower=true ConditionPathIsDirectory=/sys/module/zfs [Service] Nice=19 IOSchedulingClass=idle KillSignal=SIGINT ExecStart=/bin/sh -c '\ if /usr/bin/zpool status %i | grep "trimming"; then\ exec /usr/bin/zpool wait -t trim %i;\ else exec /usr/bin/zpool trim -w %i; fi' ExecStop=-/bin/sh -c '/usr/bin/zpool trim -s %i 2>/dev/null || true' [Install] WantedBy=multi-user.target TRIM # Scrub zroot monthly cat >/etc/systemd/system/zfs-scrub@.timer <<- 'SCRUB' [Unit] Description=Monthly zpool scrub on %i [Timer] OnCalendar=monthly AccuracySec=1h Persistent=true [Install] WantedBy=multi-user.target SCRUB cat >/etc/systemd/system/zfs-scrub@.service <<- 'SCRUB' [Unit] Description=zpool scrub on %i [Service] Nice=19 IOSchedulingClass=idle KillSignal=SIGINT ExecStart=/usr/bin/zpool scrub %i [Install] WantedBy=multi-user.target SCRUB # Pre and Post update backup hooks cat >/etc/pacman.d/hooks/00-syu_pre.hook <<- pre [Trigger] Type = Path Operation = Upgrade Operation = Install Operation = Remove Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Description = Creating pre zroot snapshot... When = PreTransaction Exec = /usr/bin/bash -c 'zfs snapshot zroot/ROOT@pre-\$(cat /zfs/snapshot-syu)' AbortOnFail pre cat >/etc/pacman.d/hooks/55-bootbackup_pre.hook <<- pre [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Path Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = rsync Description = Backing up pre /boot... When = PreTransaction Exec = /usr/bin/bash -c 'mount /boot; rsync -a --mkpath --delete /boot/ "/.boot/\$(cat /zfs/snapshot-syu)_pre"/' AbortOnFail pre cat >/etc/pacman.d/hooks/95-bootbackup_post.hook <<- post [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Path Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = rsync Description = Backing up post /boot... When = PostTransaction Exec = /usr/bin/bash -c 'rsync -a --mkpath --delete /boot/ "/.boot/\$(cat /zfs/snapshot-syu)_post"/' post cat >/etc/pacman.d/hooks/zz-syu_post.hook <<- post [Trigger] Type = Path Operation = Upgrade Operation = Install Operation = Remove Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Description = Creating post zroot snapshot... When = PostTransaction Exec = /usr/bin/bash -c 'zfs snapshot zroot/ROOT@post-\$(cat /zfs/snapshot-syu)' post # Custom pacman wrapper install /dev/stdin /opt/local/bin/syu <<- syu #!/usr/bin/env bash set -e mirrorlist= # Enable or disable pacman cache server /opt/local/bin/cacheserver # Fetch latest mirrors sudo curl --fail --silent \${mirrorlist} -o /etc/pacman.d/mirrorlist # Record current time echo \$(date "+%Y-%m-%d-%H:%M:%S") >/zfs/snapshot-syu # Check for new packages and continue if found newpkg+=(\$(checkupdates --nocolor | awk '{print \$1}')) if [ "\${newpkg}" ] then # Sync pacman dbs sudo pacman --ask 4 -Sy >/dev/null # Check zfs-linux kernel dependency zfslinux=\$(pacman -Si zfs-${linux_kernel} | grep "Depends On" | sed "s|.*${linux_kernel}=||") linux=\$(pacman -Si ${linux_kernel} | grep "Version" | awk '{print \$3}') if [ \${zfslinux} != \${linux} ] then archzfs="--ignore zfs-utils,zfs-${linux_kernel},${linux_kernel}" fi # Update archlinux-keyring first if [[ \${newpkg[@]} =~ "archlinux-keyring" ]] then sudo pacman --ask 4 -S archlinux-keyring echo fi # Perform update if dependencies are satisfied if sudo pacman --ask 4 -Syu \${archzfs} --needed then echo sudo pacdiff exit 0 fi fi syu ;; *) case ${filesystem} in btrfs) # Pre and post update backup hooks cat >/etc/pacman.d/hooks/00-syu_pre.hook <<- pre [Trigger] Type = Path Operation = Upgrade Operation = Install Operation = Remove Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = timeshift Description = Creating pre root snapshot... When = PreTransaction Exec = /usr/bin/bash -c 'timeshift --create --comments "pre_\$(date "+%Y-%m-%d-%H:%M:%S")" >/dev/null' AbortOnFail pre cat >/etc/pacman.d/hooks/55-bootbackup_pre.hook <<- pre [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Path Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = rsync Description = Backing up pre /boot... When = PreTransaction Exec = /usr/bin/bash -c 'rsync -a --mkpath --delete --exclude 'header.img' /boot/ "/.boot/\$(date "+%Y-%m-%d-%H:%M:%S")_pre"/' AbortOnFail pre cat >/etc/pacman.d/hooks/95-bootbackup_post.hook <<- post [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Path Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = rsync Description = Backing up post /boot... When = PostTransaction Exec = /usr/bin/bash -c 'rsync -a --mkpath --delete --exclude 'header.img' /boot/ "/.boot/\$(date "+%Y-%m-%d-%H:%M:%S")_post"/' post cat >/etc/pacman.d/hooks/zz-syu_post.hook <<- post [Trigger] Type = Path Operation = Upgrade Operation = Install Operation = Remove Target = usr/lib/modules/*/vmlinuz Target = usr/lib/initcpio/* Target = usr/lib/firmware/* Target = usr/src/*/dkms.conf [Action] Depends = timeshift Description = Creating post root snapshot... When = PostTransaction Exec = /usr/bin/bash -c 'timeshift --create --comments "post_\$(date "+%Y-%m-%d-%H:%M:%S")" >/dev/null' post install /dev/stdin /usr/local/bin/timeshift-gui <<- 'timeshift' #!/bin/sh pkexec env WAYLAND_DISPLAY="$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" XDG_RUNTIME_DIR=/run/user/0 timeshift-launcher timeshift ;; esac # Custom pacman wrapper install /dev/stdin /opt/local/bin/syu <<- syu #!/usr/bin/env bash set -e mirrorlist= # Enable or disable pacman cache server /opt/local/bin/cacheserver # Fetch latest mirrors sudo curl --fail --silent \${mirrorlist} -o /etc/pacman.d/mirrorlist # Check for new packages and continue if found newpkg+=(\$(checkupdates --nocolor | awk '{print \$1}')) if [ "\${newpkg}" ] then # Update archlinux-keyring first if [[ \${newpkg[@]} =~ "archlinux-keyring" ]] then sudo pacman --ask 4 -S archlinux-keyring echo fi # Perform update if dependencies are satisfied if sudo pacman --ask 4 -Syu --needed then echo sudo pacdiff exit 0 fi fi syu ;; esac echo # /opt/local/bin/cacheserver install /dev/stdin /opt/local/bin/cacheserver <<- 'cacheserver' #!/usr/bin/env bash set -e # Enable cacheserver if active cacheserver= port= scheme= if [ -z ${cacheserver} ] then exit 1 fi if nc -z -4 -w 3 ${cacheserver} ${port:-80} >/dev/null 2>&1 then if grep -q "CacheServer" /etc/pacman.conf then sudo sed "/CacheServer/ s/^\(#\)*//g" -i /etc/pacman.conf else sudo sed "/^\[core\]$\|^\[extra\]$\|^\[myvezfs\]$/a CacheServer = ${scheme:-http}://${cacheserver}:${port:-80}" -i /etc/pacman.conf fi else sudo sed "/CacheServer/ s/^/#/g" -i /etc/pacman.conf fi cacheserver if ls -l /dev/disk/* | grep -q 'VBOX\|virtio\|QEMU' then sed -e "/^cacheserver=/c cacheserver=192.168.122.1" \ -e "/^port=/c port=9090" \ -i /opt/local/bin/cacheserver /opt/local/bin/cacheserver fi # mkinitcpio say as heading 'Regenerating cpio image' mkinitcpio -P echo # Install GRUB if [[ ${arch} = [3567] ]] then say as heading 'Installing GRUB' grub-install --target=x86_64-efi --bootloader-id="Arch Linux (${hostname})" --efi-directory=/boot/efi --recheck || die 'Grub installation failed' grub-mkconfig -o /boot/grub/grub.cfg echo fi # Sysctl custom settings cat >/etc/sysctl.d/99-sysctl.conf <<- SYSCTL 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 vm.swappiness=10 vm.vfs_cache_pressure=50 SYSCTL # iptables cat >/etc/iptables/userinput.rules <<- IPTABLES ## User input # SSH port -A INPUT -p tcp -m tcp --dport ${port:-22} -j ACCEPT -m comment --comment "SSH Port" ## Simple Firewall IPTABLES sed "/OUTPUT ACCEPT/r /etc/iptables/userinput.rules" /etc/iptables/simple_firewall.rules >/etc/iptables/iptables.rules # zram echo 'zram' >/etc/modules-load.d/zram.conf echo 'options zram num_devices=1' >/etc/modprobe.d/zram.conf echo 'KERNEL=="zram0", ATTR{comp_algorithm}="lz4", ATTR{disksize}="512M" RUN="/usr/bin/mkswap /dev/zram0", TAG+="systemd"' >/etc/udev/rules.d/99-zram.rules echo '/dev/zram0 none swap defaults 0 0' >>/etc/fstab # Configure ssh and ssh_config.d/10-global.conf ssh-keygen -q \ -t ed25519 \ -P "" \ -C "${USER}@${hostname}" \ -f ~/.ssh/id_ed25519 mkdir ~/.ssh/sockets/ cat >/etc/ssh/sshd_config.d/10-sshd.conf <<- sshd PermitRootLogin no PasswordAuthentication no AuthenticationMethods publickey 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 # Multiplex ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h-%p ControlPersist 10m # Ease up on local area network devices Host 192.168.* StrictHostKeyChecking no UserKnownHostsFile=/dev/null LogLevel Error 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 # Persistent journal logging mkdir -p /etc/systemd/journald.conf.d/ cat >/etc/systemd/journald.conf.d/zz-journald.conf <<- eof [Journal] Storage=persistent eof # makepkg mkdir -p /etc/makepkg.conf.d/ cat >/etc/makepkg.conf.d/zz-makepkg.conf <<- makepkg PKGEXT=".pkg.tar" MAKEFLAGS="--jobs=$(nproc)" COMPRESSZST=(zstd -c -T0 --auto-threads=logical -) makepkg # Virtual machine settings if ls -l /dev/disk/* | grep -q 'VBOX\|virtio\|QEMU' then mv /etc/sudoers.d/.zz-NOPASSWD /etc/sudoers.d/zz-NOPASSWD fi ## ## Hooks ## # paccache cat >/etc/pacman.d/hooks/zz-paccache.hook <<- paccache [Trigger] Operation = Upgrade Operation = Install Operation = Remove Type = Package Target = * [Action] Description = Cleaning pacman cache... When = PostTransaction Exec = /usr/bin/paccache --remove paccache # locale.gen.pacnew hook install /dev/stdin /opt/local/hooks/localegen <<- hook #!/usr/bin/env bash if [ -f /etc/locale.gen.pacnew ] then sed '/#en_US.UTF-8 UTF-8/ s/#//' -i /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/100-localegen.hook <<- localegen [Trigger] Operation = Install Operation = Upgrade Type = Package Target = glibc [Action] Description = Fixing locale.gen When = PostTransaction Exec = /opt/local/hooks/localegen localegen # iptables cat >/etc/pacman.d/hooks/100-iptables.rules.hook </etc/iptables/iptables.rules rm /etc/iptables/iptables.rules.pacsave fi hook su ${username} <<- "user" curl --fail --silent https://git.myvelabs.com/lab/archlinux/raw/branch/master/functions/user -o /tmp/user bash /tmp/user user