#!/bin/bash set -e # ============================================================ # Debian Unattended ISO Builder # Baut eine Custom Debian ISO mit eingebettetem Preseed # ============================================================ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" WORK_DIR="/tmp/iso-builder-$$" DEBIAN_VERSION="${DEBIAN_VERSION:-13}" DEBIAN_CODENAME="${DEBIAN_CODENAME:-trixie}" ARCH="amd64" # --- Defaults --- IP="" NETMASK="255.255.255.0" GATEWAY="" DNS="1.1.1.1" HOSTNAME="debian" USER="sascha" PASSWORD="" SSH_KEY_FILE="$HOME/.ssh/id_ed25519.pub" OUTPUT_DIR="$SCRIPT_DIR/output" NODE="" VMID="" CREATE_VM=false VM_CORES=8 VM_MEMORY=16384 VM_DISK=64 usage() { cat </dev/null; then echo "Installing missing dependency: $cmd" sudo apt-get install -y xorriso cpio gzip genisoimage 2>/dev/null || true fi done # --- Password --- if [[ -z "$PASSWORD" ]]; then read -sp "Password for user $USER: " PASSWORD echo fi PASSWORD_HASH=$(echo "$PASSWORD" | openssl passwd -6 -stdin) # --- SSH Key --- if [[ -f "$SSH_KEY_FILE" ]]; then SSH_KEY=$(cat "$SSH_KEY_FILE") else echo "Warning: SSH key file not found: $SSH_KEY_FILE" SSH_KEY="" fi # --- Download Debian ISO --- ISO_URL="https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/debian-${DEBIAN_VERSION}.*-${ARCH}-netinst.iso" ISO_FILE="$SCRIPT_DIR/debian-${DEBIAN_VERSION}-${ARCH}-netinst.iso" if [[ ! -f "$ISO_FILE" ]]; then echo "Downloading Debian ${DEBIAN_VERSION} netinstall ISO..." # Get exact filename from listing EXACT_URL=$(curl -sL "https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/" | \ grep -oP "debian-${DEBIAN_VERSION}\.[0-9]+-${ARCH}-netinst\.iso" | head -1) wget -q --show-progress -O "$ISO_FILE" \ "https://cdimage.debian.org/debian-cd/current/${ARCH}/iso-cd/${EXACT_URL}" fi echo "Building ISO for: ${HOSTNAME} (${IP})" # --- Extract ISO --- cleanup() { rm -rf "$WORK_DIR"; } trap cleanup EXIT mkdir -p "$WORK_DIR"/{iso,custom} xorriso -osirrox on -indev "$ISO_FILE" -extract / "$WORK_DIR/iso" 2>/dev/null chmod -R u+w "$WORK_DIR/iso" # --- Generate Preseed --- PRESEED="$WORK_DIR/iso/preseed.cfg" sed \ -e "s|{{IP}}|${IP}|g" \ -e "s|{{NETMASK}}|${NETMASK}|g" \ -e "s|{{GATEWAY}}|${GATEWAY}|g" \ -e "s|{{DNS}}|${DNS}|g" \ -e "s|{{HOSTNAME}}|${HOSTNAME}|g" \ -e "s|{{USER}}|${USER}|g" \ -e "s|{{PASSWORD_HASH}}|${PASSWORD_HASH}|g" \ -e "s|{{SSH_KEY}}|${SSH_KEY}|g" \ "$SCRIPT_DIR/preseed.cfg.tpl" > "$PRESEED" # --- Patch GRUB (UEFI) - komplett ersetzen --- GRUB_CFG="$WORK_DIR/iso/boot/grub/grub.cfg" if [[ -f "$GRUB_CFG" ]]; then cat > "$GRUB_CFG" << GRUBEOF set default=0 set timeout=0 menuentry "Debian Auto Install - ${HOSTNAME}" { linux /install.amd/vmlinuz auto=true priority=critical preseed/file=/cdrom/preseed.cfg --- initrd /install.amd/initrd.gz } GRUBEOF fi # --- Patch isolinux (BIOS) - komplett ersetzen --- TXT_CFG="$WORK_DIR/iso/isolinux/txt.cfg" if [[ -f "$TXT_CFG" ]]; then cat > "$TXT_CFG" << TXTEOF default auto label auto menu label Auto Install - ${HOSTNAME} kernel /install.amd/vmlinuz append auto=true priority=critical preseed/file=/cdrom/preseed.cfg initrd=/install.amd/initrd.gz --- TXTEOF fi ISOLINUX_CFG="$WORK_DIR/iso/isolinux/isolinux.cfg" if [[ -f "$ISOLINUX_CFG" ]]; then sed -i 's/timeout .*/timeout 1/' "$ISOLINUX_CFG" fi # --- Rebuild ISO --- mkdir -p "$OUTPUT_DIR" OUTPUT_ISO="$OUTPUT_DIR/debian-${DEBIAN_VERSION}-${HOSTNAME}-${IP}.iso" cd "$WORK_DIR/iso" # Fix MD5 find . -type f ! -name 'md5sum.txt' -exec md5sum {} \; > md5sum.txt 2>/dev/null || true xorriso -as mkisofs \ -r -J \ -b isolinux/isolinux.bin \ -c isolinux/boot.cat \ -no-emul-boot \ -boot-load-size 4 \ -boot-info-table \ -eltorito-alt-boot \ -e boot/grub/efi.img \ -no-emul-boot \ -isohybrid-gpt-basdat \ -o "$OUTPUT_ISO" \ . 2>/dev/null echo "" echo "============================================" echo "ISO created: $OUTPUT_ISO" echo "============================================" echo "Host: $HOSTNAME" echo "IP: $IP" echo "Gateway: $GATEWAY" echo "User: $USER" echo "SSH Key: $(echo $SSH_KEY | cut -c1-40)..." echo "============================================" echo "" if [[ -n "$PVE_HOST" ]]; then echo "Uploading to node${NODE} (${PVE_HOST})..." scp -o StrictHostKeyChecking=no "$OUTPUT_ISO" "root@${PVE_HOST}:/var/lib/vz/template/iso/" && \ echo "✅ ISO uploaded to node${NODE}" || \ { echo "❌ Upload failed"; exit 1; } if [[ "$CREATE_VM" == true ]]; then ISO_NAME="$(basename "$OUTPUT_ISO")" STORAGE="powerstore-node${NODE}" PVE_NODE="node${NODE}" # Auto-assign next free VMID if not specified if [[ -z "$VMID" ]]; then VMID=$(ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" "pvesh get /cluster/nextid" 2>/dev/null) echo "Auto-assigned VMID: ${VMID}" fi echo "" echo "Creating VM ${VMID} on ${PVE_NODE}..." ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" " # Create VM: q35, UEFI, virtio-scsi pvesh create /nodes/${PVE_NODE}/qemu \ --vmid ${VMID} \ --name ${HOSTNAME} \ --machine q35 \ --bios ovmf \ --efidisk0 ${STORAGE}:1,efitype=4m,pre-enrolled-keys=0 \ --scsihw virtio-scsi-pci \ --scsi0 ${STORAGE}:${VM_DISK},cache=writeback \ --ide2 local:iso/${ISO_NAME},media=cdrom \ --net0 virtio,bridge=vmbr0 \ --cores ${VM_CORES} \ --memory ${VM_MEMORY} \ --cpu cputype=host \ --agent enabled=1 \ --boot order='scsi0;ide2' \ --ostype l26 \ --onboot 1 \ --numa 1 \ --balloon 0 \ --serial0 socket " && echo "✅ VM ${VMID} created" || { echo "❌ VM creation failed"; exit 1; } echo "Starting VM ${VMID}..." ssh -o StrictHostKeyChecking=no "root@${PVE_HOST}" \ "pvesh create /nodes/${PVE_NODE}/qemu/${VMID}/status/start" && \ echo "✅ VM ${VMID} started - installing Debian..." || echo "❌ VM start failed" echo "" echo "============================================" echo "Boot order: scsi0 → ide2 (disk first, ISO fallback)" echo "1st boot: disk empty → boots ISO → installs Debian" echo "2nd boot: disk has GRUB → boots from disk → done!" echo "" echo "After installation (~5 min):" echo " ssh ${USER}@${IP}" echo " ansible-playbook site.yml -l ${HOSTNAME}" echo "============================================" fi else echo "Upload to Proxmox:" echo " scp $OUTPUT_ISO root@:/var/lib/vz/template/iso/" fi