v0.5.0-beta: fix 4 installer blockers found during lint

Bugs found by agent linter on v0.5.0-alpha:

1. logvol missing --size: ksvalidator rejected. Added --size=8192 --grow.
2. bootloader --location=mbr on UEFI: conflicts with /boot/efi part.
   Switched to --location=none (anaconda auto-detects EFI vs BIOS).
3. lsblk awk truncated multi-word disk models ("WD PC SN740" → "WD").
   Now collapses model spaces to underscores, preserves full string.
   Also added mmcblk to disk regex (eMMC support).
4. Heredoc with $VAR expansion + passwords containing $/`/" corrupted
   generated ks. Now: single-quoted heredoc + sed placeholder
   substitution. Plus input validator rejects "$\` chars in passwords.

ksvalidator clean on sample generated ks.
bash -n clean.

CI build still in flight (3328ffb). This pushes a new commit; CI will
run again with these fixes. Net delay: zero (3328ffb's installer was
broken anyway, so its ISO unusable for install path).
This commit is contained in:
veilor-org 2026-05-02 03:42:15 +01:00
parent 3328ffb460
commit fc7c3f858b

View file

@ -58,7 +58,10 @@ collect_answers() {
local disks_list local disks_list
# ── Disk ── # ── Disk ──
disks_list=$(lsblk -dpno NAME,SIZE,MODEL | grep -E '^/dev/(sd|nvme|vd)' | awk '{print $1, $2"_"$3}') # Build "tag description" pairs for whiptail. Model strings have spaces
# (e.g. "WD PC SN740"), so collapse model to underscores for menu.
disks_list=$(lsblk -dpno NAME,SIZE,MODEL | grep -E '^/dev/(sd|nvme|vd|mmcblk)' | \
awk '{name=$1; size=$2; $1=""; $2=""; sub(/^ +/,""); gsub(/ /,"_"); model=$0; if(model=="")model="unknown"; print name, size"_"model}')
if [[ -z $disks_list ]]; then if [[ -z $disks_list ]]; then
whiptail --title "veilor-os" --msgbox "No installable disks found." 8 50 whiptail --title "veilor-os" --msgbox "No installable disks found." 8 50
return 1 return 1
@ -72,23 +75,34 @@ collect_answers() {
--inputbox "Set hostname:" 10 60 "veilor" \ --inputbox "Set hostname:" 10 60 "veilor" \
3>&1 1>&2 2>&3) || return 1 3>&1 1>&2 2>&3) || return 1
# Reject shell-special chars in passwords. Generated kickstart writes
# them via heredoc + sed substitution; bare $, ", \, ` would corrupt
# the ks line or partially expand. 8-char min for entropy.
validate_pw() {
local pw=$1 label=$2
if [[ ${#pw} -lt 8 ]]; then
whiptail --title "Weak $label" --msgbox "Min 8 chars." 8 40
return 1
fi
if [[ $pw =~ [\"\$\\\`] ]]; then
whiptail --title "Invalid $label" --msgbox \
"Cannot contain: \" \$ \\ \`" 8 50
return 1
fi
return 0
}
# ── LUKS passphrase ── # ── LUKS passphrase ──
luks_pw=$(whiptail --title "Disk encryption" \ luks_pw=$(whiptail --title "Disk encryption" \
--passwordbox "LUKS passphrase (full-disk encryption):" 10 60 \ --passwordbox "LUKS passphrase (full-disk encryption):" 10 60 \
3>&1 1>&2 2>&3) || return 1 3>&1 1>&2 2>&3) || return 1
[[ ${#luks_pw} -lt 8 ]] && { validate_pw "$luks_pw" "passphrase" || return 1
whiptail --title "Weak passphrase" --msgbox "Min 8 chars." 8 40
return 1
}
# ── Admin password ── # ── Admin password ──
admin_pw=$(whiptail --title "Admin password" \ admin_pw=$(whiptail --title "Admin password" \
--passwordbox "Admin user password (login after install):" 10 60 \ --passwordbox "Admin user password (login after install):" 10 60 \
3>&1 1>&2 2>&3) || return 1 3>&1 1>&2 2>&3) || return 1
[[ ${#admin_pw} -lt 8 ]] && { validate_pw "$admin_pw" "password" || return 1
whiptail --title "Weak password" --msgbox "Min 8 chars." 8 40
return 1
}
# ── Locale ── # ── Locale ──
locale=$(whiptail --title "Locale" \ locale=$(whiptail --title "Locale" \
@ -125,8 +139,12 @@ generate_ks() {
# NOTE: passwords go in via --plaintext to avoid storing crypted hash # NOTE: passwords go in via --plaintext to avoid storing crypted hash
# collisions; anaconda hashes per /etc/login.defs at install time. # collisions; anaconda hashes per /etc/login.defs at install time.
local out=/run/install/veilor-generated.ks local out=/run/install/veilor-generated.ks
local disk_basename
disk_basename=$(basename "$SEL_DISK")
mkdir -p /run/install mkdir -p /run/install
cat > "$out" << EOF # Single-quoted heredoc → no shell expansion. Substitute placeholders
# via sed afterwards. Bulletproof against $/`/" in passwords.
cat > "$out" << 'KSEOF' || return 1
# veilor-os installer-generated kickstart # veilor-os installer-generated kickstart
# DO NOT commit this file — secrets inline. # DO NOT commit this file — secrets inline.
url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64" url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64"
@ -134,7 +152,7 @@ repo --name=fedora --baseurl="https://download.fedoraproject.org/pub/fedora/linu
repo --name=updates --baseurl="https://download.fedoraproject.org/pub/fedora/linux/updates/43/Everything/x86_64/" --install repo --name=updates --baseurl="https://download.fedoraproject.org/pub/fedora/linux/updates/43/Everything/x86_64/" --install
keyboard --xlayouts='us' keyboard --xlayouts='us'
lang $SEL_LOCALE lang __LOCALE__
timezone Europe/London --utc timezone Europe/London --utc
firstboot --disable firstboot --disable
@ -142,23 +160,24 @@ eula --agreed
selinux --enforcing selinux --enforcing
services --enabled=sshd,fail2ban,usbguard,tuned,auditd,firewalld,chronyd,sddm services --enabled=sshd,fail2ban,usbguard,tuned,auditd,firewalld,chronyd,sddm
network --bootproto=dhcp --device=link --activate --hostname=$SEL_HOSTNAME network --bootproto=dhcp --device=link --activate --hostname=__HOSTNAME__
firewall --enabled --service=ssh firewall --enabled --service=ssh
rootpw --lock rootpw --lock
user --name=admin --groups=wheel --gecos="veilor admin" --password="$SEL_ADMIN_PW" --plaintext user --name=admin --groups=wheel --gecos="veilor admin" --password=__ADMIN_PW__ --plaintext
# Full hardening cmdline (installed system, not live): # 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" # --location=none: anaconda auto-places bootloader (UEFI grub2-efi or BIOS).
bootloader --location=none --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 # Disk: zero, LUKS2 (argon2id), btrfs subvolumes
zerombr zerombr
clearpart --all --initlabel --drives=$(basename "$SEL_DISK") clearpart --all --initlabel --drives=__DISK_BASENAME__
part /boot/efi --fstype=efi --size=600 --asprimary part /boot/efi --fstype=efi --size=600
part /boot --fstype=ext4 --size=1024 --asprimary part /boot --fstype=ext4 --size=1024
part pv.veilor --grow --encrypted --luks-version=luks2 --pbkdf=argon2id --passphrase="$SEL_LUKS_PW" part pv.veilor --grow --encrypted --luks-version=luks2 --pbkdf=argon2id --passphrase=__LUKS_PW__
volgroup veilor pv.veilor volgroup veilor pv.veilor
logvol / --vgname=veilor --name=root --fstype=btrfs --grow logvol / --vgname=veilor --name=root --fstype=btrfs --size=8192 --grow
%packages --excludedocs %packages --excludedocs
@^kde-desktop-environment @^kde-desktop-environment
@ -194,7 +213,17 @@ zram-generator
# Reboot when done # Reboot when done
reboot reboot
EOF KSEOF
# Substitute placeholders. Use | as sed delimiter (passwords might
# contain /). Forbidden chars in passwords (validated upstream): "$\`
# — sed safe.
sed -i \
-e "s|__LOCALE__|$SEL_LOCALE|" \
-e "s|__HOSTNAME__|$SEL_HOSTNAME|" \
-e "s|__DISK_BASENAME__|$disk_basename|" \
-e "s|__LUKS_PW__|$SEL_LUKS_PW|" \
-e "s|__ADMIN_PW__|$SEL_ADMIN_PW|" \
"$out"
echo "[INFO] generated kickstart at $out" echo "[INFO] generated kickstart at $out"
return 0 return 0
} }