Live ISO boot chain showing extra step: boot → text scroll → veilor-firstboot prompts admin pw → installer veilor-firstboot.service was enabled in live ks but it's an INSTALLED system feature (forces admin pw set on first real boot). Made no sense to ask on live (no persistent admin user, throwaway VM, etc). Live ks now: doesn't enable veilor-firstboot, masks the unit so overlay-copied unit file can't auto-activate. Install ks chroot %post already enables it (correct path). After fix: boot → text scroll → installer banner directly
294 lines
12 KiB
Text
294 lines
12 KiB
Text
#version=DEVEL
|
|
# veilor-os kickstart — Fedora 43 KDE base, hardened, minimal.
|
|
# Build with livemedia-creator inside build/Containerfile.
|
|
|
|
# ── Install source ──
|
|
# Hard-code version (not $releasever) because lorax doesn't expand
|
|
# inside kickstart `url`/`repo` directives. Updates repo critical:
|
|
# base Fedora 43 ships selinux-policy 42.12 with pcre2-10.47-built
|
|
# file_contexts.bin, which fails chroot %triggerin against host's
|
|
# libselinux (built against pcre2 10.46). 43.7 in updates is rebuilt.
|
|
url --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=fedora-43&arch=x86_64"
|
|
# Explicit `repo --name=fedora` lets livecd-creator see base repo (it only
|
|
# reads repo.repoList, ignores url= directive). livemedia-creator + Anaconda
|
|
# honor both. No behavior change for either tool.
|
|
# Use direct baseurl (kernel.org mirror) to avoid mirrorlist 404s during
|
|
# Fedora's metadata sync windows.
|
|
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
|
|
# Local fix-repo: build-time-only workaround for host pcre2/libselinux skew.
|
|
# Stripped from CI ks via sed in build-iso.yml. NOT shipped state.
|
|
repo --name=veilor-fix --baseurl=file:///tmp/veilor-fix-repo --install --cost=1
|
|
|
|
# ── Locale / keyboard / time (template — adjust per build) ──
|
|
keyboard --xlayouts='us'
|
|
lang en_GB.UTF-8
|
|
timezone Europe/London --utc
|
|
|
|
# ── Install mode ──
|
|
# Note: no display mode (text/graphical/cmdline) — livemedia-creator forbids.
|
|
firstboot --disable
|
|
eula --agreed
|
|
# Build-time SELinux disabled to avoid PCRE2 regex version mismatch between
|
|
# host libselinux and chroot's selinux-policy file_contexts.bin (pcre2 10.46
|
|
# vs 10.47). veilor-firstboot.service triggers `fixfiles -F onboot` and
|
|
# `setenforce 1` on first boot to re-enable enforcing mode.
|
|
selinux --permissive
|
|
# veilor-firstboot + veilor-modules-lock enabled via %post after overlay
|
|
# copy (units don't exist yet at services-config phase).
|
|
services --enabled=sshd,fail2ban,usbguard,tuned,auditd,firewalld,chronyd,sddm
|
|
|
|
# ── Network / hostname ──
|
|
network --bootproto=dhcp --device=link --activate --hostname=veilor-os
|
|
firewall --enabled --service=ssh
|
|
|
|
# ── Identity (zero-prompt; only LUKS passphrase asked at install) ──
|
|
# Note: `auth` command removed in pykickstart 3.x — defaults (sha512 shadow) apply.
|
|
rootpw --lock
|
|
user --name=admin --groups=wheel --gecos="veilor admin" --password="" --plaintext
|
|
|
|
# ── Bootloader: kernel hardening flags ──
|
|
# Note: init_on_alloc/init_on_free removed from default live cmdline —
|
|
# they zero every memory page at boot which 5x'd KVM live boot time.
|
|
# Re-enable per-install via veilor-firstboot.service for production.
|
|
bootloader --location=mbr --append="lockdown=integrity slab_nomerge randomize_kstack_offset=on vsyscall=none plymouth.enable=0"
|
|
|
|
# ── Live ISO partitioning (flat — for live rootfs build only) ──
|
|
# NOTE: This is the *live* image kickstart. Final installed system uses
|
|
# a separate installer kickstart (kickstart/install.ks, planned v0.2.1)
|
|
# that does LUKS2 + btrfs subvols on the target disk.
|
|
part / --fstype=ext4 --size=8192
|
|
|
|
# ── Packages ──
|
|
%packages --excludedocs
|
|
@^kde-desktop-environment
|
|
@kde-apps
|
|
@core
|
|
@hardware-support
|
|
@standard
|
|
|
|
# live install plumbing (required by livemedia-creator --make-iso)
|
|
# CRITICAL: livesys-scripts + anaconda-live ship the systemd units lorax expects
|
|
# at squashfs creation. Without them, EFI/BOOT not built and ISO wrap fails.
|
|
# (Upstream Fedora's fedora-live-kde.ks includes these via fedora-live-base.ks.)
|
|
livesys-scripts
|
|
anaconda-live
|
|
@anaconda-tools
|
|
kernel-modules
|
|
kernel-modules-extra
|
|
glibc-all-langpacks
|
|
dracut-live
|
|
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
|
|
isomd5sum
|
|
xorriso
|
|
|
|
# veilor-installer dependencies (TTY1 TUI installer wrapping anaconda)
|
|
newt
|
|
parted
|
|
cryptsetup
|
|
lvm2
|
|
btrfs-progs
|
|
|
|
|
|
# core hardening tools
|
|
fail2ban
|
|
fail2ban-firewalld
|
|
usbguard
|
|
usbguard-tools
|
|
audit
|
|
policycoreutils-python-utils
|
|
tuned
|
|
chrony
|
|
firewalld
|
|
plymouth
|
|
|
|
# admin essentials
|
|
git
|
|
vim-enhanced
|
|
tmux
|
|
htop
|
|
podman
|
|
skopeo
|
|
NetworkManager
|
|
NetworkManager-wifi
|
|
|
|
# fonts
|
|
fontconfig
|
|
freetype
|
|
fira-code-fonts
|
|
|
|
# remove fluff
|
|
# Note: KDE Plasma 6 hard-deps on cups/geoclue2/ModemManager/PackageKit
|
|
# transitively (plasma-print-manager, xdg-desktop-portal, NM-wwan etc),
|
|
# so package removal breaks depsolve. Daemons disabled at runtime via
|
|
# scripts/20-harden-kernel.sh instead.
|
|
-abrt*
|
|
-snapd
|
|
-kde-connect
|
|
-open-vm-tools-desktop
|
|
-mlocate
|
|
|
|
%end
|
|
|
|
# ── Post-install (nochroot): copy overlay tree into installed root ──
|
|
%post --nochroot --erroronfail
|
|
set -uo pipefail
|
|
# DEST: livecd-creator sets INSTALL_ROOT, livemedia-creator uses /mnt/sysimage.
|
|
DEST="${INSTALL_ROOT:-/mnt/sysimage}"
|
|
[[ -d $DEST ]] || { echo "[ERR] DEST=$DEST does not exist (livecd-creator? livemedia-creator?)" >&2; exit 1; }
|
|
|
|
# Try multiple source paths:
|
|
# /run/install/repo/veilor — boot ISO (--virt mode)
|
|
# /work — bind mount in CI container
|
|
# $(dirname kickstart)/.. — local --no-virt builds
|
|
SRC=""
|
|
for candidate in /run/install/repo/veilor /work /mnt/work; do
|
|
if [[ -d $candidate/overlay ]]; then
|
|
SRC=$candidate
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Fallback: derive from kickstart path. Anaconda passes ks via --kickstart=<path>.
|
|
if [[ -z $SRC ]]; then
|
|
KS_PATH=$(ps -ef | grep -oP -- '--kickstart[= ]\K[^ ]+' | head -1)
|
|
if [[ -n $KS_PATH && -d $(dirname "$KS_PATH")/../overlay ]]; then
|
|
SRC=$(realpath "$(dirname "$KS_PATH")/..")
|
|
fi
|
|
fi
|
|
|
|
if [[ -z $SRC ]]; then
|
|
echo "[ERR] cannot locate veilor-os repo source — overlay/scripts not copied" >&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: $?"
|
|
ls -la "$SRC/assets" "$SRC/scripts" 2>&1 || echo "[ERR] assets/scripts missing in $SRC"
|
|
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"
|
|
set +x
|
|
|
|
# Persist nochroot log into installed system for diagnostics
|
|
{
|
|
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
|
|
} > "$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"
|
|
echo "════════════════════════════════════════════════════════"
|
|
|
|
REPO=/usr/share/veilor-os
|
|
chmod +x $REPO/scripts/*.sh $REPO/scripts/selinux/*.sh /usr/local/bin/veilor-power /usr/local/bin/veilor-update /usr/local/bin/veilor-doctor /usr/local/bin/veilor-firstboot /usr/local/bin/veilor-installer
|
|
|
|
# Live image plumbing (matches upstream Fedora live ks). Without these the
|
|
# squashfs/EFI build fails — livesys-scripts ships systemd units lorax expects.
|
|
systemctl enable livesys.service livesys-late.service 2>/dev/null || true
|
|
systemctl enable tmp.mount 2>/dev/null || true
|
|
|
|
# /etc/machine-id reset on first boot (live image baseline)
|
|
> /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
|
|
bash $REPO/scripts/30-apply-v03-theme.sh || echo "[WARN] v03-theme apply failed"
|
|
|
|
# Force admin password set on first boot.
|
|
# livecd-creator does NOT honor `user` kickstart directive (it's a LIVE
|
|
# image, no installer step). Create admin manually in chroot %post.
|
|
# Note: SDDM rejects blank passwords by default (PAM nullok off), so we
|
|
# set throwaway pw `veilor` + chage -d 0 to force reset on first login.
|
|
if ! getent passwd admin >/dev/null; then
|
|
useradd -m -G wheel -s /bin/bash -c "veilor admin" admin
|
|
echo 'admin:veilor' | chpasswd
|
|
chage -d 0 admin
|
|
echo "[INFO] admin user created (default pw=veilor, expired)"
|
|
fi
|
|
|
|
# Symlink display-manager.service → sddm.service. graphical.target Wants=
|
|
# display-manager but the alias doesn't get auto-created when sddm package
|
|
# is installed via livecd-creator (vs Anaconda installer which handles it).
|
|
# Without this, sddm stays inactive even though enabled.
|
|
ln -sf /usr/lib/systemd/system/sddm.service /etc/systemd/system/display-manager.service
|
|
|
|
# Live ISO default target: multi-user (TTY1 = veilor-installer TUI lands first).
|
|
# User picks "Try live — desktop" from menu → systemctl isolate graphical.target.
|
|
# Real installs land on graphical.target by default (set by anaconda).
|
|
systemctl set-default multi-user.target
|
|
|
|
# Branding: GRUB menu title + plymouth `details` text theme (no graphical
|
|
# splash). Pure text-scroll boot exposes the gum installer immediately on
|
|
# tty1 instead of plymouth swallowing it.
|
|
sed -i \
|
|
-e 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="veilor-os"|' \
|
|
-e 's|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=""|' \
|
|
/etc/default/grub 2>/dev/null || true
|
|
plymouth-set-default-theme details 2>/dev/null || true
|
|
[ -f /boot/grub2/grub.cfg ] && grub2-mkconfig -o /boot/grub2/grub.cfg 2>/dev/null || true
|
|
|
|
# zram swap (no disk swap; keys never leak to platter)
|
|
dnf install -y zram-generator || true
|
|
cat > /etc/systemd/zram-generator.conf << 'EOF'
|
|
[zram0]
|
|
zram-size = min(ram, 8192)
|
|
compression-algorithm = zstd
|
|
EOF
|
|
|
|
# Enable services
|
|
# veilor-firstboot.service NOT enabled on live ISO — it prompts admin pw
|
|
# which makes no sense on a live boot. Real installs enable it in their
|
|
# generated kickstart's chroot %post (see overlay/usr/local/bin/veilor-installer).
|
|
systemctl enable veilor-modules-lock.service
|
|
systemctl enable sshd fail2ban usbguard tuned auditd firewalld chronyd
|
|
# Mask veilor-firstboot on live so even if it landed in /etc/systemd/system
|
|
# (overlay drag), it can't activate.
|
|
systemctl mask veilor-firstboot.service 2>/dev/null || true
|
|
|
|
# Default tuned profile = balanced (AC/battery udev rule will override)
|
|
tuned-adm profile veilor-balanced 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
|