veilor-os/overlay/usr/local/bin/veilor-installer

605 lines
21 KiB
Text
Raw Normal View History

#!/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:
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# 1. ASCII banner (assets/installer/banner.txt)
# 2. Menu: Install / Live desktop / Live shell / Reboot / Power off
# 3. If Install: collect answers via gum (disk, hostname, LUKS pw,
# admin pw, locale)
# 4. Generate /run/install/veilor-generated.ks from template + answers
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# (full veilor-os install: package list + overlay + scripts + harden)
# 5. Exec anaconda --kickstart=/run/install/veilor-generated.ks
# 6. On finish: reboot into installed system
#
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# v0.5.1 — gum (charm.sh) replaces whiptail; whiptail kept as fallback.
# Generated kickstart now installs full veilor-os (matches live ks).
set -uo pipefail
export TERM="${TERM:-linux}"
LOG=/var/log/veilor-installer.log
# require_tty MUST run before the tee redirect — process substitution
# replaces fd1 with a pipe, breaking `[[ -t 1 ]]`.
require_tty() {
if ! [[ -t 0 && -t 1 ]]; then
echo "[ERR] veilor-installer must run on a real tty" >&2
exit 1
fi
}
require_tty
# Now safe to tee output for log persistence — tty detection already passed.
exec > >(tee -a "$LOG") 2>&1
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# ── Branded styling for gum ─────────────────────────────────────────────
# colors.gum sets GUM_* env vars — pure-black palette from veilor-black KDE.
# Sourced at top so every prompt below picks up branded colors.
COLORS=/usr/share/veilor-os/assets/installer/colors.gum
[[ -r $COLORS ]] && source "$COLORS"
BANNER_FILE=/usr/share/veilor-os/assets/installer/banner.txt
# Detect TUI backend once. gum is preferred; whiptail is the fallback so the
# installer keeps working on minimal images or if /usr/local/bin/gum is
# missing (e.g. broken vendored binary, /usr remount issues).
if command -v gum >/dev/null 2>&1; then
TUI=gum
elif command -v whiptail >/dev/null 2>&1; then
TUI=whiptail
else
echo "[ERR] neither gum nor whiptail available — cannot run TUI" >&2
exit 1
fi
banner() {
clear
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
if [[ -r $BANNER_FILE ]]; then
if [[ $TUI == gum ]]; then
# gum style draws a rounded border + padding around the banner.
gum style --border rounded --margin "1" --padding "1 2" \
--border-foreground "${VEILOR_DIM:-240}" \
--foreground "${VEILOR_FG:-15}" \
"$(cat "$BANNER_FILE")"
else
cat "$BANNER_FILE"
echo
fi
else
# Fallback ASCII if banner.txt missing (older overlay).
cat << 'EOF'
veilor-os installer
hardened. branded. yours.
EOF
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
fi
}
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# ── TUI wrapper functions ───────────────────────────────────────────────
# Each prompt_* call abstracts gum / whiptail. Always emit the chosen value
# on stdout; non-zero exit on cancel/ESC. Callers use `||` to propagate.
# prompt_choose <header> <opt1> [opt2 ...]
# Single-select menu. Returns the selected option literal on stdout.
prompt_choose() {
local header=$1; shift
if [[ $TUI == gum ]]; then
gum choose --header "$header" "$@"
else
# whiptail menu needs tag/desc pairs. Use the option as both.
local args=()
local opt
for opt in "$@"; do args+=("$opt" "$opt"); done
whiptail --title "veilor-os" --menu "$header" 18 70 8 \
"${args[@]}" 3>&1 1>&2 2>&3
fi
}
# prompt_choose_pairs <header> <tag1> <desc1> [tag2 desc2 ...]
# Tag/description menu. Returns the chosen tag.
# Used when display label differs from machine-readable value (e.g. disk
# path /dev/nvme0n1 vs description "476G WDC PC SN740").
prompt_choose_pairs() {
local header=$1; shift
if [[ $TUI == gum ]]; then
# gum has no tag/desc concept — render "tag — desc" lines, parse.
local lines=() i=1
while (( i <= $# )); do
local tag=${!i}; ((i++))
local desc=${!i}; ((i++))
lines+=("$tag — $desc")
done
local picked
picked=$(printf '%s\n' "${lines[@]}" | gum choose --header "$header") || return 1
# Strip " — desc" suffix to recover the tag.
echo "${picked%% — *}"
else
whiptail --title "veilor-os" --menu "$header" 18 70 8 \
"$@" 3>&1 1>&2 2>&3
fi
}
# prompt_input <header> [default]
prompt_input() {
local header=$1 default=${2:-}
if [[ $TUI == gum ]]; then
gum input --header "$header" --value "$default"
else
whiptail --title "veilor-os" --inputbox "$header" 10 60 "$default" \
3>&1 1>&2 2>&3
fi
}
# prompt_password <header>
prompt_password() {
local header=$1
if [[ $TUI == gum ]]; then
gum input --password --header "$header"
else
whiptail --title "veilor-os" --passwordbox "$header" 10 60 \
3>&1 1>&2 2>&3
fi
}
# prompt_confirm <message>
# Exits 0 on yes, 1 on no — matches whiptail --yesno semantics.
prompt_confirm() {
local msg=$1
if [[ $TUI == gum ]]; then
gum confirm "$msg"
else
whiptail --title "Confirm" --yesno "$msg" 16 60
fi
}
# prompt_message <message>
# Non-blocking notice. gum has no msgbox; print styled + sleep.
prompt_message() {
local msg=$1
if [[ $TUI == gum ]]; then
gum style --foreground "${VEILOR_FG:-15}" --border rounded \
--border-foreground "${VEILOR_DIM:-240}" --padding "1 2" -- "$msg"
sleep 2
else
whiptail --title "veilor-os" --msgbox "$msg" 10 60
fi
}
# prompt_error <message>
# Same as message but with a red foreground for visibility.
prompt_error() {
local msg=$1
if [[ $TUI == gum ]]; then
gum style --foreground 1 --border rounded --border-foreground 1 \
--padding "1 2" -- "$msg"
sleep 2
else
whiptail --title "Error" --msgbox "$msg" 10 60
fi
}
main_menu() {
prompt_choose "Welcome" \
"Install" \
"live - (KDE)" \
"live - shell" \
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
"Reboot" \
"Power off"
}
collect_answers() {
local disk hostname luks_pw admin_pw locale
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
local disks_pairs
# ── Disk ──
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# Build whiptail-style "tag desc" pairs. prompt_choose_pairs reshapes
# for gum if needed.
# shellcheck disable=SC2207
disks_pairs=($(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 [[ ${#disks_pairs[@]} -eq 0 ]]; then
prompt_error "No installable disks found."
return 1
fi
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
disk=$(prompt_choose_pairs "Select install disk (will be ERASED)" "${disks_pairs[@]}") || return 1
# ── Hostname ──
# Hardcoded for branded consistency. Post-install: `hostnamectl set-hostname`.
hostname="veilor"
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# Reject shell-special and sed-special chars in passwords. Generated
# kickstart writes them via heredoc + sed substitution; bare $, ", \, `
# would corrupt the ks line or partially expand at heredoc time.
# &, |, /, newline are sed-special: & expands to the matched pattern
# (so `aA1!@#%^&*()` becomes `aA1!@#%^__ADMIN_PW__*()`), | is our
# delimiter, / would match if delimiter changes, newline breaks the
# sed expression. sed_escape() below adds defense-in-depth, but we
# also reject these at input so the user sees an immediate error
# rather than a corrupted ks file. 8-char min for entropy.
validate_pw() {
local pw=$1 label=$2
if [[ ${#pw} -lt 8 ]]; then
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
prompt_error "Weak $label — minimum 8 characters."
return 1
fi
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
if [[ $pw =~ [\"\$\\\`\&\|/$'\n'] ]]; then
prompt_error "Invalid $label — cannot contain: \" \$ \\ \` & | / newline"
return 1
fi
return 0
}
# ── LUKS passphrase ──
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
luks_pw=$(prompt_password "LUKS passphrase (full-disk encryption)") || return 1
validate_pw "$luks_pw" "passphrase" || return 1
# ── Admin password ──
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
admin_pw=$(prompt_password "Admin user password (login after install)") || return 1
validate_pw "$admin_pw" "password" || return 1
# ── Locale ──
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
locale=$(prompt_choose "Choose locale" \
"en_GB.UTF-8" \
"en_US.UTF-8") || return 1
# ── Confirmation ──
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
prompt_confirm "About to install veilor-os:
Disk: $disk (will be ERASED)
Locale: $locale
LUKS: set
Admin pw: set
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
Proceed?" || 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
}
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# sed_escape — escape sed special chars in a replacement string.
# Replacement-side metacharacters: & (matched pattern), \ (escape),
# | (our chosen delimiter), / (alternate delimiter — escape too in case
# delimiter ever changes). Newline is rejected in validate_pw because
# escaping it portably across BSD/GNU sed is fiddly.
# Order matters: \ must be escaped FIRST so we don't double-escape the
# backslashes we're about to emit for &, |, /.
sed_escape() {
printf '%s' "$1" | sed -e 's/[\\&|/]/\\&/g'
}
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.
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
#
# %packages mirrors live ks lines 63-141 (full hardening pkg list,
# minus livesys/anaconda-live which are live-only).
# %post --nochroot copies overlay + scripts + assets from boot ISO.
# %post (chroot) runs the same hardening scripts the live build runs.
local out=/run/install/veilor-generated.ks
local disk_basename
disk_basename=$(basename "$SEL_DISK")
mkdir -p /run/install
# 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
# 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 __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=__HOSTNAME__
firewall --enabled --service=ssh
rootpw --lock
user --name=admin --groups=wheel --gecos="veilor admin" --password=__ADMIN_PW__ --plaintext
# Full hardening cmdline (installed system, not live):
# --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 (no LVM intermediary).
# Native btrfs-on-LUKS matches Fedora KDE Spin defaults; LVM+btrfs combo
# triggered "mount failed: wrong fs type, bad option, bad superblock"
# under anaconda --cmdline. btrfs subvols give us the snapshot/rollback
# story without LVM's added complexity.
zerombr
clearpart --all --initlabel --drives=__DISK_BASENAME__
part /boot/efi --fstype=efi --size=600
part /boot --fstype=ext4 --size=1024
part btrfs.veilor --grow --encrypted --luks-version=luks2 --pbkdf=argon2id --passphrase=__LUKS_PW__
btrfs none --label=veilor btrfs.veilor
btrfs / --subvol --name=root LABEL=veilor
btrfs /home --subvol --name=home LABEL=veilor
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# ── Packages — mirrors live ks (kickstart/veilor-os.ks 63-141), minus
# live-only entries (livesys-scripts, anaconda-live, @anaconda-tools,
# dracut-live, isomd5sum, xorriso) which are pointless on a real disk.
%packages --excludedocs
@^kde-desktop-environment
@kde-apps
@core
@hardware-support
@standard
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
kernel-modules
kernel-modules-extra
glibc-all-langpacks
dracut-config-generic
kernel
grub2-efi-x64
grub2-efi-x64-modules
grub2-pc
grub2-pc-modules
grub2-tools
grub2-tools-extra
shim-x64
efibootmgr
syslinux
# veilor-installer dependencies (kept on installed system so
# `sudo veilor-installer` from a recovery shell still works).
newt
parted
cryptsetup
lvm2
btrfs-progs
# core hardening tools
fail2ban
fail2ban-firewalld
usbguard
usbguard-tools
audit
policycoreutils-python-utils
tuned
chrony
firewalld
plymouth
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# admin essentials
git
vim-enhanced
tmux
htop
podman
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
skopeo
NetworkManager
NetworkManager-wifi
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# fonts
fontconfig
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
freetype
fira-code-fonts
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# zram (no swap-on-disk)
zram-generator
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# remove fluff
# Note: KDE Plasma 6 hard-deps on cups/geoclue2/ModemManager/PackageKit
# transitively; daemons disabled at runtime via 20-harden-kernel.sh.
-abrt*
-snapd
-kde-connect
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
-open-vm-tools-desktop
-mlocate
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
%end
# ── Post-install (nochroot): copy overlay + scripts + assets from boot ISO.
# Mirror of live ks 144-196 trimmed to single-source: ISO mounted at
# /run/install/repo, veilor/ subtree contains overlay/, scripts/, assets/.
# (See build/Containerfile — ISO build copies the repo into veilor/.)
%post --nochroot --erroronfail
set -uo pipefail
DEST="${INSTALL_ROOT:-/mnt/sysimage}"
[[ -d $DEST ]] || { echo "[ERR] DEST=$DEST does not exist" >&2; exit 1; }
SRC=/run/install/repo/veilor
if [[ ! -d $SRC/overlay ]]; then
echo "[ERR] $SRC/overlay missing — boot ISO did not include veilor/ tree" >&2
exit 1
fi
echo "[INFO] using SRC=$SRC DEST=$DEST"
set -x
cp -a "$SRC/overlay/." "$DEST/" || echo "[ERR] overlay cp failed: $?"
mkdir -p "$DEST/usr/share/veilor-os" || echo "[ERR] mkdir failed: $?"
cp -a "$SRC/assets" "$DEST/usr/share/veilor-os/" || echo "[ERR] assets cp failed: $?"
cp -a "$SRC/scripts" "$DEST/usr/share/veilor-os/" || echo "[ERR] scripts cp failed: $?"
ls -la "$DEST/usr/share/veilor-os/" 2>&1 || echo "[ERR] dest dir missing post-cp"
# Force root ownership on everything we copied — `cp -a` preserves
# CI runner uid (1001), which makes sudo refuse to read /etc/sudoers.d.
chown -R 0:0 "$DEST/etc" "$DEST/usr/share/veilor-os" "$DEST/usr/local/bin" 2>&1 || echo "[WARN] chown failed"
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
set +x
{
echo "=== %post --nochroot trace ==="
date
echo "SRC=$SRC DEST=$DEST"
ls -la "$DEST/usr/share/veilor-os/" 2>&1
ls -la "$DEST/usr/local/bin/" 2>&1
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
} > "$DEST/var/log/veilor-nochroot.log" 2>&1 || true
%end
# ── Post-install (chroot): apply hardening, theme, branding ────────────
%post
set -uo pipefail
exec > >(tee -a /var/log/veilor-install.log) 2>&1
echo "════════════════════════════════════════════════════════"
echo " veilor-os install — %post (real install)"
echo "════════════════════════════════════════════════════════"
REPO=/usr/share/veilor-os
chmod +x $REPO/scripts/*.sh $REPO/scripts/selinux/*.sh \
/usr/local/bin/veilor-power /usr/local/bin/veilor-firstboot \
/usr/local/bin/veilor-installer 2>/dev/null || true
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# /etc/machine-id reset on first boot
> /etc/machine-id
# Apply hardening
bash $REPO/scripts/10-harden-base.sh
bash $REPO/scripts/20-harden-kernel.sh
# Build SELinux module
bash $REPO/scripts/selinux/build-policy.sh || echo "[WARN] SELinux build failed; load on first boot"
# Apply KDE theme + DuckSans + os-release branding
bash $REPO/scripts/kde-theme-apply.sh
# Symlink display-manager.service → sddm.service. (Anaconda usually
# handles this when sddm is the only DM, but be explicit.)
ln -sf /usr/lib/systemd/system/sddm.service /etc/systemd/system/display-manager.service
# Real install boots straight to SDDM (NOT to TTY1 installer like live).
systemctl set-default graphical.target
# zram swap (no disk swap; keys never leak to platter)
cat > /etc/systemd/zram-generator.conf << 'EOF'
[zram0]
zram-size = min(ram, 8192)
compression-algorithm = zstd
EOF
# Enable services
systemctl enable veilor-firstboot.service 2>/dev/null || true
systemctl enable veilor-modules-lock.service 2>/dev/null || true
systemctl enable sshd fail2ban usbguard tuned auditd firewalld chronyd sddm
# Default tuned profile = balanced (AC/battery udev rule will override)
tuned-adm profile veilor-balanced 2>/dev/null || true
# Force admin to set a fresh password on first login. The `user` directive
# above already created the account with the user's chosen password —
# `chage -d 0` expires it so SDDM/passwd prompts for change immediately.
chage -d 0 admin 2>/dev/null || true
# Lock root explicitly (kickstart --lock should already do this)
passwd -l root
# Sanity: zero references to onyx / personal IPs in installed system
if grep -rqi 'onyx\|192\.168\.0\.\|fedora\.local' /etc/veilor* /etc/tuned/profiles/veilor-* 2>/dev/null; then
echo "[ERR] brand leak detected in /etc — investigate"
fi
echo "════════════════════════════════════════════════════════"
echo " veilor-os install complete"
echo "════════════════════════════════════════════════════════"
%end
# Reboot when done
reboot
KSEOF
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# Substitute placeholders. Use | as sed delimiter. validate_pw()
# already rejects "$\`&|/\n at input — sed_escape() is defence in
# depth in case future code paths feed unsanitised values (e.g.
# locale/hostname from a file, or a relaxed validator).
sed -i \
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
-e "s|__LOCALE__|$(sed_escape "$SEL_LOCALE")|" \
-e "s|__HOSTNAME__|$(sed_escape "$SEL_HOSTNAME")|" \
-e "s|__DISK_BASENAME__|$(sed_escape "$disk_basename")|" \
-e "s|__LUKS_PW__|$(sed_escape "$SEL_LUKS_PW")|" \
-e "s|__ADMIN_PW__|$(sed_escape "$SEL_ADMIN_PW")|" \
"$out"
echo "[INFO] generated kickstart at $out"
return 0
}
run_install() {
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
prompt_message "Installing veilor-os to $SEL_DISK ...
This will take 10-30 minutes.
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
Logs: /var/log/veilor-installer.log + /tmp/anaconda.log"
sleep 1
# Hand off to anaconda. --kickstart runs unattended.
# LANG / LC_ALL must be set — anaconda's keyboard.activate_keyboard()
# raises AnacondaError: 'LANG' if missing. tty1 inherits no locale by
# default. Use the locale the user picked (or fall back to en_GB).
export LANG="${SEL_LOCALE:-en_GB.UTF-8}"
export LC_ALL="$LANG"
# XDG_RUNTIME_DIR must exist — anaconda's display.setup_display()
# tries os.getenv("XDG_RUNTIME_DIR") + constants.WAYLAND_SOCKET_NAME
# and crashes with TypeError on None. We're running unattended so
# no graphical display is needed, but the env var still has to exist.
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
mkdir -p "$XDG_RUNTIME_DIR"
chmod 0700 "$XDG_RUNTIME_DIR"
# --cmdline: fully unattended text-only mode. No Wayland setup, no
# graphical/text TUI. Reads kickstart, executes, reboots. Right mode
# for our gum-driven flow where ks is fully self-contained.
if anaconda --cmdline --kickstart=/run/install/veilor-generated.ks; then
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
prompt_message "Install complete. System will reboot.
Remove the install media after shutdown."
sleep 3
systemctl reboot
else
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
prompt_error "Anaconda exited non-zero.
Logs at /tmp/anaconda.log + /var/log/veilor-installer.log.
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
Press OK to drop to shell."
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
}
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.
}
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
# ── Entry ──
# (require_tty already called above before exec redirect — see top of file)
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
banner
while true; do
case "$(main_menu)" in
"Install")
if collect_answers && generate_ks; then
run_install || continue
fi
;;
"live - (KDE)") launch_desktop ;;
"live - shell") drop_to_shell ;;
v0.5.1: gum installer + full veilor-os kickstart generation (#9) * v0.5.1: gum installer + full veilor-os ks generation Two changes, one commit (matches v0.5.1 milestone): 1. Swap whiptail → gum (charm.sh) - Source /usr/share/veilor-os/assets/installer/colors.gum at top so all prompts pick up branded GUM_* env vars. - Render banner.txt via `gum style --border rounded`. - Wrap every prompt behind prompt_choose / prompt_input / prompt_password / prompt_confirm / prompt_message / prompt_error helpers that dispatch gum→whiptail based on `command -v gum`. Defensive: minimal images without /usr/local/bin/gum still get a working TUI. - Main menu items now use literal labels (case-matched), not 1..5 tags. 2. Generated kickstart now installs full veilor-os Previously emitted a vanilla F43 KDE + ~12 hardening packages with no overlay/scripts/branding. Now mirrors live ks (kickstart/veilor-os.ks 63-141) for %packages, plus: - %post --nochroot copies overlay/, scripts/, assets/ from /run/install/repo/veilor (single source — boot ISO mount path). - %post (chroot) runs scripts/10-harden-base.sh, 20-harden-kernel.sh, selinux/build-policy.sh, kde-theme-apply.sh. - `chage -d 0 admin` so first login forces password change. (Account itself is created by anaconda from the `user` directive — admin pw collected via gum is passed through --plaintext.) - `systemctl set-default graphical.target` (real install boots SDDM, not the TTY1 installer like live). - Drops live-only entries (livesys-scripts, anaconda-live, dracut-live, isomd5sum, xorriso, livesys.service enables). Tested: bash -n clean; ksvalidator on a substituted-placeholder copy exits 0. gum binary itself (/usr/local/bin/gum) is vendored by a separate build-side change — not in this PR. * fix: escape sed special chars + reject & | / in passwords Reviewer found a password like aA1!@#%^&*()_-+={}[] becomes aA1!@#%^__ADMIN_PW__*()_-+={}[] because sed expands & to matched pattern. Two layers of defense: 1. validate_pw rejects & | / newline at input 2. sed_escape() helper escapes any remaining special chars before substitution --------- Co-authored-by: veilor-org <admin@veilor.org> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 04:39:27 +01:00
"Reboot") systemctl reboot ;;
"Power off") systemctl poweroff ;;
*) drop_to_shell ;;
esac
done