Compare commits
No commits in common. "8cc60e3daefa8ce32c56e839a41dc30783d36ec5" and "667453596a1f20bd81e80f838b2242348bdde9f4" have entirely different histories.
8cc60e3dae
...
667453596a
203
README.md
203
README.md
@ -1,176 +1,59 @@
|
||||
# Void Bootstrapp - Vollständiger CLI-Installer
|
||||
# Void Bootstrapp Wrapper
|
||||
|
||||
Ein vollautomatischer Bash-Installer für verschlüsselte Void Linux Installationen.
|
||||
Ein Bash-Wrapper, der die kritischen Vorarbeiten fuer eine verschluesselte Void-Linux-Installation erledigt und danach den offiziellen `void-installer` nutzt.
|
||||
|
||||
## Überblick
|
||||
## Ablauf
|
||||
|
||||
Dieser Installer automatisiert die komplette Installation von Void Linux mit LUKS2-Verschlüsselung, btrfs/ext4-Dateisystem und allen erforderlichen System-Konfigurationen - **ohne manuelle Eingriffe**.
|
||||
1. Sanity Checks (Root, UEFI, Zielplatte)
|
||||
2. Partitionierung (GPT, ESP + Root)
|
||||
3. LUKS-Verschluesselung und Mapping
|
||||
4. Dateisysteme (btrfs oder ext4)
|
||||
5. Mounts fuer den Installer
|
||||
6. Installer-Handoff (manuelle Mountpoints, kein Reformat)
|
||||
7. Post-Install (crypttab, dracut, GRUB, optional Swapfile)
|
||||
|
||||
**Was ist neu (v2.0):**
|
||||
- Vollständig automatisiert - keine manuelle void-installer Navigation mehr
|
||||
- Package-Installation (Base-System, Kernel, Tools)
|
||||
- Locale/Timezone/Keyboard-Konfiguration
|
||||
- User-Management (Root + Standard-User mit sudo)
|
||||
- Service-Aktivierung (dhcpcd für Netzwerk)
|
||||
|
||||
## Installation-Workflow
|
||||
|
||||
Der Installer führt folgende Phasen automatisch aus:
|
||||
|
||||
1. **Sanity Checks** - Root-Rechte, UEFI-Modus, Zielplatte validieren
|
||||
2. **Konfiguration** - Interaktive Eingabe aller Parameter
|
||||
3. **Partitionierung** - GPT-Layout (ESP + Root-Partition)
|
||||
4. **Verschlüsselung** - LUKS2/LUKS1 auf Root-Partition
|
||||
5. **Dateisysteme** - btrfs (mit Subvolumes) oder ext4
|
||||
6. **Mounts** - Alle Dateisysteme mounten
|
||||
7. **Packages** - Base-System, Kernel, Network-Tools installieren
|
||||
8. **Locale** - System-Sprache, Timezone, Keyboard konfigurieren
|
||||
9. **Users** - Root-Passwort + Standard-User mit sudo anlegen
|
||||
10. **Services** - dhcpcd (Netzwerk) aktivieren
|
||||
11. **Post-Install** - initramfs, GRUB, crypttab, Swap-File
|
||||
|
||||
## Verwendung
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
sudo ./src/main.sh [--dry-run]
|
||||
sudo ./src/main.sh [--dry-run] [--skip-installer]
|
||||
```
|
||||
|
||||
### Flags
|
||||
## Flags
|
||||
|
||||
- `--dry-run`: Zeigt die Konfiguration und bricht ohne Änderungen ab
|
||||
- `--dry-run`: Zeigt die Konfiguration und bricht ohne Aenderungen ab.
|
||||
- `--skip-installer`: Laesst den Start von `void-installer` aus (manuell starten).
|
||||
|
||||
## Konfiguration
|
||||
## Installer Bedienung nach dem Script
|
||||
|
||||
Während der Installation werden folgende Parameter abgefragt:
|
||||
Im `void-installer` musst du die vorbereiteten Mounts uebernehmen und darfst nichts formatieren.
|
||||
|
||||
### Disk-Konfiguration
|
||||
- **Target Disk**: Zielplatte (z.B. `/dev/sda`)
|
||||
- **Hostname**: System-Hostname
|
||||
- **Filesystem**: `btrfs` (Standard) oder `ext4`
|
||||
- **LUKS Version**: `2` (Standard) oder `1` (für ältere GRUB-Versionen)
|
||||
- **ESP Size**: EFI System Partition Größe (Standard: `1GiB`)
|
||||
- **Root End**: Ende der Root-Partition (Standard: `100%`)
|
||||
- **Swap Size**: Swap-File Größe (Standard: `4GiB`, `0` zum Deaktivieren)
|
||||
1. Waehle die manuelle Partitionierung.
|
||||
2. Setze nur die Mountpoints, ohne Formatierung.
|
||||
3. Root:
|
||||
- Device: `/dev/mapper/cryptroot`
|
||||
- Label: `void-root` (btrfs und ext4)
|
||||
- Mountpoint: `/`
|
||||
- Format: aus
|
||||
4. ESP:
|
||||
- Device: erste Partition der Disk (meist `...1`)
|
||||
- Label: `EFI`
|
||||
- Mountpoint: `/boot/efi`
|
||||
- Format: aus
|
||||
|
||||
### Locale-Konfiguration
|
||||
- **Locale**: System-Sprache (Standard: `en_US.UTF-8`)
|
||||
- **Timezone**: Zeitzone (Standard: `UTC`, z.B. `Europe/Berlin`)
|
||||
- **Keyboard**: Console-Keyboard-Layout (Standard: `us`, z.B. `de-latin1`)
|
||||
|
||||
### User-Konfiguration
|
||||
- **Username**: Name des Standard-Benutzers (erforderlich)
|
||||
- **Root-Passwort**: Wird interaktiv gesetzt
|
||||
- **User-Passwort**: Wird interaktiv gesetzt
|
||||
|
||||
Der Standard-User wird automatisch folgenden Gruppen zugeordnet:
|
||||
- `wheel` - sudo-Zugang
|
||||
- `audio` - Audio-Geräte
|
||||
- `video` - Video-Geräte
|
||||
- `storage` - Speicher-Geräte
|
||||
- `network` - Netzwerk-Konfiguration
|
||||
|
||||
## Btrfs Subvolumes
|
||||
|
||||
Bei Auswahl von btrfs werden automatisch folgende Subvolumes angelegt:
|
||||
|
||||
- `@` → `/` (Root)
|
||||
- `@home` → `/home` (Benutzer-Daten)
|
||||
- `@var` → `/var` (Variable Daten)
|
||||
- `@log` → `/var/log` (System-Logs)
|
||||
- `@snapshots` → `/.snapshots` (Snapshot-Speicher)
|
||||
- `@swap` → `/swap` (Swap-File Container)
|
||||
|
||||
## Package-Liste
|
||||
|
||||
Der Installer installiert standardmäßig folgende Pakete:
|
||||
|
||||
**Base-System:**
|
||||
- `base-system` - Essential base packages
|
||||
- `linux` - Linux Kernel
|
||||
- `linux-firmware` - Hardware-Firmware
|
||||
- `grub` - Bootloader
|
||||
- `cryptsetup` - LUKS-Tools
|
||||
- `dracut` - initramfs Generator
|
||||
|
||||
**Netzwerk:**
|
||||
- `dhcpcd` - DHCP-Client
|
||||
- `iproute2` - IP-Konfiguration
|
||||
- `iputils` - ping, traceroute
|
||||
|
||||
**System-Tools:**
|
||||
- `vim` - Text-Editor
|
||||
- `nano` - Alternative Editor
|
||||
- `sudo` - Privilege Escalation
|
||||
|
||||
## Nach der Installation
|
||||
|
||||
Nach erfolgreichem Abschluss:
|
||||
|
||||
1. System neustarten: `reboot`
|
||||
2. LUKS-Passphrase beim Boot eingeben
|
||||
3. Mit Standard-User einloggen
|
||||
4. Netzwerk ist via dhcpcd automatisch verfügbar
|
||||
5. `sudo` funktioniert für wheel-Gruppen-Mitglieder
|
||||
|
||||
## Systemanforderungen
|
||||
|
||||
- **Boot-Modus**: UEFI (BIOS wird nicht unterstützt)
|
||||
- **Architektur**: x86_64
|
||||
- **Live-Medium**: Void Linux Live-ISO
|
||||
- **Netzwerk**: Für Package-Download erforderlich
|
||||
Wenn Labels nicht angezeigt werden, pruefe mit `lsblk -f`, welches Device `void-root` bzw. `EFI` traegt.
|
||||
Bei btrfs sind Subvolumes bereits angelegt und gemountet: `@`, `@home`, `@var`, `@log`, `@snapshots`, `@swap`.
|
||||
Der Installer soll keine neuen Subvolumes anlegen oder formatieren, sondern die vorhandenen Mounts unveraendert lassen.
|
||||
Es reicht, im Installer nur `/` und `/boot/efi` zu setzen; die Subvolumes werden im Post-Install in `/etc/fstab` eingetragen.
|
||||
Mapping der Subvolumes (gesetzt durch den Wrapper/Post-Install):
|
||||
- `@` -> `/`
|
||||
- `@home` -> `/home`
|
||||
- `@var` -> `/var`
|
||||
- `@log` -> `/var/log`
|
||||
- `@snapshots` -> `/.snapshots`
|
||||
- `@swap` -> `/swap`
|
||||
|
||||
## Hinweise
|
||||
|
||||
- **Destruktiv**: Der Installer löscht die Zielplatte nach expliziter Bestätigung
|
||||
- **Interaktiv**: Passwörter werden aus Sicherheitsgründen interaktiv abgefragt
|
||||
- **Logs**: Unter `/tmp/void-wrapper-YYYY-MM-DD-HHMMSS.log`
|
||||
|
||||
## Fehlerbehebung
|
||||
|
||||
Bei Fehlern während der Installation:
|
||||
|
||||
1. **Fehler in Packages-Phase**: Netzwerk-Konnektivität und Mirror-Status prüfen
|
||||
2. **Fehler in Users-Phase**: Passwort-Eingabe wiederholen
|
||||
3. **Rollback angeboten**: Bei Fehler werden Mounts und LUKS-Mappings automatisch aufgeräumt
|
||||
|
||||
Für detaillierte Fehleranalyse siehe Log-Datei.
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Struktur
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.sh # Orchestrierung aller Phasen
|
||||
├── logging.sh # Logging-Funktionen
|
||||
├── config.sh # Konfigurations-Prompts und Validierung
|
||||
├── sanity.sh # System-Checks
|
||||
├── partitioning.sh # GPT-Partitionierung
|
||||
├── encryption.sh # LUKS-Setup
|
||||
├── filesystems.sh # Dateisystem-Formatierung
|
||||
├── mounts.sh # Mount-Management
|
||||
├── packages.sh # Package-Installation
|
||||
├── locale.sh # Locale/Timezone/Keyboard
|
||||
├── users.sh # User-Management
|
||||
├── services.sh # Service-Aktivierung
|
||||
├── postinstall.sh # initramfs, GRUB, crypttab
|
||||
└── rollback.sh # Cleanup bei Fehlern
|
||||
```
|
||||
|
||||
### Architektur-Prinzipien
|
||||
|
||||
- **Narrative Documentation**: Jedes Modul dokumentiert Motivation, Decisions, Alternatives
|
||||
- **Modulare Phasen**: Jede Phase ist eigenständig und testbar
|
||||
- **Error Handling**: `set -euo pipefail` + trap-basiertes Rollback
|
||||
- **Logging**: Alle Operationen werden geloggt
|
||||
|
||||
## Zukünftige Erweiterungen
|
||||
|
||||
- Config-File Support (JSON) für unattended Installations
|
||||
- Desktop-Environment Auswahl (XFCE, KDE, GNOME)
|
||||
- Zusätzliche Service-Optionen (sshd, chronyd)
|
||||
- Checkpoint-System für Phase-Wiederaufnahme
|
||||
|
||||
## Lizenz
|
||||
|
||||
Dieses Projekt ist für Void Linux Installationen optimiert und folgt den Best Practices der Void Linux Dokumentation.
|
||||
- Der Wrapper ist destruktiv und loescht die Zielplatte nach expliziter Bestaetigung.
|
||||
- UEFI ist Pflicht; BIOS-Installationen sind nicht unterstuetzt.
|
||||
- Logs landen unter `/tmp/void-wrapper-YYYY-MM-DD-HHMMSS.log`.
|
||||
|
||||
@ -53,14 +53,6 @@ export ESP_MOUNT="/mnt/boot/efi"
|
||||
export ROOT_LABEL="void-root"
|
||||
export EFI_LABEL="EFI"
|
||||
|
||||
# Locale and user configuration
|
||||
export LOCALE="en_US.UTF-8"
|
||||
export TIMEZONE="UTC"
|
||||
export KEYBOARD="us"
|
||||
export USERNAME=""
|
||||
export USER_SHELL="/bin/bash"
|
||||
export USER_GROUPS="wheel,audio,video,storage,network"
|
||||
|
||||
validate_size() {
|
||||
local size="$1"
|
||||
# Allow sizes like: 1GiB, 100MB, 50%, 100%
|
||||
@ -142,12 +134,6 @@ config_prompt_interactive() {
|
||||
prompt_with_default ESP_SIZE "ESP size (e.g. 1GiB)" "${ESP_SIZE}"
|
||||
prompt_with_default ROOT_END "Root partition end (e.g. 100% or 200GiB)" "${ROOT_END}"
|
||||
prompt_with_default SWAP_SIZE "Swap size (e.g. 4GiB, 0 to disable)" "${SWAP_SIZE}"
|
||||
|
||||
# Locale and user configuration
|
||||
prompt_with_default LOCALE "Locale (e.g. en_US.UTF-8, de_DE.UTF-8)" "${LOCALE}"
|
||||
prompt_with_default TIMEZONE "Timezone (e.g. UTC, Europe/Berlin, America/New_York)" "${TIMEZONE}"
|
||||
prompt_with_default KEYBOARD "Keyboard layout (e.g. us, de-latin1, fr)" "${KEYBOARD}"
|
||||
prompt_required USERNAME "Username for standard user" "${USERNAME}"
|
||||
}
|
||||
|
||||
config_validate() {
|
||||
@ -157,9 +143,6 @@ config_validate() {
|
||||
if [[ -z "$HOSTNAME" ]]; then
|
||||
die "Hostname is required."
|
||||
fi
|
||||
if [[ -z "$USERNAME" ]]; then
|
||||
die "Username is required."
|
||||
fi
|
||||
|
||||
case "$FS_TYPE" in
|
||||
btrfs|ext4) ;;
|
||||
@ -186,16 +169,6 @@ config_validate() {
|
||||
if [[ "$SWAP_SIZE" != "0" ]] && ! validate_size "$SWAP_SIZE"; then
|
||||
die "Invalid swap size format: $SWAP_SIZE (expected: 4GiB, 8GB, 0 to disable)"
|
||||
fi
|
||||
|
||||
# Validate locale format (basic check)
|
||||
if [[ ! "$LOCALE" =~ ^[a-z]{2}_[A-Z]{2}\.(UTF-8|utf8)$ ]]; then
|
||||
log_warn "Locale format may be invalid: $LOCALE (expected: en_US.UTF-8)"
|
||||
fi
|
||||
|
||||
# Validate username (alphanumeric, underscore, hyphen)
|
||||
if [[ ! "$USERNAME" =~ ^[a-z_][a-z0-9_-]*$ ]]; then
|
||||
die "Invalid username: $USERNAME (must start with lowercase letter or underscore, contain only lowercase alphanumeric, underscore, hyphen)"
|
||||
fi
|
||||
}
|
||||
|
||||
config_print_summary() {
|
||||
@ -205,7 +178,6 @@ config_print_summary() {
|
||||
: "${LUKS_VERSION:?LUKS version is required}"
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
: "${ESP_MOUNT:?ESP mount path is required}"
|
||||
: "${USERNAME:?Username is required}"
|
||||
log_info "Configuration summary:"
|
||||
log_info " Disk: $DISK"
|
||||
log_info " Hostname: $HOSTNAME"
|
||||
@ -216,10 +188,6 @@ config_print_summary() {
|
||||
log_info " Swap size: ${SWAP_SIZE}"
|
||||
log_info " Mount root: $MOUNT_ROOT"
|
||||
log_info " ESP mount: $ESP_MOUNT"
|
||||
log_info " Locale: $LOCALE"
|
||||
log_info " Timezone: $TIMEZONE"
|
||||
log_info " Keyboard: $KEYBOARD"
|
||||
log_info " Username: $USERNAME"
|
||||
}
|
||||
|
||||
config_confirm_destructive() {
|
||||
|
||||
71
src/installer.sh
Normal file
71
src/installer.sh
Normal file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# === Motivation ===
|
||||
# Keep the official installer as the configuration authority.
|
||||
|
||||
# === Problem Statement ===
|
||||
# We need a clean handoff so the installer uses existing mounts without reformatting.
|
||||
|
||||
# === Scope ===
|
||||
# In scope: instructions and guardrails for the user during the installer run.
|
||||
# Out of scope: automated installer configuration.
|
||||
|
||||
# === Concepts ===
|
||||
# Handoff: a pause where the wrapper delegates to the installer.
|
||||
|
||||
# === Decisions ===
|
||||
# Provide clear, minimal guidance to avoid overriding prepared filesystems.
|
||||
# Support only CLI installer flow to keep guidance consistent.
|
||||
|
||||
# === Alternatives Considered ===
|
||||
# Fully scripted installation rejected for this phase.
|
||||
|
||||
# === Constraints ===
|
||||
# The wrapper must not hide or alter installer behavior.
|
||||
|
||||
# === Open Questions ===
|
||||
# Should we provide a checklist or step-by-step guide during the installer handoff?
|
||||
# How do we detect if the installer reformatted filesystems against our intent?
|
||||
# Should we monitor the installer process, or fully delegate control?
|
||||
|
||||
# === Success Criteria ===
|
||||
# The installer completes using the prepared mounts without reformatting.
|
||||
|
||||
run_installer() {
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
: "${ESP_MOUNT:?ESP mount path is required}"
|
||||
if ! findmnt "$MOUNT_ROOT" >/dev/null 2>&1; then
|
||||
die "Mount root not found: $MOUNT_ROOT"
|
||||
fi
|
||||
if ! findmnt "$ESP_MOUNT" >/dev/null 2>&1; then
|
||||
die "ESP mount not found: $ESP_MOUNT"
|
||||
fi
|
||||
|
||||
log_info "Installer handoff: use the prepared mounts without formatting."
|
||||
log_info "In the installer: choose manual partitioning and only set mountpoints."
|
||||
|
||||
if [[ "${SKIP_INSTALLER:-0}" -eq 1 ]]; then
|
||||
log_warn "Skipping installer per request."
|
||||
return 0
|
||||
fi
|
||||
|
||||
read -r -p "Press Enter to launch void-installer..." _
|
||||
|
||||
if command -v void-installer >/dev/null 2>&1; then
|
||||
void-installer
|
||||
else
|
||||
log_warn "void-installer not found. Run it manually in another shell."
|
||||
fi
|
||||
|
||||
read -r -p "Press Enter once the installer is complete..." _
|
||||
|
||||
# Verify critical mounts are still intact after installer
|
||||
if ! findmnt "$MOUNT_ROOT" >/dev/null 2>&1; then
|
||||
die "Root mount disappeared after installer. The installer may have reformatted the filesystem."
|
||||
fi
|
||||
if ! findmnt "$ESP_MOUNT" >/dev/null 2>&1; then
|
||||
die "ESP mount disappeared after installer. The installer may have reformatted the filesystem."
|
||||
fi
|
||||
|
||||
log_info "Mount verification passed."
|
||||
}
|
||||
117
src/locale.sh
117
src/locale.sh
@ -1,117 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# === Motivation ===
|
||||
# Configure system locale, timezone, and keyboard layout for international users.
|
||||
|
||||
# === Problem Statement ===
|
||||
# A freshly installed system needs localization configuration to support user language and region.
|
||||
|
||||
# === Scope ===
|
||||
# In scope: locale generation, timezone configuration, keyboard layout.
|
||||
# Out of scope: X11 keyboard configuration, font selection, language packs.
|
||||
|
||||
# === Concepts ===
|
||||
# Locale: Defines language, character encoding, and formatting conventions (LC_*).
|
||||
# Timezone: Maps local time to UTC with regional rules (DST, etc.).
|
||||
# Keyboard layout: Console keymap for character input.
|
||||
|
||||
# === Decisions ===
|
||||
# Default locale is en_US.UTF-8 for broadest compatibility.
|
||||
# Default timezone will be configured based on user prompt (see config.sh).
|
||||
# Default keyboard layout is 'us' (can be overridden via config).
|
||||
# Use /etc/default/libc-locales for locale configuration (Void standard).
|
||||
# Use symlink for timezone (/etc/localtime → /usr/share/zoneinfo/<zone>).
|
||||
# Use /etc/rc.conf for console keyboard layout (KEYMAP variable).
|
||||
|
||||
# === Alternatives Considered ===
|
||||
# systemd-localed rejected (Void uses runit).
|
||||
# Generating all locales rejected (wastes disk space and time).
|
||||
# Auto-detection of timezone rejected (unreliable without network/GeoIP).
|
||||
|
||||
# === Constraints ===
|
||||
# Target system must be mounted at $MOUNT_ROOT.
|
||||
# Locale must be generated before it can be used.
|
||||
# Timezone must exist in /usr/share/zoneinfo.
|
||||
|
||||
# === Open Questions ===
|
||||
# Should we support multiple locales (e.g., en_US + de_DE)?
|
||||
# Should we validate timezone path before creating symlink?
|
||||
# Should we configure X11 keyboard layout as well?
|
||||
|
||||
# === Success Criteria ===
|
||||
# Locale is generated and available in the installed system.
|
||||
# Timezone is correctly configured and persists after reboot.
|
||||
# Keyboard layout matches user selection on console login.
|
||||
|
||||
locale_configure() {
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
: "${LOCALE:?Locale is required}"
|
||||
: "${TIMEZONE:?Timezone is required}"
|
||||
: "${KEYBOARD:?Keyboard layout is required}"
|
||||
|
||||
log_info "Locale configuration: setting up locale, timezone, and keyboard"
|
||||
|
||||
# Configure locale in /etc/default/libc-locales
|
||||
log_info "Configuring locale: $LOCALE"
|
||||
local locale_file="$MOUNT_ROOT/etc/default/libc-locales"
|
||||
mkdir -p "$(dirname "$locale_file")"
|
||||
|
||||
# Ensure locale file exists
|
||||
if [[ ! -f "$locale_file" ]]; then
|
||||
touch "$locale_file"
|
||||
fi
|
||||
|
||||
# Uncomment or add the selected locale
|
||||
if grep -q "^#${LOCALE}" "$locale_file"; then
|
||||
# Locale exists but is commented - uncomment it
|
||||
sed -i "s/^#${LOCALE}/${LOCALE}/" "$locale_file"
|
||||
elif grep -q "^${LOCALE}" "$locale_file"; then
|
||||
# Locale already enabled
|
||||
log_info "Locale $LOCALE already enabled"
|
||||
else
|
||||
# Add locale to file
|
||||
echo "$LOCALE UTF-8" >> "$locale_file"
|
||||
fi
|
||||
|
||||
# Generate locales
|
||||
log_info "Generating locales (this may take a moment)..."
|
||||
if ! chroot "$MOUNT_ROOT" xbps-reconfigure -f glibc-locales; then
|
||||
log_warn "Failed to reconfigure glibc-locales, but continuing"
|
||||
fi
|
||||
|
||||
# Configure timezone
|
||||
log_info "Configuring timezone: $TIMEZONE"
|
||||
local timezone_source="/usr/share/zoneinfo/$TIMEZONE"
|
||||
local timezone_target="$MOUNT_ROOT/etc/localtime"
|
||||
|
||||
if [[ ! -f "$MOUNT_ROOT$timezone_source" ]]; then
|
||||
log_warn "Timezone file not found: $timezone_source - using UTC as fallback"
|
||||
TIMEZONE="UTC"
|
||||
timezone_source="/usr/share/zoneinfo/UTC"
|
||||
fi
|
||||
|
||||
# Remove existing symlink or file
|
||||
rm -f "$timezone_target"
|
||||
|
||||
# Create symlink
|
||||
ln -sf "$timezone_source" "$timezone_target"
|
||||
|
||||
# Also set timezone in /etc/timezone for compatibility
|
||||
echo "$TIMEZONE" > "$MOUNT_ROOT/etc/timezone"
|
||||
|
||||
# Configure keyboard layout
|
||||
log_info "Configuring keyboard layout: $KEYBOARD"
|
||||
local rc_conf="$MOUNT_ROOT/etc/rc.conf"
|
||||
|
||||
mkdir -p "$(dirname "$rc_conf")"
|
||||
touch "$rc_conf"
|
||||
|
||||
# Update or add KEYMAP entry in /etc/rc.conf
|
||||
if grep -q "^KEYMAP=" "$rc_conf"; then
|
||||
sed -i "s/^KEYMAP=.*/KEYMAP=\"$KEYBOARD\"/" "$rc_conf"
|
||||
else
|
||||
echo "KEYMAP=\"$KEYBOARD\"" >> "$rc_conf"
|
||||
fi
|
||||
|
||||
log_info "Locale configuration complete."
|
||||
}
|
||||
25
src/main.sh
25
src/main.sh
@ -72,27 +72,23 @@ source "$SCRIPT_DIR/encryption.sh"
|
||||
source "$SCRIPT_DIR/filesystems.sh"
|
||||
# shellcheck source=src/mounts.sh
|
||||
source "$SCRIPT_DIR/mounts.sh"
|
||||
# shellcheck source=src/packages.sh
|
||||
source "$SCRIPT_DIR/packages.sh"
|
||||
# shellcheck source=src/locale.sh
|
||||
source "$SCRIPT_DIR/locale.sh"
|
||||
# shellcheck source=src/users.sh
|
||||
source "$SCRIPT_DIR/users.sh"
|
||||
# shellcheck source=src/services.sh
|
||||
source "$SCRIPT_DIR/services.sh"
|
||||
# shellcheck source=src/installer.sh
|
||||
source "$SCRIPT_DIR/installer.sh"
|
||||
# shellcheck source=src/postinstall.sh
|
||||
source "$SCRIPT_DIR/postinstall.sh"
|
||||
# shellcheck source=src/rollback.sh
|
||||
source "$SCRIPT_DIR/rollback.sh"
|
||||
|
||||
DRY_RUN=0
|
||||
SKIP_INSTALLER=0
|
||||
CURRENT_PHASE=""
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: ./main.sh [--dry-run]
|
||||
Usage: ./main.sh [--dry-run] [--skip-installer]
|
||||
|
||||
--dry-run Print configuration summary and exit
|
||||
--skip-installer Skip launching void-installer (manual run)
|
||||
USAGE
|
||||
}
|
||||
|
||||
@ -102,6 +98,9 @@ parse_args() {
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
--skip-installer)
|
||||
SKIP_INSTALLER=1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
@ -154,14 +153,10 @@ main() {
|
||||
run_phase "encryption" encrypt_root
|
||||
run_phase "filesystems" format_filesystems
|
||||
run_phase "mounts" mount_filesystems
|
||||
run_phase "packages" packages_install
|
||||
run_phase "locale" locale_configure
|
||||
run_phase "users" users_setup
|
||||
run_phase "services" services_configure
|
||||
run_phase "installer" run_installer
|
||||
run_phase "post-install" postinstall_run
|
||||
|
||||
log_info "Installation completed successfully."
|
||||
log_info "System is ready to boot."
|
||||
log_info "Installation wrapper completed."
|
||||
log_info "Log file: ${LOG_FILE}"
|
||||
}
|
||||
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# === Motivation ===
|
||||
# Install all required packages for a bootable Void Linux system.
|
||||
|
||||
# === Problem Statement ===
|
||||
# The installer must provide a minimal base system without relying on void-installer.
|
||||
|
||||
# === Scope ===
|
||||
# In scope: package installation, repository synchronization, dependency resolution.
|
||||
# Out of scope: package selection UI, custom repository configuration.
|
||||
|
||||
# === Concepts ===
|
||||
# XBPS: Void's package manager, handles installation and dependency resolution.
|
||||
# chroot: Execute package installation in the target system environment.
|
||||
# Repository sync: Update package index before installation.
|
||||
|
||||
# === Decisions ===
|
||||
# Install minimal package set by default for base CLI system.
|
||||
# Support optional package installation via config (future: config file support).
|
||||
# Use xbps-install with sync (-S) and yes (-y) flags for non-interactive installation.
|
||||
# Ensure network connectivity before package installation (handled by live environment).
|
||||
# Package list is conservative: only essentials for boot + network + basic tools.
|
||||
|
||||
# === Alternatives Considered ===
|
||||
# Interactive package selection rejected (violates CLI-only requirement).
|
||||
# Separate kernel installation rejected (included in base-system dependency).
|
||||
# systemd-based packages rejected (Void uses runit).
|
||||
|
||||
# === Constraints ===
|
||||
# Target system must be mounted at $MOUNT_ROOT.
|
||||
# Repository must be accessible via network.
|
||||
# xbps must be available in live environment.
|
||||
|
||||
# === Open Questions ===
|
||||
# Should firmware packages beyond linux-firmware be included (e.g., nvidia, AMD)?
|
||||
# Should we validate package availability before installation?
|
||||
# How to handle optional packages (desktop environments, development tools)?
|
||||
|
||||
# === Success Criteria ===
|
||||
# All required packages are installed in the target system.
|
||||
# Package manager can be used in the installed system post-reboot.
|
||||
# System can boot with network connectivity and basic tools available.
|
||||
|
||||
packages_install() {
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
require_command xbps-install
|
||||
|
||||
log_info "Package installation: installing base system"
|
||||
|
||||
# Define minimal package set for bootable CLI system
|
||||
local base_packages=(
|
||||
"base-system" # Essential base packages (includes kernel via dependency)
|
||||
"linux" # Linux kernel
|
||||
"linux-firmware" # Firmware blobs for hardware
|
||||
"grub" # Bootloader
|
||||
"cryptsetup" # LUKS encryption tools
|
||||
"dracut" # initramfs generator
|
||||
)
|
||||
|
||||
local network_packages=(
|
||||
"dhcpcd" # DHCP client for network
|
||||
"iproute2" # ip command for network configuration
|
||||
"iputils" # ping, traceroute, etc.
|
||||
)
|
||||
|
||||
local system_tools=(
|
||||
"vim" # Text editor
|
||||
"nano" # Alternative text editor
|
||||
"sudo" # Privilege escalation
|
||||
)
|
||||
|
||||
# Combine all package arrays
|
||||
local all_packages=("${base_packages[@]}" "${network_packages[@]}" "${system_tools[@]}")
|
||||
|
||||
log_info "Package list: ${all_packages[*]}"
|
||||
log_info "Installing ${#all_packages[@]} packages (this may take several minutes)..."
|
||||
|
||||
# Ensure repository is accessible and synchronized
|
||||
# Use chroot to install packages into target system
|
||||
if ! chroot "$MOUNT_ROOT" xbps-install -Syu "${all_packages[@]}"; then
|
||||
die "Package installation failed. Check network connectivity and repository status."
|
||||
fi
|
||||
|
||||
log_info "Package installation complete."
|
||||
|
||||
# Verify critical packages are installed
|
||||
log_info "Verifying package installation..."
|
||||
local critical_packages=("base-system" "linux" "grub" "cryptsetup")
|
||||
for pkg in "${critical_packages[@]}"; do
|
||||
if ! chroot "$MOUNT_ROOT" xbps-query "$pkg" >/dev/null 2>&1; then
|
||||
die "Critical package not found: $pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Package verification passed."
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# === Motivation ===
|
||||
# Enable essential system services for network connectivity and basic functionality.
|
||||
|
||||
# === Problem Statement ===
|
||||
# A freshly installed system needs service activation to function properly at boot.
|
||||
|
||||
# === Scope ===
|
||||
# In scope: network service activation, optional services (SSH, NTP).
|
||||
# Out of scope: service configuration, firewall rules, custom service creation.
|
||||
|
||||
# === Concepts ===
|
||||
# runit: Void's init system and service supervisor.
|
||||
# Service directory: /etc/sv/<service> contains service definition.
|
||||
# Service activation: Symlink from /etc/runit/runsvdir/default/<service> to /etc/sv/<service>.
|
||||
# dhcpcd: DHCP client for automatic network configuration.
|
||||
# sshd: OpenSSH server for remote access.
|
||||
# chronyd: NTP client for time synchronization.
|
||||
|
||||
# === Decisions ===
|
||||
# Enable dhcpcd by default for network connectivity (required for most systems).
|
||||
# Disable sshd by default for security (can be enabled via config).
|
||||
# Disable chronyd by default (optional service, can be enabled via config).
|
||||
# Use absolute symlinks for service activation (Void standard).
|
||||
# Validate service directory exists before creating symlink.
|
||||
|
||||
# === Alternatives Considered ===
|
||||
# NetworkManager rejected as default (dhcpcd is lighter and sufficient for server/minimal systems).
|
||||
# systemd-networkd rejected (Void uses runit).
|
||||
# Enabling all services rejected (security and resource waste).
|
||||
# Auto-detection of network interface rejected (dhcpcd handles this).
|
||||
|
||||
# === Constraints ===
|
||||
# Target system must be mounted at $MOUNT_ROOT.
|
||||
# Service packages must be installed before activation.
|
||||
# Service definitions must exist in /etc/sv/.
|
||||
|
||||
# === Open Questions ===
|
||||
# Should we detect network interface type and choose NetworkManager vs dhcpcd?
|
||||
# Should we prompt for optional services interactively or only via config?
|
||||
# Should we enable dbus by default for desktop environments?
|
||||
|
||||
# === Success Criteria ===
|
||||
# dhcpcd is enabled and system has network connectivity after reboot.
|
||||
# Optional services are enabled if requested via configuration.
|
||||
# Services start successfully on boot.
|
||||
|
||||
services_configure() {
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
local runsvdir="$MOUNT_ROOT/etc/runit/runsvdir/default"
|
||||
|
||||
log_info "Service configuration: enabling system services"
|
||||
|
||||
# Ensure runsvdir exists
|
||||
mkdir -p "$runsvdir"
|
||||
|
||||
# Enable dhcpcd for network connectivity
|
||||
log_info "Enabling dhcpcd service"
|
||||
enable_service "dhcpcd"
|
||||
|
||||
# Optional services (can be extended with config file support)
|
||||
# For now, we only enable essential services
|
||||
# Future: Add support for ENABLE_SSHD, ENABLE_CHRONYD via config
|
||||
|
||||
log_info "Service configuration complete."
|
||||
}
|
||||
|
||||
enable_service() {
|
||||
local service="$1"
|
||||
local service_dir="/etc/sv/$service"
|
||||
local runsvdir="$MOUNT_ROOT/etc/runit/runsvdir/default"
|
||||
local service_link="$runsvdir/$service"
|
||||
|
||||
# Check if service definition exists
|
||||
if [[ ! -d "$MOUNT_ROOT$service_dir" ]]; then
|
||||
log_warn "Service definition not found: $service_dir (service may not be installed)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if service is already enabled
|
||||
if [[ -L "$service_link" ]]; then
|
||||
log_info "Service $service already enabled"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create symlink to enable service
|
||||
if ! ln -sf "$service_dir" "$service_link"; then
|
||||
log_warn "Failed to enable service: $service"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Service $service enabled"
|
||||
return 0
|
||||
}
|
||||
120
src/users.sh
120
src/users.sh
@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# === Motivation ===
|
||||
# Create user accounts with proper permissions for system access.
|
||||
|
||||
# === Problem Statement ===
|
||||
# A bootable system needs at least root access and a non-privileged user account.
|
||||
|
||||
# === Scope ===
|
||||
# In scope: root password, standard user creation, group membership, sudo configuration.
|
||||
# Out of scope: multiple users, password policies, PAM configuration.
|
||||
|
||||
# === Concepts ===
|
||||
# Root user: Administrative account with UID 0, full system access.
|
||||
# Standard user: Non-privileged account for daily tasks, can escalate via sudo.
|
||||
# wheel group: Traditional Unix group for sudo access.
|
||||
# useradd: Creates user accounts with home directory and group membership.
|
||||
|
||||
# === Decisions ===
|
||||
# Root password is set interactively (security requirement).
|
||||
# Standard user is created with wheel group for sudo access.
|
||||
# Additional groups (audio, video, storage, network) are added for hardware access.
|
||||
# User password is set interactively (same security requirement as root).
|
||||
# sudo is configured by uncommenting wheel group in /etc/sudoers.
|
||||
# Default shell is /bin/bash (most common, widely supported).
|
||||
|
||||
# === Alternatives Considered ===
|
||||
# Hash-based password rejected for initial implementation (requires additional config).
|
||||
# Passwordless sudo rejected (security risk).
|
||||
# Creating user without sudo rejected (user would be unable to administer system).
|
||||
# systemd-homed rejected (Void uses traditional user management).
|
||||
|
||||
# === Constraints ===
|
||||
# Target system must be mounted at $MOUNT_ROOT.
|
||||
# sudo package must be installed before configuration.
|
||||
# Interactive password input requires TTY.
|
||||
|
||||
# === Open Questions ===
|
||||
# Should we support multiple user creation?
|
||||
# Should we validate password strength?
|
||||
# Should we configure shell based on user preference?
|
||||
|
||||
# === Success Criteria ===
|
||||
# Root password is set and login works.
|
||||
# Standard user can log in with password.
|
||||
# Standard user can execute sudo commands.
|
||||
# User has access to audio, video, and network hardware.
|
||||
|
||||
users_setup() {
|
||||
: "${MOUNT_ROOT:?Mount root is required}"
|
||||
: "${USERNAME:?Username is required}"
|
||||
local user_shell="${USER_SHELL:-/bin/bash}"
|
||||
local user_groups="${USER_GROUPS:-wheel,audio,video,storage,network}"
|
||||
|
||||
log_info "User setup: configuring root and creating standard user"
|
||||
|
||||
# Set root password interactively
|
||||
log_info "Setting root password"
|
||||
log_info "Please enter the root password when prompted:"
|
||||
if ! chroot "$MOUNT_ROOT" passwd; then
|
||||
die "Failed to set root password"
|
||||
fi
|
||||
|
||||
# Create standard user
|
||||
log_info "Creating user: $USERNAME"
|
||||
if chroot "$MOUNT_ROOT" id "$USERNAME" >/dev/null 2>&1; then
|
||||
log_warn "User $USERNAME already exists, skipping creation"
|
||||
else
|
||||
# Create user with home directory and group membership
|
||||
if ! chroot "$MOUNT_ROOT" useradd -m -G "$user_groups" -s "$user_shell" "$USERNAME"; then
|
||||
die "Failed to create user: $USERNAME"
|
||||
fi
|
||||
log_info "User $USERNAME created with groups: $user_groups"
|
||||
fi
|
||||
|
||||
# Set user password interactively
|
||||
log_info "Setting password for user: $USERNAME"
|
||||
log_info "Please enter the password for $USERNAME when prompted:"
|
||||
if ! chroot "$MOUNT_ROOT" passwd "$USERNAME"; then
|
||||
die "Failed to set password for user: $USERNAME"
|
||||
fi
|
||||
|
||||
# Configure sudo for wheel group
|
||||
log_info "Configuring sudo access for wheel group"
|
||||
local sudoers_file="$MOUNT_ROOT/etc/sudoers"
|
||||
|
||||
if [[ ! -f "$sudoers_file" ]]; then
|
||||
die "Sudoers file not found. Ensure sudo package is installed."
|
||||
fi
|
||||
|
||||
# Backup sudoers file
|
||||
cp "$sudoers_file" "${sudoers_file}.bak"
|
||||
|
||||
# Uncomment wheel group line (allow members of wheel to execute any command)
|
||||
if grep -q "^# %wheel ALL=(ALL:ALL) ALL" "$sudoers_file"; then
|
||||
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' "$sudoers_file"
|
||||
log_info "Enabled sudo for wheel group"
|
||||
elif grep -q "^%wheel ALL=(ALL:ALL) ALL" "$sudoers_file"; then
|
||||
log_info "sudo for wheel group already enabled"
|
||||
elif grep -q "^# %wheel ALL=(ALL) ALL" "$sudoers_file"; then
|
||||
# Alternative format
|
||||
sed -i 's/^# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/' "$sudoers_file"
|
||||
log_info "Enabled sudo for wheel group (alternative format)"
|
||||
else
|
||||
# Add wheel group entry if not present
|
||||
echo "%wheel ALL=(ALL:ALL) ALL" >> "$sudoers_file"
|
||||
log_info "Added sudo configuration for wheel group"
|
||||
fi
|
||||
|
||||
# Validate sudoers file syntax
|
||||
if ! chroot "$MOUNT_ROOT" visudo -c -f /etc/sudoers >/dev/null 2>&1; then
|
||||
log_warn "Sudoers file validation failed, restoring backup"
|
||||
mv "${sudoers_file}.bak" "$sudoers_file"
|
||||
die "Failed to configure sudo (syntax error)"
|
||||
fi
|
||||
|
||||
rm -f "${sudoers_file}.bak"
|
||||
|
||||
log_info "User setup complete."
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user