v0.5.0-alpha: TTY1 installer (omarchy/archinstall-style)
Adds: - overlay/usr/local/sbin/veilor-installer — bash+whiptail TUI - overlay/etc/systemd/system/getty@tty1.service.d/veilor-installer.conf → replaces tty1 login with installer - ks: newt + parted + cryptsetup + lvm2 + btrfs-progs packages - ks: systemctl set-default multi-user.target (TTY1 lands first; user picks "Try live — desktop" from menu to isolate graphical.target) - ks: chmod +x veilor-installer in chroot %post Flow: 1. Boot ISO → TTY1 → ASCII banner + menu: 1) Install to disk 2) Try live — desktop 3) Try live — shell 4) Reboot 5) Power off 2. Install path: collects disk/hostname/LUKS/admin pw/locale via whiptail, generates /run/install/veilor-generated.ks, execs anaconda --kickstart= 3. Reboots into hardened install with full init_on_alloc/free cmdline Known limitations (v0.5.0-alpha): - Generated ks doesn't yet copy overlay/scripts into target (anaconda installs base Fedora, missing veilor branding/hardening). Fix in v0.5.1. - whiptail = ugly. v0.5.1 swaps to gum (Go TUI) for omarchy-tier UX. - No mid-install progress bar; anaconda runs unattended in same tty.
This commit is contained in:
parent
ebf0032559
commit
3328ffb460
3 changed files with 297 additions and 1 deletions
|
|
@ -92,6 +92,13 @@ syslinux
|
|||
isomd5sum
|
||||
xorriso
|
||||
|
||||
# veilor-installer dependencies (TTY1 TUI installer wrapping anaconda)
|
||||
newt
|
||||
parted
|
||||
cryptsetup
|
||||
lvm2
|
||||
btrfs-progs
|
||||
|
||||
|
||||
# core hardening tools
|
||||
fail2ban
|
||||
|
|
@ -198,7 +205,7 @@ echo " veilor-os install — %post"
|
|||
echo "════════════════════════════════════════════════════════"
|
||||
|
||||
REPO=/usr/share/veilor-os
|
||||
chmod +x $REPO/scripts/*.sh $REPO/scripts/selinux/*.sh /usr/local/bin/veilor-power /usr/local/sbin/veilor-firstboot
|
||||
chmod +x $REPO/scripts/*.sh $REPO/scripts/selinux/*.sh /usr/local/bin/veilor-power /usr/local/sbin/veilor-firstboot /usr/local/sbin/veilor-installer
|
||||
|
||||
# Live image plumbing (matches upstream Fedora live ks). Without these the
|
||||
# squashfs/EFI build fails — livesys-scripts ships systemd units lorax expects.
|
||||
|
|
@ -236,6 +243,11 @@ fi
|
|||
# Without this, sddm stays inactive even though enabled.
|
||||
ln -sf /usr/lib/systemd/system/sddm.service /etc/systemd/system/display-manager.service
|
||||
|
||||
# Live ISO default target: multi-user (TTY1 = veilor-installer TUI lands first).
|
||||
# User picks "Try live — desktop" from menu → systemctl isolate graphical.target.
|
||||
# Real installs land on graphical.target by default (set by anaconda).
|
||||
systemctl set-default multi-user.target
|
||||
|
||||
# zram swap (no disk swap; keys never leak to platter)
|
||||
dnf install -y zram-generator || true
|
||||
cat > /etc/systemd/zram-generator.conf << 'EOF'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# veilor-os: replace tty1 login prompt with installer TUI.
|
||||
# Live ISO ONLY. Removed by anaconda during real install (this overlay
|
||||
# isn't copied into target system — see kickstart/install.ks).
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=-/usr/local/sbin/veilor-installer
|
||||
StandardInput=tty
|
||||
StandardOutput=tty
|
||||
StandardError=tty
|
||||
TTYPath=/dev/tty1
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
268
overlay/usr/local/sbin/veilor-installer
Normal file
268
overlay/usr/local/sbin/veilor-installer
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
#!/usr/bin/env bash
|
||||
# veilor-os installer — TUI wrapper around anaconda kickstart install.
|
||||
# Runs on tty1 in place of getty (live ISO boot path).
|
||||
#
|
||||
# Flow:
|
||||
# 1. ASCII banner
|
||||
# 2. Menu: Install / Live shell / Reboot / Power off
|
||||
# 3. If Install: collect answers via whiptail (disk, hostname, LUKS pw,
|
||||
# admin pw, locale)
|
||||
# 4. Generate /run/install/veilor-generated.ks from template + answers
|
||||
# 5. Exec anaconda --kickstart=/run/install/veilor-generated.ks
|
||||
# 6. On finish: reboot into installed system
|
||||
#
|
||||
# v0.5.0 — first cut. v0.5.1 swaps whiptail for gum (Go TUI, prettier).
|
||||
|
||||
set -uo pipefail
|
||||
export TERM="${TERM:-linux}"
|
||||
LOG=/var/log/veilor-installer.log
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
|
||||
banner() {
|
||||
clear
|
||||
cat << 'EOF'
|
||||
|
||||
▌ ▌▙▀▖▌ ▐▌▛▀▖▌ ▖▙▀▖▖▖
|
||||
▙▖▌█ ▐▖▟▘▙▄▘▌ ▌▌ ▙▟
|
||||
▘
|
||||
veilor-os installer
|
||||
hardened. branded. yours.
|
||||
|
||||
EOF
|
||||
echo "──────────────────────────────────────────"
|
||||
echo
|
||||
}
|
||||
|
||||
require_tty() {
|
||||
if ! [[ -t 0 && -t 1 ]]; then
|
||||
echo "[ERR] veilor-installer must run on a real tty" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
local choice
|
||||
choice=$(whiptail --title "veilor-os" \
|
||||
--menu "Welcome. What would you like to do?" 16 60 5 \
|
||||
"1" "Install veilor-os to disk" \
|
||||
"2" "Try live — desktop (KDE Plasma)" \
|
||||
"3" "Try live — shell" \
|
||||
"4" "Reboot" \
|
||||
"5" "Power off" \
|
||||
3>&1 1>&2 2>&3)
|
||||
echo "$choice"
|
||||
}
|
||||
|
||||
collect_answers() {
|
||||
local disk hostname luks_pw admin_pw locale
|
||||
local disks_list
|
||||
|
||||
# ── Disk ──
|
||||
disks_list=$(lsblk -dpno NAME,SIZE,MODEL | grep -E '^/dev/(sd|nvme|vd)' | awk '{print $1, $2"_"$3}')
|
||||
if [[ -z $disks_list ]]; then
|
||||
whiptail --title "veilor-os" --msgbox "No installable disks found." 8 50
|
||||
return 1
|
||||
fi
|
||||
disk=$(whiptail --title "Select install disk" \
|
||||
--menu "WARNING: selected disk will be ERASED." 18 70 8 \
|
||||
$disks_list 3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
# ── Hostname ──
|
||||
hostname=$(whiptail --title "Hostname" \
|
||||
--inputbox "Set hostname:" 10 60 "veilor" \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
# ── LUKS passphrase ──
|
||||
luks_pw=$(whiptail --title "Disk encryption" \
|
||||
--passwordbox "LUKS passphrase (full-disk encryption):" 10 60 \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
[[ ${#luks_pw} -lt 8 ]] && {
|
||||
whiptail --title "Weak passphrase" --msgbox "Min 8 chars." 8 40
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Admin password ──
|
||||
admin_pw=$(whiptail --title "Admin password" \
|
||||
--passwordbox "Admin user password (login after install):" 10 60 \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
[[ ${#admin_pw} -lt 8 ]] && {
|
||||
whiptail --title "Weak password" --msgbox "Min 8 chars." 8 40
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Locale ──
|
||||
locale=$(whiptail --title "Locale" \
|
||||
--menu "Choose locale:" 14 50 4 \
|
||||
"en_GB.UTF-8" "English (UK)" \
|
||||
"en_US.UTF-8" "English (US)" \
|
||||
"de_DE.UTF-8" "Deutsch" \
|
||||
"fr_FR.UTF-8" "Francais" \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
# ── Confirmation ──
|
||||
whiptail --title "Confirm install" --yesno \
|
||||
"About to install veilor-os:
|
||||
|
||||
Disk: $disk (will be ERASED)
|
||||
Hostname: $hostname
|
||||
Locale: $locale
|
||||
LUKS: set
|
||||
Admin pw: set
|
||||
|
||||
Proceed?" 16 60 || return 1
|
||||
|
||||
# Export to caller via globals
|
||||
SEL_DISK=$disk
|
||||
SEL_HOSTNAME=$hostname
|
||||
SEL_LUKS_PW=$luks_pw
|
||||
SEL_ADMIN_PW=$admin_pw
|
||||
SEL_LOCALE=$locale
|
||||
return 0
|
||||
}
|
||||
|
||||
generate_ks() {
|
||||
# Build kickstart for actual disk install.
|
||||
# NOTE: passwords go in via --plaintext to avoid storing crypted hash
|
||||
# collisions; anaconda hashes per /etc/login.defs at install time.
|
||||
local out=/run/install/veilor-generated.ks
|
||||
mkdir -p /run/install
|
||||
cat > "$out" << EOF
|
||||
# veilor-os installer-generated kickstart
|
||||
# DO NOT commit this file — secrets inline.
|
||||
url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64"
|
||||
repo --name=fedora --baseurl="https://download.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os/" --install
|
||||
repo --name=updates --baseurl="https://download.fedoraproject.org/pub/fedora/linux/updates/43/Everything/x86_64/" --install
|
||||
|
||||
keyboard --xlayouts='us'
|
||||
lang $SEL_LOCALE
|
||||
timezone Europe/London --utc
|
||||
|
||||
firstboot --disable
|
||||
eula --agreed
|
||||
selinux --enforcing
|
||||
services --enabled=sshd,fail2ban,usbguard,tuned,auditd,firewalld,chronyd,sddm
|
||||
|
||||
network --bootproto=dhcp --device=link --activate --hostname=$SEL_HOSTNAME
|
||||
firewall --enabled --service=ssh
|
||||
|
||||
rootpw --lock
|
||||
user --name=admin --groups=wheel --gecos="veilor admin" --password="$SEL_ADMIN_PW" --plaintext
|
||||
|
||||
# Full hardening cmdline (installed system, not live):
|
||||
bootloader --location=mbr --append="lockdown=integrity slab_nomerge init_on_alloc=1 init_on_free=1 randomize_kstack_offset=on vsyscall=none"
|
||||
|
||||
# Disk: zero, LUKS2 (argon2id), btrfs subvolumes
|
||||
zerombr
|
||||
clearpart --all --initlabel --drives=$(basename "$SEL_DISK")
|
||||
part /boot/efi --fstype=efi --size=600 --asprimary
|
||||
part /boot --fstype=ext4 --size=1024 --asprimary
|
||||
part pv.veilor --grow --encrypted --luks-version=luks2 --pbkdf=argon2id --passphrase="$SEL_LUKS_PW"
|
||||
volgroup veilor pv.veilor
|
||||
logvol / --vgname=veilor --name=root --fstype=btrfs --grow
|
||||
|
||||
%packages --excludedocs
|
||||
@^kde-desktop-environment
|
||||
@kde-apps
|
||||
@core
|
||||
@hardware-support
|
||||
@standard
|
||||
fail2ban
|
||||
fail2ban-firewalld
|
||||
usbguard
|
||||
usbguard-tools
|
||||
audit
|
||||
policycoreutils-python-utils
|
||||
tuned
|
||||
chrony
|
||||
firewalld
|
||||
plymouth
|
||||
git
|
||||
vim-enhanced
|
||||
tmux
|
||||
htop
|
||||
podman
|
||||
NetworkManager
|
||||
NetworkManager-wifi
|
||||
fontconfig
|
||||
fira-code-fonts
|
||||
zram-generator
|
||||
-abrt*
|
||||
-snapd
|
||||
-kde-connect
|
||||
-mlocate
|
||||
%end
|
||||
|
||||
# Reboot when done
|
||||
reboot
|
||||
EOF
|
||||
echo "[INFO] generated kickstart at $out"
|
||||
return 0
|
||||
}
|
||||
|
||||
run_install() {
|
||||
whiptail --title "Installing" --infobox \
|
||||
"Installing veilor-os to $SEL_DISK ...
|
||||
This will take 10-30 minutes.
|
||||
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log" 10 60
|
||||
sleep 2
|
||||
# Hand off to anaconda. --kickstart runs unattended.
|
||||
if anaconda --kickstart=/run/install/veilor-generated.ks; then
|
||||
whiptail --title "Done" --msgbox \
|
||||
"Install complete. System will reboot.
|
||||
Remove the install media after shutdown." 10 50
|
||||
sleep 3
|
||||
systemctl reboot
|
||||
else
|
||||
whiptail --title "Install failed" --msgbox \
|
||||
"Anaconda exited non-zero.
|
||||
Logs at /tmp/anaconda.log + /var/log/veilor-installer.log.
|
||||
Press OK to drop to shell." 12 60
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
drop_to_shell() {
|
||||
clear
|
||||
cat << 'EOF'
|
||||
═══════════════════════════════════════════════════
|
||||
veilor-os live shell
|
||||
═══════════════════════════════════════════════════
|
||||
|
||||
You are in a live, in-memory environment.
|
||||
Nothing persists across reboot.
|
||||
|
||||
Re-run the installer: sudo veilor-installer
|
||||
Reboot: sudo systemctl reboot
|
||||
Power off: sudo systemctl poweroff
|
||||
|
||||
EOF
|
||||
exec /bin/bash --login
|
||||
}
|
||||
|
||||
# ── Entry ──
|
||||
require_tty
|
||||
banner
|
||||
|
||||
launch_desktop() {
|
||||
clear
|
||||
echo "Launching KDE Plasma..."
|
||||
sleep 1
|
||||
systemctl isolate graphical.target
|
||||
# systemd-isolate switches target; sddm spawns on tty1.
|
||||
# If user logs out, they come back here. Loop continues.
|
||||
}
|
||||
|
||||
while true; do
|
||||
case "$(main_menu)" in
|
||||
1)
|
||||
if collect_answers && generate_ks; then
|
||||
run_install || continue
|
||||
fi
|
||||
;;
|
||||
2) launch_desktop ;;
|
||||
3) drop_to_shell ;;
|
||||
4) systemctl reboot ;;
|
||||
5) systemctl poweroff ;;
|
||||
*) drop_to_shell ;;
|
||||
esac
|
||||
done
|
||||
Loading…
Reference in a new issue