Compare commits
7 Commits
667453596a
...
8cc60e3dae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cc60e3dae | ||
|
|
b2031eae4b | ||
|
|
0913adf00e | ||
|
|
dcd22b04c0 | ||
|
|
4ac64e6659 | ||
|
|
56c8234d35 | ||
|
|
f295eb5684 |
203
README.md
203
README.md
@ -1,59 +1,176 @@
|
|||||||
# Void Bootstrapp Wrapper
|
# Void Bootstrapp - Vollständiger CLI-Installer
|
||||||
|
|
||||||
Ein Bash-Wrapper, der die kritischen Vorarbeiten fuer eine verschluesselte Void-Linux-Installation erledigt und danach den offiziellen `void-installer` nutzt.
|
Ein vollautomatischer Bash-Installer für verschlüsselte Void Linux Installationen.
|
||||||
|
|
||||||
## Ablauf
|
## Überblick
|
||||||
|
|
||||||
1. Sanity Checks (Root, UEFI, Zielplatte)
|
Dieser Installer automatisiert die komplette Installation von Void Linux mit LUKS2-Verschlüsselung, btrfs/ext4-Dateisystem und allen erforderlichen System-Konfigurationen - **ohne manuelle Eingriffe**.
|
||||||
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)
|
|
||||||
|
|
||||||
## Usage
|
**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
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo ./src/main.sh [--dry-run] [--skip-installer]
|
sudo ./src/main.sh [--dry-run]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Flags
|
### Flags
|
||||||
|
|
||||||
- `--dry-run`: Zeigt die Konfiguration und bricht ohne Aenderungen ab.
|
- `--dry-run`: Zeigt die Konfiguration und bricht ohne Änderungen ab
|
||||||
- `--skip-installer`: Laesst den Start von `void-installer` aus (manuell starten).
|
|
||||||
|
|
||||||
## Installer Bedienung nach dem Script
|
## Konfiguration
|
||||||
|
|
||||||
Im `void-installer` musst du die vorbereiteten Mounts uebernehmen und darfst nichts formatieren.
|
Während der Installation werden folgende Parameter abgefragt:
|
||||||
|
|
||||||
1. Waehle die manuelle Partitionierung.
|
### Disk-Konfiguration
|
||||||
2. Setze nur die Mountpoints, ohne Formatierung.
|
- **Target Disk**: Zielplatte (z.B. `/dev/sda`)
|
||||||
3. Root:
|
- **Hostname**: System-Hostname
|
||||||
- Device: `/dev/mapper/cryptroot`
|
- **Filesystem**: `btrfs` (Standard) oder `ext4`
|
||||||
- Label: `void-root` (btrfs und ext4)
|
- **LUKS Version**: `2` (Standard) oder `1` (für ältere GRUB-Versionen)
|
||||||
- Mountpoint: `/`
|
- **ESP Size**: EFI System Partition Größe (Standard: `1GiB`)
|
||||||
- Format: aus
|
- **Root End**: Ende der Root-Partition (Standard: `100%`)
|
||||||
4. ESP:
|
- **Swap Size**: Swap-File Größe (Standard: `4GiB`, `0` zum Deaktivieren)
|
||||||
- Device: erste Partition der Disk (meist `...1`)
|
|
||||||
- Label: `EFI`
|
|
||||||
- Mountpoint: `/boot/efi`
|
|
||||||
- Format: aus
|
|
||||||
|
|
||||||
Wenn Labels nicht angezeigt werden, pruefe mit `lsblk -f`, welches Device `void-root` bzw. `EFI` traegt.
|
### Locale-Konfiguration
|
||||||
Bei btrfs sind Subvolumes bereits angelegt und gemountet: `@`, `@home`, `@var`, `@log`, `@snapshots`, `@swap`.
|
- **Locale**: System-Sprache (Standard: `en_US.UTF-8`)
|
||||||
Der Installer soll keine neuen Subvolumes anlegen oder formatieren, sondern die vorhandenen Mounts unveraendert lassen.
|
- **Timezone**: Zeitzone (Standard: `UTC`, z.B. `Europe/Berlin`)
|
||||||
Es reicht, im Installer nur `/` und `/boot/efi` zu setzen; die Subvolumes werden im Post-Install in `/etc/fstab` eingetragen.
|
- **Keyboard**: Console-Keyboard-Layout (Standard: `us`, z.B. `de-latin1`)
|
||||||
Mapping der Subvolumes (gesetzt durch den Wrapper/Post-Install):
|
|
||||||
- `@` -> `/`
|
### User-Konfiguration
|
||||||
- `@home` -> `/home`
|
- **Username**: Name des Standard-Benutzers (erforderlich)
|
||||||
- `@var` -> `/var`
|
- **Root-Passwort**: Wird interaktiv gesetzt
|
||||||
- `@log` -> `/var/log`
|
- **User-Passwort**: Wird interaktiv gesetzt
|
||||||
- `@snapshots` -> `/.snapshots`
|
|
||||||
- `@swap` -> `/swap`
|
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
|
||||||
|
|
||||||
## Hinweise
|
## Hinweise
|
||||||
|
|
||||||
- Der Wrapper ist destruktiv und loescht die Zielplatte nach expliziter Bestaetigung.
|
- **Destruktiv**: Der Installer löscht die Zielplatte nach expliziter Bestätigung
|
||||||
- UEFI ist Pflicht; BIOS-Installationen sind nicht unterstuetzt.
|
- **Interaktiv**: Passwörter werden aus Sicherheitsgründen interaktiv abgefragt
|
||||||
- Logs landen unter `/tmp/void-wrapper-YYYY-MM-DD-HHMMSS.log`.
|
- **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.
|
||||||
|
|||||||
@ -53,6 +53,14 @@ export ESP_MOUNT="/mnt/boot/efi"
|
|||||||
export ROOT_LABEL="void-root"
|
export ROOT_LABEL="void-root"
|
||||||
export EFI_LABEL="EFI"
|
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() {
|
validate_size() {
|
||||||
local size="$1"
|
local size="$1"
|
||||||
# Allow sizes like: 1GiB, 100MB, 50%, 100%
|
# Allow sizes like: 1GiB, 100MB, 50%, 100%
|
||||||
@ -134,6 +142,12 @@ config_prompt_interactive() {
|
|||||||
prompt_with_default ESP_SIZE "ESP size (e.g. 1GiB)" "${ESP_SIZE}"
|
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 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}"
|
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() {
|
config_validate() {
|
||||||
@ -143,6 +157,9 @@ config_validate() {
|
|||||||
if [[ -z "$HOSTNAME" ]]; then
|
if [[ -z "$HOSTNAME" ]]; then
|
||||||
die "Hostname is required."
|
die "Hostname is required."
|
||||||
fi
|
fi
|
||||||
|
if [[ -z "$USERNAME" ]]; then
|
||||||
|
die "Username is required."
|
||||||
|
fi
|
||||||
|
|
||||||
case "$FS_TYPE" in
|
case "$FS_TYPE" in
|
||||||
btrfs|ext4) ;;
|
btrfs|ext4) ;;
|
||||||
@ -169,6 +186,16 @@ config_validate() {
|
|||||||
if [[ "$SWAP_SIZE" != "0" ]] && ! validate_size "$SWAP_SIZE"; then
|
if [[ "$SWAP_SIZE" != "0" ]] && ! validate_size "$SWAP_SIZE"; then
|
||||||
die "Invalid swap size format: $SWAP_SIZE (expected: 4GiB, 8GB, 0 to disable)"
|
die "Invalid swap size format: $SWAP_SIZE (expected: 4GiB, 8GB, 0 to disable)"
|
||||||
fi
|
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() {
|
config_print_summary() {
|
||||||
@ -178,6 +205,7 @@ config_print_summary() {
|
|||||||
: "${LUKS_VERSION:?LUKS version is required}"
|
: "${LUKS_VERSION:?LUKS version is required}"
|
||||||
: "${MOUNT_ROOT:?Mount root is required}"
|
: "${MOUNT_ROOT:?Mount root is required}"
|
||||||
: "${ESP_MOUNT:?ESP mount path is required}"
|
: "${ESP_MOUNT:?ESP mount path is required}"
|
||||||
|
: "${USERNAME:?Username is required}"
|
||||||
log_info "Configuration summary:"
|
log_info "Configuration summary:"
|
||||||
log_info " Disk: $DISK"
|
log_info " Disk: $DISK"
|
||||||
log_info " Hostname: $HOSTNAME"
|
log_info " Hostname: $HOSTNAME"
|
||||||
@ -188,6 +216,10 @@ config_print_summary() {
|
|||||||
log_info " Swap size: ${SWAP_SIZE}"
|
log_info " Swap size: ${SWAP_SIZE}"
|
||||||
log_info " Mount root: $MOUNT_ROOT"
|
log_info " Mount root: $MOUNT_ROOT"
|
||||||
log_info " ESP mount: $ESP_MOUNT"
|
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() {
|
config_confirm_destructive() {
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
#!/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
Normal file
117
src/locale.sh
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#!/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,23 +72,27 @@ source "$SCRIPT_DIR/encryption.sh"
|
|||||||
source "$SCRIPT_DIR/filesystems.sh"
|
source "$SCRIPT_DIR/filesystems.sh"
|
||||||
# shellcheck source=src/mounts.sh
|
# shellcheck source=src/mounts.sh
|
||||||
source "$SCRIPT_DIR/mounts.sh"
|
source "$SCRIPT_DIR/mounts.sh"
|
||||||
# shellcheck source=src/installer.sh
|
# shellcheck source=src/packages.sh
|
||||||
source "$SCRIPT_DIR/installer.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/postinstall.sh
|
# shellcheck source=src/postinstall.sh
|
||||||
source "$SCRIPT_DIR/postinstall.sh"
|
source "$SCRIPT_DIR/postinstall.sh"
|
||||||
# shellcheck source=src/rollback.sh
|
# shellcheck source=src/rollback.sh
|
||||||
source "$SCRIPT_DIR/rollback.sh"
|
source "$SCRIPT_DIR/rollback.sh"
|
||||||
|
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
SKIP_INSTALLER=0
|
|
||||||
CURRENT_PHASE=""
|
CURRENT_PHASE=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<USAGE
|
cat <<USAGE
|
||||||
Usage: ./main.sh [--dry-run] [--skip-installer]
|
Usage: ./main.sh [--dry-run]
|
||||||
|
|
||||||
--dry-run Print configuration summary and exit
|
--dry-run Print configuration summary and exit
|
||||||
--skip-installer Skip launching void-installer (manual run)
|
|
||||||
USAGE
|
USAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +102,6 @@ parse_args() {
|
|||||||
--dry-run)
|
--dry-run)
|
||||||
DRY_RUN=1
|
DRY_RUN=1
|
||||||
;;
|
;;
|
||||||
--skip-installer)
|
|
||||||
SKIP_INSTALLER=1
|
|
||||||
;;
|
|
||||||
-h|--help)
|
-h|--help)
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
@ -153,10 +154,14 @@ main() {
|
|||||||
run_phase "encryption" encrypt_root
|
run_phase "encryption" encrypt_root
|
||||||
run_phase "filesystems" format_filesystems
|
run_phase "filesystems" format_filesystems
|
||||||
run_phase "mounts" mount_filesystems
|
run_phase "mounts" mount_filesystems
|
||||||
run_phase "installer" run_installer
|
run_phase "packages" packages_install
|
||||||
|
run_phase "locale" locale_configure
|
||||||
|
run_phase "users" users_setup
|
||||||
|
run_phase "services" services_configure
|
||||||
run_phase "post-install" postinstall_run
|
run_phase "post-install" postinstall_run
|
||||||
|
|
||||||
log_info "Installation wrapper completed."
|
log_info "Installation completed successfully."
|
||||||
|
log_info "System is ready to boot."
|
||||||
log_info "Log file: ${LOG_FILE}"
|
log_info "Log file: ${LOG_FILE}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
97
src/packages.sh
Normal file
97
src/packages.sh
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/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."
|
||||||
|
}
|
||||||
95
src/services.sh
Normal file
95
src/services.sh
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#!/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
Normal file
120
src/users.sh
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/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