From cb5bf7ff945d86a893b280356b971a31d8192677 Mon Sep 17 00:00:00 2001 From: Joe Gitta Date: Sat, 23 May 2026 14:26:25 +0000 Subject: [PATCH] feat: generic user, dynamic paths, simplified ISO build pipeline --- browser.py | 21 ++-- build-iso.sh | 213 +++++++++++++++++++------------------ install-kiosk.sh | 246 ++++++++++++++++++++++--------------------- preseed-bootstrap.sh | 40 +++++++ preseed.cfg | 59 ++++++----- 5 files changed, 319 insertions(+), 260 deletions(-) create mode 100644 preseed-bootstrap.sh diff --git a/browser.py b/browser.py index 9aa69f3..596d3a1 100755 --- a/browser.py +++ b/browser.py @@ -14,8 +14,9 @@ from PyQt5.QtWidgets import ( QTreeWidget, QTreeWidgetItem, QAbstractItemView ) -HOME_URL = "file:///home/jgitta/kiosk-home/index.html" -BOOKMARKS_FILE = "/home/jgitta/kiosk-browser/bookmarks.json" +_HOME = pathlib.Path.home() +HOME_URL = f"file://{_HOME}/kiosk-home/index.html" +BOOKMARKS_FILE = str(_HOME / "kiosk-browser/bookmarks.json") CDP_PORT = 9222 TOOLBAR_H = 72 # single row — was 142 (82 + 60) # Screen size is detected dynamically in main() after QApplication starts @@ -736,7 +737,7 @@ class KioskOverlay(QWidget): # ── Chrome launcher ─────────────────────────────────────────────── def launch_chrome(): - profile_dir = pathlib.Path("/home/jgitta/chromium-kiosk") + profile_dir = _HOME / "chromium-kiosk" profile_dir.mkdir(parents=True, exist_ok=True) for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"): @@ -767,12 +768,16 @@ def launch_chrome(): cookies_db = profile_dir / "Default" / "Cookies" if cookies_db.exists(): try: + _cfg = json.loads((_HOME / "kiosk-browser/config.json").read_text()) + _email_url = _cfg.get("urls", {}).get("email", "") + _email_host = _email_url.split("//")[-1].split("/")[0] if _email_url else "" conn = sqlite3.connect(str(cookies_db)) - conn.execute( - "DELETE FROM cookies WHERE host_key LIKE '%mail.jgitta.com%'") - conn.commit() + if _email_host: + conn.execute("DELETE FROM cookies WHERE host_key LIKE ?", + (f"%{_email_host}%",)) + conn.commit() + print(f"Cleared stale session cookie for {_email_host}") conn.close() - print("Cleared stale Roundcube session cookie") except Exception as e: print(f"Cookie clear warning: {e}") @@ -789,7 +794,7 @@ def launch_chrome(): "--disable-extensions", "--disable-gpu", "--disable-dev-shm-usage", - "--user-data-dir=/home/jgitta/chromium-kiosk", + f"--user-data-dir={_HOME / 'chromium-kiosk'}", f"--remote-debugging-port={CDP_PORT}", "--remote-allow-origins=*", f"--window-position=0,{TOOLBAR_H}", diff --git a/build-iso.sh b/build-iso.sh index 8a60b9a..9cfa644 100755 --- a/build-iso.sh +++ b/build-iso.sh @@ -1,119 +1,124 @@ #!/bin/bash set -e -# Build SeniorNet Kiosk ISO -# Run on workstation: ./build-iso.sh -# Requires: debian-live-build, sudo - -echo "==> SeniorNet Kiosk ISO Builder" -echo +# SeniorNet Kiosk ISO Builder +# Takes the official Debian Trixie netinstall ISO and repacks it with: +# - preseed.cfg embedded for silent auto-install +# - GRUB + isolinux configured for auto-boot (UEFI + BIOS) +# +# The kiosk app itself is NOT bundled — it's downloaded from Gitea at +# first-boot time. Rebuild the ISO only when changing Debian base or preseed. +# +# Run: sudo ./build-iso.sh +# Output: iso-output/seniornet-kiosk.iso if [ "$EUID" -ne 0 ]; then - echo "ERROR: Run as root (sudo ./build-iso.sh)" - exit 1 + echo "ERROR: Run as root: sudo ./build-iso.sh"; exit 1 fi -# Check dependencies -if ! command -v lb &>/dev/null; then - echo "==> Installing debian-live-build..." - apt-get update - apt-get install -y debian-live-build -fi - -BUILD_DIR="/tmp/kiosk-build-$$" -OUTPUT_DIR="$(pwd)/iso-output" REPO_DIR="$(cd "$(dirname "$0")" && pwd)" +OUTPUT_DIR="$REPO_DIR/iso-output" +WORK_DIR="$(mktemp -d /tmp/kiosk-iso-XXXX)" +trap 'rm -rf "$WORK_DIR"' EXIT -mkdir -p "$BUILD_DIR" "$OUTPUT_DIR" -cd "$BUILD_DIR" +DEBIAN_ISO_URL="https://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/amd64/iso-cd/debian-testing-amd64-netinst.iso" +ISO_CACHE="$OUTPUT_DIR/debian-netinst.iso" +OUTPUT_ISO="$OUTPUT_DIR/seniornet-kiosk.iso" -echo "==> Initializing live-build..." -lb config \ - --distribution trixie \ - --architectures amd64 \ - --image-type iso-hybrid \ - --bootappend-live "boot=live components autologin username=root quiet splash" \ - --bootappend-install "preseed/file=/preseed.cfg auto=true priority=critical" \ - --linux-packages "linux-image-amd64" \ - --parent-mirror-bootstrap "https://deb.debian.org/debian" \ - --parent-mirror-binary "https://deb.debian.org/debian" \ - --parent-mirror-binary-security "https://security.debian.org/debian-security" \ - --parent-distribution-binary-security trixie-security +mkdir -p "$OUTPUT_DIR" -echo "==> Adding packages..." -mkdir -p config/package-lists -cat > config/package-lists/kiosk.list.chroot << 'PKGLIST' -# Base system -openssh-server -build-essential -git -curl -wget -sudo - -# Display and window manager -xserver-xorg -xserver-xorg-video-dummy -xinit -openbox -xvfb -xdotool -xprop - -# Chromium browser -chromium - -# Python and kiosk app -python3 -python3-pip -sqlite3 -PKGLIST - -echo "==> Adding preseed file..." -mkdir -p config/includes.installer -cp "$REPO_DIR/preseed.cfg" config/includes.installer/ - -echo "==> Adding hook script..." -mkdir -p config/hooks/normal -cat > config/hooks/normal/9999-kiosk-postinstall.chroot << 'HOOK' -#!/bin/bash -set -e - -echo "==> Running kiosk post-install hook..." - -# Install Python packages -pip3 install --break-system-packages PyQt5 websocket-client - -# Clone repo -if [ ! -d /home/jgitta/kiosk-browser ]; then - git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git /home/jgitta/kiosk-browser - cd /home/jgitta/kiosk-browser - bash install-kiosk.sh +# ── Dependencies ─────────────────────────────────────────────────── +echo "==> Checking dependencies..." +MISSING="" +for pkg in xorriso isolinux curl; do + dpkg -l "$pkg" &>/dev/null || MISSING="$MISSING $pkg" +done +if [ -n "$MISSING" ]; then + apt-get install -y $MISSING fi -echo "==> Kiosk post-install complete" -HOOK -chmod +x config/hooks/normal/9999-kiosk-postinstall.chroot - -echo "==> Building ISO (this may take 5-10 minutes)..." -lb build 2>&1 | tail -20 - -ISO_FILE="live-image-amd64.hybrid.iso" -if [ -f "$ISO_FILE" ]; then - cp "$ISO_FILE" "$OUTPUT_DIR/seniornet-kiosk.iso" - echo - echo "==> ✓ ISO build complete!" - echo " Output: $OUTPUT_DIR/seniornet-kiosk.iso" - echo - echo "Next steps:" - echo " 1. Write to USB:" - echo " sudo dd if=$OUTPUT_DIR/seniornet-kiosk.iso of=/dev/sdX bs=4M status=progress && sync" - echo - echo " 2. Boot from USB and wait ~10 minutes for silent installation" - echo - echo " 3. Machine will reboot into kiosk mode automatically" - echo +# ── Download Debian netinstall ISO (cached) ──────────────────────── +if [ -f "$ISO_CACHE" ]; then + echo "==> Using cached Debian ISO: $ISO_CACHE" else - echo "ERROR: ISO build failed" - exit 1 + echo "==> Downloading Debian Trixie netinstall (~700 MB)..." + curl -L --progress-bar "$DEBIAN_ISO_URL" -o "$ISO_CACHE" fi + +# ── Extract ISO ──────────────────────────────────────────────────── +echo "==> Extracting ISO..." +mkdir -p "$WORK_DIR/iso" +xorriso -osirrox on -indev "$ISO_CACHE" -extract / "$WORK_DIR/iso" 2>/dev/null +chmod -R u+w "$WORK_DIR/iso" + +# ── Add preseed to ISO root ──────────────────────────────────────── +# Accessible during install as /cdrom/preseed.cfg +cp "$REPO_DIR/preseed.cfg" "$WORK_DIR/iso/preseed.cfg" + +# ── Auto-boot: GRUB (UEFI) ──────────────────────────────────────── +echo "==> Configuring auto-boot..." +cat > "$WORK_DIR/iso/boot/grub/grub.cfg" << 'GRUBEOF' +set default=0 +set timeout=5 + +menuentry "SeniorNet Kiosk — Automatic Install" { + linux /install.amd/vmlinuz preseed/file=/cdrom/preseed.cfg auto=true priority=critical quiet --- + initrd /install.amd/initrd.gz +} + +menuentry "SeniorNet Kiosk — Install (verbose)" { + linux /install.amd/vmlinuz preseed/file=/cdrom/preseed.cfg auto=true priority=critical --- + initrd /install.amd/initrd.gz +} +GRUBEOF + +# ── Auto-boot: isolinux (BIOS legacy) ───────────────────────────── +if [ -f "$WORK_DIR/iso/isolinux/isolinux.cfg" ]; then + cat > "$WORK_DIR/iso/isolinux/isolinux.cfg" << 'ISOEOF' +default install +timeout 50 + +label install + menu label SeniorNet Kiosk - Automatic Install + kernel /install.amd/vmlinuz + append initrd=/install.amd/initrd.gz preseed/file=/cdrom/preseed.cfg auto=true priority=critical quiet --- + +label verbose + menu label SeniorNet Kiosk - Install (verbose) + kernel /install.amd/vmlinuz + append initrd=/install.amd/initrd.gz preseed/file=/cdrom/preseed.cfg auto=true priority=critical --- +ISOEOF +fi + +# ── Repack ISO (UEFI + BIOS hybrid) ─────────────────────────────── +echo "==> Building final ISO..." +cd "$WORK_DIR/iso" + +xorriso -as mkisofs \ + -r -J -joliet-long \ + -V "SeniorNet Kiosk" \ + --grub2-mbr boot/grub/i386-pc/boot_hybrid.img \ + -partition_offset 16 \ + --mbr-force-bootable \ + -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b boot/grub/efi.img \ + -appended_part_as_gpt \ + -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \ + -c '/boot/boot.cat' \ + -b '/boot/grub/i386-pc/eltorito.img' \ + -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \ + -eltorito-alt-boot \ + -e '--interval:appended_partition_2:::' \ + -no-emul-boot \ + -o "$OUTPUT_ISO" \ + . 2>&1 | tail -5 + +echo +SIZE=$(du -sh "$OUTPUT_ISO" | cut -f1) +echo "==> ✓ ISO complete: $OUTPUT_ISO ($SIZE)" +echo +echo "Write to USB:" +echo " sudo dd if=\"$OUTPUT_ISO\" of=/dev/sdX bs=4M status=progress && sync" +echo +echo "Boot from USB — Debian installs silently (~10 min) then reboots." +echo "On first reboot: setup wizard prompts for username and senior's name." +echo "Second reboot: kiosk is running." diff --git a/install-kiosk.sh b/install-kiosk.sh index 07ce0a6..2e3faf5 100755 --- a/install-kiosk.sh +++ b/install-kiosk.sh @@ -1,161 +1,163 @@ #!/bin/bash set -e -# Post-install script for SeniorNet Kiosk ISO -# Runs after Debian installation completes +# SeniorNet Kiosk setup script. +# Runs on first boot via kiosk-firstboot.service (which gives it a real TTY). +# Can also be run manually: sudo bash install-kiosk.sh -echo "==> SeniorNet Kiosk Post-Install" +REPO_BASE="https://gitea.jgitta.com/jgitta/senior-kiosk/raw/branch/master" + +clear +echo "============================================" +echo " SeniorNet Kiosk — First-Boot Setup" +echo "============================================" echo -REPO_DIR="/home/jgitta/kiosk-browser" -HOME_DIR="/home/jgitta/kiosk-home" -CHROMIUM_PROFILE="/home/jgitta/chromium-kiosk" +# ── Prompt for user details ──────────────────────────────────────── +while true; do + read -rp "Enter a username for this kiosk (e.g. mary): " KIOSK_USER + KIOSK_USER=$(echo "$KIOSK_USER" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9_-') + if [ -n "$KIOSK_USER" ]; then break; fi + echo " Invalid — use only letters, numbers, hyphens, underscores." +done -# Ensure jgitta user exists -if ! id "jgitta" &>/dev/null; then - useradd -m -s /bin/bash jgitta +while true; do + read -rp "Enter the senior's display name (e.g. Mary Smith): " KIOSK_NAME + if [ -n "$KIOSK_NAME" ]; then break; fi + echo " Name cannot be empty." +done + +echo +echo " Username : $KIOSK_USER" +echo " Name : $KIOSK_NAME" +echo +read -rp "Proceed with setup? [Y/n] " CONFIRM +if [[ "$CONFIRM" =~ ^[Nn] ]]; then echo "Aborted."; exit 1; fi +echo + +# ── Paths ────────────────────────────────────────────────────────── +KIOSK_HOME="/home/$KIOSK_USER" +REPO_DIR="$KIOSK_HOME/kiosk-browser" +HOME_DIR="$KIOSK_HOME/kiosk-home" +CHROMIUM_PROFILE="$KIOSK_HOME/chromium-kiosk" + +# ── User ─────────────────────────────────────────────────────────── +echo "==> Creating user $KIOSK_USER..." +if ! id "$KIOSK_USER" &>/dev/null; then + useradd -m -s /bin/bash -c "$KIOSK_NAME" "$KIOSK_USER" fi -# Install packages -echo "==> Installing dependencies..." -apt-get update -apt-get install -y python3 python3-pip chromium openbox xserver-xorg xserver-xorg-video-dummy xinit xdotool xprop git sqlite3 xvfb 2>&1 | grep -v "^Reading" +# ── Packages ─────────────────────────────────────────────────────── +echo "==> Installing packages..." +apt-get update -qq +apt-get install -y \ + chromium \ + openbox \ + lightdm \ + xserver-xorg \ + xdotool \ + xprop \ + python3 \ + python3-pyqt5 \ + python3-pip \ + sqlite3 \ + git \ + curl \ + openssh-server \ + 2>&1 | grep -Ev "^(Reading|Selecting|Preparing|Unpacking|Setting|Processing|Triggers|Get)" -# Install Python packages -echo "==> Installing Python packages..." -pip3 install --break-system-packages PyQt5 websocket-client 2>&1 | tail -5 +echo "==> Installing websocket-client..." +pip3 install --break-system-packages websocket-client 2>&1 | tail -2 -# Clone repo -echo "==> Cloning repository..." +# ── Kiosk repo ───────────────────────────────────────────────────── +echo "==> Cloning kiosk repo..." if [ ! -d "$REPO_DIR" ]; then git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git "$REPO_DIR" fi -cd "$REPO_DIR" - -# Deploy home screen with config +# ── Home screen ──────────────────────────────────────────────────── echo "==> Deploying home screen..." mkdir -p "$HOME_DIR" -if [ -f config.json ] && [ -f home-screen-index.html ]; then - config_json=$(cat config.json | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ') - sed "s|const config = {};|const config = $config_json;|" home-screen-index.html > "$HOME_DIR/index.html" - chown jgitta:jgitta "$HOME_DIR/index.html" -fi +config_json=$(cat "$REPO_DIR/config.json" | tr '\n' ' ') +sed "s|const config = {};|const config = $config_json;|" \ + "$REPO_DIR/home-screen-index.html" > "$HOME_DIR/index.html" -# Create Chromium profile directory +# ── Chromium profile ─────────────────────────────────────────────── mkdir -p "$CHROMIUM_PROFILE" -chown -R jgitta:jgitta "$CHROMIUM_PROFILE" -# Systemd units -echo "==> Installing systemd units..." +# ── LightDM auto-login ───────────────────────────────────────────── +echo "==> Configuring LightDM auto-login..." +mkdir -p /etc/lightdm/lightdm.conf.d +cat > /etc/lightdm/lightdm.conf.d/50-kiosk.conf << EOF +[Seat:*] +autologin-user=$KIOSK_USER +autologin-user-timeout=0 +user-session=openbox +EOF -cat > /etc/systemd/system/kiosk-display.service << 'UNIT1' -[Unit] -Description=SeniorNet Kiosk Display -Before=kiosk.service -After=network.target - -[Service] -Type=forking -User=jgitta -ExecStart=/bin/bash -c 'Xvfb :0 -screen 0 1920x1080x24 -ac &' -ExecStartPost=/bin/sleep 2 -ExecStartPost=/bin/bash -c 'DISPLAY=:0 su - jgitta -c "openbox &"' -ExecStartPost=/bin/sleep 1 - -[Install] -WantedBy=multi-user.target -UNIT1 - -cat > /etc/systemd/system/kiosk.service << 'UNIT2' -[Unit] -Description=SeniorNet Kiosk -After=network.target kiosk-display.service -Wants=kiosk-display.service - -[Service] -Type=simple -User=jgitta -Environment="DISPLAY=:0" -WorkingDirectory=/home/jgitta/kiosk-browser -ExecStart=/usr/bin/python3 /home/jgitta/kiosk-browser/browser.py -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -UNIT2 - -systemctl daemon-reload -systemctl enable kiosk-display.service kiosk.service - -# kiosk-restart helper -cat > /usr/local/bin/kiosk-restart << 'HELPER' -#!/bin/bash -pkill -f "remote-debugging-port=9222" || true -pkill -f "python3.*browser" || true -sleep 1 -systemctl restart kiosk.service -HELPER -chmod +x /usr/local/bin/kiosk-restart - -# Openbox config +# ── Openbox ──────────────────────────────────────────────────────── echo "==> Configuring Openbox..." -mkdir -p /home/jgitta/.config/openbox +mkdir -p "$KIOSK_HOME/.config/openbox" -cat > /home/jgitta/.config/openbox/rc.xml << 'OPENBOX' +cat > "$KIOSK_HOME/.config/openbox/rc.xml" << 'EOF' -OPENBOX +EOF -cat > /home/jgitta/.xinitrc << 'XINITRC' +cat > "$KIOSK_HOME/.config/openbox/autostart" << 'EOF' +(while true; do + python3 ~/kiosk-browser/browser.py + sleep 2 +done) & +EOF + +# ── kiosk-restart helper ─────────────────────────────────────────── +cat > /usr/local/bin/kiosk-restart << 'EOF' #!/bin/bash -exec openbox -XINITRC -chmod +x /home/jgitta/.xinitrc - -# X11 hardening -echo "==> Configuring X11..." -cat > /etc/X11/xorg.conf.d/50-kiosk.conf << 'XORG' -Section "Device" - Identifier "Dummy0" - Driver "dummy" -EndSection - -Section "Monitor" - Identifier "Monitor0" - HorizSync 30-80 - VertRefresh 30-75 -EndSection - -Section "Screen" - Identifier "Screen0" - Device "Dummy0" - Monitor "Monitor0" - DefaultDepth 24 - SubSection "Display" - Depth 24 - Modes "1920x1080" - EndSubSection -EndSection +pkill -f "remote-debugging-port=9222" || true +pkill -f "python3.*browser" || true +sleep 1 +echo "Kiosk restarted — new code is live" +EOF +chmod +x /usr/local/bin/kiosk-restart +# ── X11 hardening ────────────────────────────────────────────────── +echo "==> Hardening X11..." +mkdir -p /etc/X11/xorg.conf.d +cat > /etc/X11/xorg.conf.d/50-kiosk.conf << 'EOF' Section "ServerFlags" Option "DontVTSwitch" "true" - Option "BlankTime" "0" - Option "StandbyTime" "0" + Option "DontZap" "true" + Option "BlankTime" "0" + Option "StandbyTime" "0" EndSection -XORG +EOF -# Permissions +# ── SSH: allow root login for admin ─────────────────────────────── +sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config + +# ── Permissions ──────────────────────────────────────────────────── echo "==> Setting permissions..." -chown -R jgitta:jgitta /home/jgitta/.config /home/jgitta/.xinitrc "$REPO_DIR" "$HOME_DIR" +chown -R "$KIOSK_USER:$KIOSK_USER" \ + "$KIOSK_HOME/.config" \ + "$REPO_DIR" \ + "$HOME_DIR" \ + "$CHROMIUM_PROFILE" -# Enable getty on ttys for troubleshooting (optional, comment out for full lockdown) -systemctl set-default multi-user.target +# ── Enable LightDM, remove first-boot service ───────────────────── +systemctl enable lightdm +systemctl set-default graphical.target +systemctl disable kiosk-firstboot 2>/dev/null || true +rm -f /etc/systemd/system/kiosk-firstboot.service +systemctl daemon-reload echo -echo "==> Post-install complete" -echo " Machine will auto-start kiosk on next boot" +echo "============================================" +echo " Setup complete!" +echo " Kiosk will start after reboot as: $KIOSK_NAME ($KIOSK_USER)" +echo "============================================" echo +read -rp "Press Enter to reboot..." +reboot diff --git a/preseed-bootstrap.sh b/preseed-bootstrap.sh new file mode 100644 index 0000000..5846cdb --- /dev/null +++ b/preseed-bootstrap.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# Downloaded and run by the preseed late_command after Debian base install. +# Fetches install-kiosk.sh and installs it as a first-boot setup service. +# The service runs interactively on the first reboot, prompts for name/username, +# then installs the kiosk and reboots into kiosk mode. + +REPO_BASE="https://gitea.jgitta.com/jgitta/senior-kiosk/raw/branch/master" + +echo "==> SeniorNet: downloading setup script..." +curl -fsSL "$REPO_BASE/install-kiosk.sh" -o /usr/local/bin/kiosk-setup +chmod +x /usr/local/bin/kiosk-setup + +echo "==> SeniorNet: installing first-boot service..." +cat > /etc/systemd/system/kiosk-firstboot.service << 'EOF' +[Unit] +Description=SeniorNet Kiosk First-Boot Setup +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/kiosk-setup +StandardInput=tty +TTYPath=/dev/tty1 +TTYReset=yes +IgnoreSIGPIPE=no +SendSIGHUP=yes +RemainAfterExit=no + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable kiosk-firstboot +# Boot to text mode for the first-boot wizard, then kiosk setup switches to graphical +systemctl set-default multi-user.target + +echo "==> SeniorNet: ready. First-boot setup will run on next reboot." diff --git a/preseed.cfg b/preseed.cfg index 920872c..b93e332 100644 --- a/preseed.cfg +++ b/preseed.cfg @@ -1,58 +1,65 @@ # Debian Preseed — SeniorNet Kiosk -# Silent/unattended installer configuration +# Silent unattended Debian base install. +# After install, downloads preseed-bootstrap.sh which installs a first-boot +# setup wizard. On first reboot the wizard prompts for username/name, +# installs the kiosk, then reboots into kiosk mode. -# Locale and keyboard +# ── Locale / keyboard ────────────────────────────────────────────── d-i debian-installer/locale string en_US.UTF-8 d-i keyboard-configuration/xkb-keymap select us -# Network +# ── Network ──────────────────────────────────────────────────────── d-i netcfg/choose_interface select auto d-i netcfg/get_hostname string kiosk d-i netcfg/get_domain string local -d-i netcfg/hostname string kiosk -# Mirror +# ── Mirror ───────────────────────────────────────────────────────── d-i mirror/country string manual d-i mirror/http/hostname string deb.debian.org d-i mirror/http/directory string /debian d-i mirror/http/proxy string -# Partitioning — use entire disk -d-i partman-auto/method string lvm -d-i partman-lvm/device_remove_lvm boolean true -d-i partman-auto/purge_lvm_confirmation boolean true -d-i partman-auto/disk string /dev/sda -d-i partman-auto/choose_recipe select atomic -d-i partman/confirm_write_new_label boolean true -d-i partman/confirm boolean true - -# Clock/timezone +# ── Clock / timezone ─────────────────────────────────────────────── d-i time/zone string UTC d-i clock-setup/utc boolean true d-i clock-setup/ntp boolean true -# Account setup — root with no password +# ── Accounts ─────────────────────────────────────────────────────── +# Root only — install-kiosk.sh creates the kiosk user interactively. d-i passwd/root-login boolean true d-i passwd/make-user boolean false d-i passwd/root-password password kiosk d-i passwd/root-password-again password kiosk -# APT +# ── Disk — auto-detect first real disk ───────────────────────────── +# Handles /dev/sda, /dev/nvme0n1, /dev/vda, etc. +d-i preseed/early_command string \ + DISK=$(ls /sys/block/ | grep -Ev '^(loop|sr|fd|ram)' | head -1); \ + debconf-set partman-auto/disk "/dev/$DISK"; \ + debconf-set grub-installer/bootdev "/dev/$DISK" + +d-i partman-auto/method string regular +d-i partman-auto/choose_recipe select atomic +d-i partman/confirm_write_new_label boolean true +d-i partman/confirm boolean true +d-i partman/confirm_nooverwrite boolean true + +# ── APT ──────────────────────────────────────────────────────────── d-i apt-setup/services-select multiselect security, updates d-i apt-setup/security_host string security.debian.org -# Package selection +# ── Package selection ────────────────────────────────────────────── tasksel tasksel/first multiselect standard, ssh-server -d-i pkgsel/include string openssh-server build-essential git curl wget +d-i pkgsel/include string curl git -# Grub bootloader +# ── Bootloader ───────────────────────────────────────────────────── d-i grub-installer/only boolean true d-i grub-installer/bootdev string default -# Finish installation -d-i finish-install/reboot_in_background boolean true - -# Run post-install script +# ── Post-install bootstrap ───────────────────────────────────────── +# Downloads preseed-bootstrap.sh which installs the first-boot setup service. d-i preseed/late_command string \ - in-target bash -c 'curl -fsSL https://gitea.jgitta.com/jgitta/senior-kiosk/raw/master/install-kiosk.sh | bash' || \ - in-target bash -c 'cd /root && git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git && bash senior-kiosk/install-kiosk.sh' + in-target bash -c 'curl -fsSL https://gitea.jgitta.com/jgitta/senior-kiosk/raw/branch/master/preseed-bootstrap.sh | bash' + +# ── Finish ───────────────────────────────────────────────────────── +d-i finish-install/reboot_in_background boolean true