bigboy-alma-deploy/alma10-minimal-bigboy.ks

555 lines
No EOL
23 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#version=DEVEL
# AlmaLinux 10.2 Unattended Installation Kickstart — BigBoy Sovereign AI Server
#
# This kickstart automates Phase 1 (Base OS Installation & Foundation)
# Designed for 5-drive architecture: 1× NVMe (OS) + 4× SATA (data)
#
# Usage:
# USB method: Insert USB, boot, type: inst.ks=file:///ks.cfg
# HTTP method: Boot, type: inst.ks=http://<workbench_ip>:8000/alma10-minimal-bigboy.ks
#
# Reference: plannng/alma-phase1-install-workflow.md
#
# Checksum (validate before use):
# sha256sum alma10-minimal-bigboy.ks
# (Note: Update after final edits)
#
# BEFORE USING THIS FILE — fill in both placeholders below:
# <user-name> — your administrator account username
# <sha512-hash> — output of `openssl passwd -6`, run on any Linux box.
# NEVER put a plaintext password in this file — it is
# committed to git and lives in history permanently.
#
# ============================================================================
# ============================================================================
# INSTALLATION MODE & FIRST BOOT
# ============================================================================
# Use text mode installer (no GUI needed for headless server)
text
# Do not run Setup Agent on first boot (we handle via Ansible Phase 2)
firstboot --disable
# ============================================================================
# LOCALIZATION & SYSTEM CONFIGURATION
# ============================================================================
# Keyboard layout: US (standard for IT infrastructure)
keyboard --xlayouts='us'
# System language: English (UTF-8 for international support)
lang en_US.UTF-8
# System timezone: Europe/Rome UTC (CE headquarters timezone)
# Logs will use this timezone for consistency across deployment
timezone Europe/Rome --utc
# ============================================================================
# NETWORK CONFIGURATION
# ============================================================================
# Network: DHCP on primary interface (enp4s0)
# Will typically receive an address on the workbench bench LAN
# (192.168.0.0/24) during build/maintenance. Phase 13 later migrates
# BigBoy to the home LAN (192.168.1.0/24).
network --bootproto=dhcp --device=link --activate --hostname=bigboy --ipv6=off
# ============================================================================
# SECURITY & AUTHENTICATION
# ============================================================================
# Root password: LOCK (no password login allowed)
# All administrative access happens through the account below, plus sudo.
rootpw --lock
# ----------------------------------------------------------------------
# USER ACCOUNT (Administrator access)
# ----------------------------------------------------------------------
#
# Password login is enabled deliberately for initial bring-up — not every
# device John works from has an SSH key provisioned yet. This is an
# intentional, temporary loosening: Phase 9 (Security Hardening) disables
# PasswordAuthentication and moves to key-only, matching the same
# kickstart-lenient / Ansible-hardens pattern used for SELinux and
# firewalld elsewhere in this file. Don't remove password login without
# confirming a working key-based login first — until Phase 9 runs, this
# password is the only way in.
#
# The value below MUST be a hash, never a plaintext password — this file
# is committed to git and the value lives in history permanently even if
# changed later. Generate with:
# openssl passwd -6
# on any Linux box, then paste the resulting $6$... hash in place of
# <sha512-hash>.
user --name=<user-name> --groups=wheel --shell=/bin/bash --password=<sha512-hash> --iscrypted
# SELinux: Disabled for initial deployment (Phase 9 enables in Permissive)
# Allows us to monitor denials during first month without blocking services
selinux --disabled
# ----------------------------------------------------------------------
# FIREWALL: Enabled from first boot — LAN-only SSH access
# ----------------------------------------------------------------------
#
# Originally planned as disabled-until-Phase-9. That plan assumed
# key-only SSH from the start; since password auth is enabled above for
# initial bring-up, leaving the firewall off would mean password-auth SSH
# is reachable from anywhere that can route to this box — not acceptable
# even temporarily.
#
# firewalld starts enabled; the default zone carries no blanket SSH
# allowance. %post below adds rich rules restricting SSH specifically to
# the two known LAN ranges:
# 192.168.1.0/24 — home LAN (also covers WireGuard VPN clients, which
# Fritz bridges directly into this range — confirmed
# 2026-07-16, no separate VPN subnet exists)
# 192.168.0.0/24 — workbench bench LAN (BigBoy's build/maintenance
# network; also what's reached by devices whose
# home-LAN traffic workbench NATs onto the bench
# switch — arrives with workbench's bench-LAN address,
# already covered by this same range)
#
# No forwarded port 22 exists on the router, so this firewall rule is a
# second layer, not the only thing standing between BigBoy and the open
# internet — but it's the correct posture regardless, especially with
# password auth active.
#
# Phase 9 still applies its broader hardening pass on top of this
# baseline; that pass should also revoke the temporary password-login
# exception above, not just review the firewall further.
firewall --enabled
# ============================================================================
# REPOSITORY CONFIGURATION
# ============================================================================
# Base URL: AlmaLinux 10 official repositories
# Uses kickstart mirror for fastest package downloads during install
url --url="https://repo.almalinux.org/almalinux/10/BaseOS/x86_64/kickstart/"
# AppStream repository (applications, runtimes, development tools)
repo --name="almalinux10-appstream" --mirrorlist="https://mirrors.almalinux.org/mirrorlist/10/appstream"
# CodeReady Linux Builder (CRB, equivalent to AlmaLinux 8 PowerTools)
# Contains development packages needed for Phase 4 GPU driver compilation
repo --name="almalinux10-crb" --mirrorlist="https://mirrors.almalinux.org/mirrorlist/10/crb/"
# EPEL (Extra Packages for Enterprise Linux)
# Additional packages not in standard RHEL repos
repo --name="epel10" --mirrorlist="https://mirrors.fedoraproject.org/mirrorlist?repo=epel-10&arch=x86_64"
# NOTE: these repos require real outbound internet access during install,
# not just reachability within the bench LAN — confirm this path is open
# before booting, not after a stalled package download mid-install.
# ============================================================================
# BOOTLOADER CONFIGURATION
# ============================================================================
# Bootloader: UEFI (modern standard)
# Boot drive: NVMe (primary OS drive)
# Location: partition (for UEFI boot)
bootloader --location=partition --boot-drive=nvme0n1
# ============================================================================
# DISK PARTITIONING SCHEME
# ============================================================================
#
# Design:
# NVMe (500GB): EFI boot (1GB) + btrfs OS (~499GB)
# SATA Drive 1 (RAG): Single btrfs partition (mounted by Phase 3 Ansible)
# SATA Drive 2 (Prompt): Single btrfs partition (mounted by Phase 3 Ansible)
# SATA Drive 3 (Backup): Single btrfs partition (mounted by Phase 3 Ansible)
# SATA Drive 4 (AI Logs): Single btrfs partition (mounted by Phase 3 Ansible)
#
# Important: SATA partitions are created but NOT mounted during kickstart.
# Phase 3 Ansible role handles subvolume creation, mounting, and fstab.
#
# Drive identity confirmed 2026-07-16 via blkid labels prior to wipe:
# sda = BACKUPDRV, sdb = RAGLIB, sdc = AILOGS, sdd = PROMPTLIB
# (labels are destroyed by the wipe/reinstall — recorded here for
# provenance only; Phase 3 re-establishes them from group_vars/bigboy.yml)
# ============================================================================
# Do not erase existing partitions (safety measure)
clearpart --none --initlabel
# ============================================================================
# NVMe PARTITIONING (Primary OS drive)
# ============================================================================
# Partition 1: EFI System Partition
# Size: 1 GB (sufficient for kernel + bootloader)
# Filesystem: vfat (EFI standard)
# Mount: /boot/efi (handled by Anaconda)
part /boot/efi --fstype=efi --size=1024 --ondrive=nvme0n1
# Partition 2: OS Root (btrfs)
# Size: Grow to fill remaining NVMe space (~499 GB)
# Filesystem: btrfs (enables snapshots, compression, subvolumes)
# Mount: / (root filesystem)
# Note: Anaconda will create default btrfs layout; Phase 3 Ansible reconfigures
part / --fstype=btrfs --size=1 --grow --ondrive=nvme0n1
# ============================================================================
# SATA DRIVE PARTITIONING (Data drives — Phase 3 handles subvolumes)
# ============================================================================
# Note: Device naming in installer:
# Physical: /dev/nvme0n1 (NVMe), /dev/sda-/dev/sdd (SATA)
# Installer may refer to them differently; use physical names
#
# All SATA drives created as single btrfs partitions here.
# Phase 3 Ansible will:
# - Create subvolumes on each drive
# - Mount them at /srv/rag-library, /srv/prompt-library, etc.
# - Configure fstab with UUIDs (from HARDWARE.md)
# - Set compression and mount options
# SATA Drive 1 (sda): Backup partition — was labeled BACKUPDRV
# Will be mounted at /srv/backup in Phase 3
part /srv/backup-raw --fstype=btrfs --size=1 --grow --ondrive=sda
# SATA Drive 2 (sdb): RAG Library partition — was labeled RAGLIB
# Will be mounted at /srv/rag-library in Phase 3
part /srv/rag-raw --fstype=btrfs --size=1 --grow --ondrive=sdb
# SATA Drive 3 (sdc): AI Logs partition — was labeled AILOGS
# Will be mounted at /srv/ai-logs in Phase 3
part /srv/ai-raw --fstype=btrfs --size=1 --grow --ondrive=sdc
# SATA Drive 4 (sdd): Prompt Library partition — was labeled PROMPTLIB
# Will be mounted at /srv/prompt-library in Phase 3
part /srv/prompt-raw --fstype=btrfs --size=1 --grow --ondrive=sdd
# ============================================================================
# PACKAGE SELECTION
# ============================================================================
%packages
# ============================================================================
# CORE OS PACKAGES
# ============================================================================
# @core: Essential OS packages (required)
@core
# Kernel and headers (required for GPU driver compilation in Phase 4)
kernel
kernel-devel
kernel-headers
# UEFI bootloader and shim (required for secure boot compatibility)
grub2-efi-x64
shim-x64
efibootmgr
# ============================================================================
# BUILD ESSENTIALS (for Phase 4 NVIDIA driver installation)
# ============================================================================
# GCC compiler (required by NVIDIA driver kernel module compilation)
gcc
# Make build tool (required by NVIDIA driver Makefile)
make
# Patch utility (sometimes needed by driver post-install scripts)
patch
# Perl (sometimes used in driver installation scripts)
perl
# ============================================================================
# SYSTEM UTILITIES (minimal essential set)
# ============================================================================
# Networking and file transfer
curl
wget
# Text editors (vim for configuration editing)
vim
# Version control (git for CI/CD in future phases)
git
# Terminal multiplexer (tmux for Ansible session management)
tmux
# System monitoring (htop for real-time system observation)
htop
# ============================================================================
# SYSTEM ADMINISTRATION
# ============================================================================
# OpenSSH client and server (SSH access for Ansible Phase 2+)
openssh-clients
openssh-server
# Sudo (will be configured for Ansible non-root operations, Phase 2)
sudo
# ============================================================================
# STORAGE & MONITORING UTILITIES
# ============================================================================
# btrfs-progs: Tools for btrfs filesystem management (Phase 3, Phase 11)
btrfs-progs
# smartmontools: SMART disk health monitoring (Phase 11 thermal testing)
smartmontools
# util-linux: Standard Linux system utilities (mount, fdisk, etc.)
util-linux
# ============================================================================
# EXPLICITLY EXCLUDED PACKAGES (reduce footprint)
# ============================================================================
# Localization packages (not needed; en_US already specified)
-kde-l10n-*
-kde-l10n-common
# Network Manager GUI (not needed; CLI only)
-network-manager-applet
-nm-connection-editor
%end
# ============================================================================
# SERVICES CONFIGURATION
# ============================================================================
# Enabled services:
# - sshd: SSH daemon (required for Ansible Phase 2+)
# - NetworkManager: Network management daemon (handles DHCP, interfaces)
#
# Disabled services:
# - avahi-daemon: mDNS/Bonjour (not needed on server)
services --enabled=sshd,NetworkManager --disabled=avahi-daemon
# ============================================================================
# POST-INSTALLATION SCRIPT
# ============================================================================
#
# This script runs after package installation, before reboot.
# Handles kickstart-specific setup that Anaconda can't do automatically.
#
# Logs: Written to /root/anaconda-post.log (check if install fails)
# ============================================================================
%post --log=/root/anaconda-post.log
#!/bin/bash
# ============================================================================
# LOGGING INITIALIZATION (EU Sovereignty Policy)
# ============================================================================
# Create deployment log directory (used by all Ansible phases)
# Phase 0 (CE EU AI-Cloud Sovereignty Policy) requires structured logging
mkdir -p /srv/deployment-log
chmod 0755 /srv/deployment-log
# Log kickstart completion timestamp and system info
{
echo "=== Kickstart Installation Completed ==="
echo "Timestamp: $(date -Iseconds)"
echo "Hostname: $(hostname)"
echo "Kernel: $(uname -r)"
echo "AlmaLinux version: $(cat /etc/almalinux-release)"
echo ""
echo "Installed packages:"
rpm -qa | wc -l
echo ""
echo "Disk layout:"
lsblk
echo ""
echo "Network configuration:"
ip addr show enp4s0
echo ""
echo "Repositories:"
dnf repolist
} >> /srv/deployment-log/kickstart.log 2>&1
# ============================================================================
# GPU DRIVER PREPARATION (Phase 4 NVIDIA driver installation)
# ============================================================================
# Blacklist nouveau (open-source NVIDIA driver) before GPU driver install
# This prevents conflicts during Phase 4 NVIDIA proprietary driver installation
cat >> /etc/modprobe.d/blacklist-nouveau.conf << 'EOF'
# Blacklist nouveau to allow proprietary NVIDIA driver installation (Phase 4)
blacklist nouveau
options nouveau modeset=0
EOF
# Rebuild initramfs without nouveau module
# This ensures nouveau won't load on next boot
dracut --force 2>&1 >> /srv/deployment-log/kickstart.log
# ============================================================================
# SUDO BOOTSTRAP (temporary — narrow NOPASSWD exception)
# ============================================================================
#
# Phase 2 (Ansible) is what actually configures the real sudoers policy for
# the wheel group. But Phase 2 itself needs to run privileged commands to
# do that — and without this, there is no path to escalate privilege at
# all between first boot and Phase 2 completing. This drop-in is narrowly
# scoped to <user-name> only, clearly temporary, and is expected to be
# superseded (not just left in place) once Phase 2's real sudoers policy
# is applied. Verify Phase 2 actually replaces this rather than assuming
# it does.
cat > /etc/sudoers.d/00-bootstrap-<user-name> << 'EOF'
# Temporary bootstrap exception — superseded by Phase 2 Ansible sudoers policy.
# If you are reading this after Phase 2 has run, something did not get
# cleaned up correctly; check ansible/roles/phase-2-*/ for the real policy.
<user-name> ALL=(ALL) NOPASSWD: ALL
EOF
chmod 0440 /etc/sudoers.d/00-bootstrap-<user-name>
# ============================================================================
# SSH DAEMON SETUP (Foundation for Phase 2+ Ansible)
# ============================================================================
# Explicitly enable password authentication — don't rely on the distro
# default, which can vary and may already be hardened in the base image.
# Phase 9 flips this back to 'no' once key-based auth is confirmed working.
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
# Enable SSH daemon to start on boot
systemctl enable sshd
# Start SSH immediately (allows manual access if needed before Phase 2)
systemctl start sshd
# Restart to pick up the PasswordAuthentication change above
systemctl restart sshd
# Log SSH readiness
{
echo "SSH daemon enabled and started, password auth explicitly enabled"
systemctl status sshd | head -1
} >> /srv/deployment-log/kickstart.log 2>&1
# ============================================================================
# FIREWALL: Restrict SSH to known LAN ranges only
# ============================================================================
#
# firewalld is enabled (see FIREWALL block above) but the default zone has
# no blanket ssh service allowance — only these two specific source ranges
# can reach sshd at all. Anything outside 192.168.1.0/24 or 192.168.0.0/24
# is dropped by the zone default, before authentication is even attempted.
#
# firewall-cmd --permanent writes directly to the zone's XML config on
# disk; this works correctly here even though firewalld isn't actively
# running yet inside the install chroot — the rules take effect the
# moment firewalld starts on first real boot.
firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept'
firewall-cmd --permanent --zone=public \
--add-rich-rule='rule family="ipv4" source address="192.168.0.0/24" service name="ssh" accept'
{
echo "Firewall rich rules applied: SSH restricted to LAN ranges"
echo " Allowed: 192.168.1.0/24 (home LAN, incl. WireGuard VPN clients)"
echo " Allowed: 192.168.0.0/24 (workbench bench LAN)"
} >> /srv/deployment-log/kickstart.log 2>&1
# ============================================================================
# POST-SCRIPT COMPLETION LOG
# ============================================================================
{
echo ""
echo "=== Kickstart Post-Installation Complete ==="
echo "Timestamp: $(date -Iseconds)"
echo "Deployment directory: /srv/deployment-log/"
echo "Next phase: Ansible Phase 2 (System Configuration)"
} >> /srv/deployment-log/kickstart.log 2>&1
# Exit success
exit 0
%end
# ============================================================================
# KDUMP CONFIGURATION (disable for minimal footprint)
# ============================================================================
%addon com_redhat_kdump --disable
%end
# ============================================================================
# ANACONDA PASSWORD POLICY
# ============================================================================
#
# Note: Root password is locked (rootpw --locked above)
# These policies apply to the user account created during installation
# ============================================================================
%anaconda
# Root password policy (not applicable due to locked root)
# pwpolicy root --minlen=6 --minquality=50 --notstrict --nochanges --notempty
# User account policy — the account has a real (hashed) password per the
# USER ACCOUNT section above, so this is not left empty-ok as it was when
# no user account existed.
pwpolicy user --minlen=8 --minquality=50 --notstrict --nochanges --notempty
# LUKS encryption policy (if encrypted partitions created)
pwpolicy luks --minlen=6 --minquality=50 --notstrict --nochanges --notempty
%end
# ============================================================================
# INSTALLATION COMPLETION
# ============================================================================
# Reboot automatically after installation completes
# --eject: Attempt to eject installation media (USB) if possible
reboot --eject
# ============================================================================
# END OF KICKSTART FILE
# ============================================================================
#
# Verification checklist before use:
# [ ] Replaced <user-name> with the actual administrator username
# (appears in THREE places: user --name=, sudoers filename, sudoers
# file content — a find/replace across the whole file is safest)
# [ ] Replaced <sha512-hash> with the actual `openssl passwd -6` output
# [ ] NVMe device name is correct (nvme0n1)
# [ ] SATA device names are correct (sda, sdb, sdc, sdd)
# [ ] Network interface (enp4s0) matches hardware
# [ ] Hostname (bigboy) is correct
# [ ] Timezone (Europe/Rome) is correct
# [ ] Repositories are accessible (test with: curl <repo_url>)
# [ ] Real outbound internet access confirmed (not just bench LAN reachability)
# [ ] Post-install script has no syntax errors
#
# Expected outcome (Phase 1):
# - AlmaLinux 10.2 minimal installation
# - NVMe partitioned: EFI (1GB) + btrfs root (~499GB)
# - SATA drives partitioned: single btrfs partition each
# - <user-name> account created, wheel group, password login enabled
# - Temporary NOPASSWD sudo bootstrap in place for <user-name>
# - SSH daemon running, password auth explicitly enabled
# - Firewall enabled, SSH restricted to 192.168.1.0/24 and 192.168.0.0/24
# - /srv/deployment-log/ created and logged
# - Nouveau blacklisted, initramfs rebuilt
# - System reboots automatically
#
# Known temporary states this file creates, both closed out by Phase 9:
# 1. Password-based SSH login (Phase 9 → key-only)
# 2. Blanket NOPASSWD sudo bootstrap (Phase 9/Phase 2 → real sudoers policy)
#
# Next phase: Phase 2 (System Configuration & Secondary Drive Preparation)
# ============================================================================