153 lines
5.5 KiB
Bash
153 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Colour helpers
|
|
b=$(tput bold); r=$(tput sgr0); g=$(tput setaf 2); y=$(tput setaf 3); c=$(tput setaf 6); e=$(tput setaf 1)
|
|
info(){ echo "${c}==>${r} $*"; }
|
|
warn(){ echo "${y}!${r} $*"; }
|
|
die(){ echo "${e}x${r} $*" >&2; exit 1; }
|
|
|
|
NO_PROMPT=0
|
|
[[ "${1:-}" =~ (--no-prompt|--auto) ]] && NO_PROMPT=1
|
|
|
|
# --- Load or create .env ---
|
|
[[ ! -f .env ]] && { warn ".env missing, creating from template"; cp .env.template .env 2>/dev/null || touch .env; }
|
|
source .env || true
|
|
|
|
prompt_var(){ local n=$1 t=$2 d="${!n:-}"; [[ $NO_PROMPT == 1 ]] && { info "Using $n=$d"; return; }
|
|
read -e -p "$t [$d]: " i; [[ -n $i ]] && eval "$n=\"\$i\""; }
|
|
prompt_secret(){ local n=$1 t=$2 d="${!n:-}"; if [[ -z $d ]]; then read -s -p "$t: " i; echo; eval "$n=\"\$i\""; else info "Using $n from .env"; fi; }
|
|
save_env(){ local n=$1 v="${!n}"; sed -i "/^$n=/d" .env; echo "$n=\"$v\"" >> .env; }
|
|
|
|
# --- Gather configuration ---
|
|
prompt_var HOSTNAME "Hostname"; prompt_var USERNAME "Username"; prompt_var KEYMAP "Keymap"
|
|
prompt_var TIMEZONE "Timezone"; prompt_var LOCALE "Locale"
|
|
save_env HOSTNAME; save_env USERNAME; save_env KEYMAP; save_env TIMEZONE; save_env LOCALE
|
|
|
|
# --- Networking ---
|
|
timedatectl set-ntp true
|
|
if [[ -z "$(ip link | grep -E 'state UP')" ]]; then
|
|
warn "No active network found"
|
|
prompt_var WIFI_SSID "Wi-Fi SSID"; prompt_secret WIFI_PASS "Wi-Fi password"
|
|
iwctl station wlan0 scan || true
|
|
iwctl station wlan0 connect "$WIFI_SSID" --passphrase "$WIFI_PASS" || die "Wi-Fi connect failed"
|
|
else info "Network detected"; fi
|
|
|
|
# --- Disk selection ---
|
|
if [[ -z "${DISK:-}" ]]; then
|
|
info "Available disks:"; lsblk -dno NAME,SIZE,MODEL | grep -v loop | nl -w2 -s'. '
|
|
[[ $NO_PROMPT == 1 ]] && die "No DISK specified in .env"
|
|
read -p "Select disk number: " n; DISK="/dev/$(lsblk -dno NAME | grep -v loop | sed -n "${n}p")"
|
|
fi; save_env DISK
|
|
EFI="${DISK}p1"; ROOT="${DISK}p2"
|
|
read -p "${e}${b}⚠️ Erase $DISK? ENTER to confirm${r}"
|
|
|
|
# --- Partition + encrypt ---
|
|
sgdisk --zap-all "$DISK"
|
|
sgdisk -n1:0:+1G -t1:ef00 -c1:"EFI System" "$DISK"
|
|
sgdisk -n2:0:0 -t2:8300 -c2:"Linux LUKS" "$DISK"; partprobe "$DISK"
|
|
prompt_secret LUKS_PASSWORD "LUKS password"; save_env LUKS_PASSWORD
|
|
echo -n "$LUKS_PASSWORD" | cryptsetup luksFormat --batch-mode --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha512 "$ROOT" -
|
|
echo -n "$LUKS_PASSWORD" | cryptsetup open "$ROOT" cryptroot -
|
|
mkfs.fat -F32 "$EFI"; mkfs.ext4 /dev/mapper/cryptroot
|
|
mount /dev/mapper/cryptroot /mnt; mkdir /mnt/boot; mount "$EFI" /mnt/boot
|
|
|
|
# --- Mirrors + base ---
|
|
pacman -Sy --noconfirm reflector
|
|
reflector --country "United Kingdom" --latest 20 --sort rate --save /etc/pacman.d/mirrorlist
|
|
pacstrap -K /mnt base linux linux-firmware vim networkmanager sudo base-devel git
|
|
|
|
genfstab -U /mnt >> /mnt/etc/fstab
|
|
|
|
# --- Chroot configuration ---
|
|
arch-chroot /mnt /bin/bash <<CHROOT
|
|
set -euo pipefail
|
|
ln -sf /usr/share/zoneinfo/$TIMEZONE /etc/localtime
|
|
hwclock --systohc
|
|
sed -i "s/^#${LOCALE}/${LOCALE}/" /etc/locale.gen
|
|
locale-gen
|
|
echo "LANG=$LOCALE" > /etc/locale.conf
|
|
echo "$HOSTNAME" > /etc/hostname
|
|
cat <<EOF >/etc/hosts
|
|
127.0.0.1 localhost
|
|
::1 localhost
|
|
127.0.1.1 $HOSTNAME.localdomain $HOSTNAME
|
|
EOF
|
|
echo "KEYMAP=$KEYMAP" > /etc/vconsole.conf
|
|
|
|
sed -i 's/\(filesystems\)/encrypt \1/' /etc/mkinitcpio.conf
|
|
mkinitcpio -P
|
|
|
|
bootctl install
|
|
UUID=\$(blkid -s UUID -o value ${ROOT})
|
|
cat <<EOF >/boot/loader/loader.conf
|
|
default arch.conf
|
|
timeout 3
|
|
console-mode max
|
|
editor no
|
|
EOF
|
|
cat <<EOF >/boot/loader/entries/arch.conf
|
|
title Arch Linux
|
|
linux /vmlinuz-linux
|
|
initrd /initramfs-linux.img
|
|
options cryptdevice=UUID=\${UUID}:cryptroot root=/dev/mapper/cryptroot rw
|
|
EOF
|
|
|
|
echo "Set root password:"; passwd
|
|
useradd -m -G wheel -s /bin/bash $USERNAME
|
|
echo "Set password for $USERNAME:"; passwd $USERNAME
|
|
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' /etc/sudoers
|
|
systemctl enable NetworkManager
|
|
|
|
# --- Optional yay ---
|
|
if [[ "${INSTALL_YAY,,}" == "yes" ]]; then
|
|
pacman -Sy --noconfirm base-devel git
|
|
su - $USERNAME -c "cd /tmp && git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -si --noconfirm && rm -rf /tmp/yay"
|
|
fi
|
|
|
|
# --- Optional hardening ---
|
|
if [[ "${ENABLE_HARDENING,,}" == "yes" ]]; then
|
|
pacman -Sy --noconfirm ufw apparmor fail2ban archlinux-keyring
|
|
systemctl enable ufw; systemctl start ufw
|
|
ufw default deny incoming; ufw default allow outgoing; ufw enable
|
|
systemctl enable apparmor; systemctl enable fail2ban
|
|
cat <<EOF >/etc/sysctl.d/99-hardening.conf
|
|
net.ipv4.conf.all.accept_redirects = 0
|
|
net.ipv6.conf.all.accept_redirects = 0
|
|
net.ipv4.conf.all.send_redirects = 0
|
|
net.ipv4.conf.all.accept_source_route = 0
|
|
net.ipv6.conf.all.accept_source_route = 0
|
|
net.ipv4.tcp_syncookies = 1
|
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
|
kernel.randomize_va_space = 2
|
|
EOF
|
|
sysctl --system
|
|
echo 'Defaults timestamp_timeout=5' > /etc/sudoers.d/hardening
|
|
fi
|
|
CHROOT
|
|
|
|
# --- Optional WireGuard setup ---
|
|
if [[ "${ENABLE_WIREGUARD,,}" == "yes" && -f "${WIREGUARD_CONF_PATH}" ]]; then
|
|
info "Installing WireGuard..."
|
|
install -Dm600 "$WIREGUARD_CONF_PATH" "/mnt/etc/wireguard/wg0.conf"
|
|
arch-chroot /mnt pacman -Sy --noconfirm wireguard-tools
|
|
arch-chroot /mnt systemctl enable wg-quick@wg0.service
|
|
else
|
|
warn "WireGuard skipped (no config or disabled)."
|
|
fi
|
|
|
|
umount -R /mnt; cryptsetup close cryptroot
|
|
info "Installation complete"
|
|
|
|
cat <<EOF
|
|
|
|
${g}${b}✅ Arch Linux installation complete!${r}
|
|
|
|
Remove media and reboot:
|
|
${b}reboot${r}
|
|
|
|
Then log in as '${USERNAME}' and pull your next setup, e.g.:
|
|
${c}wget https://your.gitea.instance/raw/setup_dev_env.sh && bash setup_dev_env.sh${r}
|
|
EOF
|