feat: generic user, dynamic paths, simplified ISO build pipeline

This commit is contained in:
2026-05-23 14:26:25 +00:00
parent 312db58775
commit cb5bf7ff94
5 changed files with 319 additions and 260 deletions
+12 -7
View File
@@ -14,8 +14,9 @@ from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView QTreeWidget, QTreeWidgetItem, QAbstractItemView
) )
HOME_URL = "file:///home/jgitta/kiosk-home/index.html" _HOME = pathlib.Path.home()
BOOKMARKS_FILE = "/home/jgitta/kiosk-browser/bookmarks.json" HOME_URL = f"file://{_HOME}/kiosk-home/index.html"
BOOKMARKS_FILE = str(_HOME / "kiosk-browser/bookmarks.json")
CDP_PORT = 9222 CDP_PORT = 9222
TOOLBAR_H = 72 # single row — was 142 (82 + 60) TOOLBAR_H = 72 # single row — was 142 (82 + 60)
# Screen size is detected dynamically in main() after QApplication starts # Screen size is detected dynamically in main() after QApplication starts
@@ -736,7 +737,7 @@ class KioskOverlay(QWidget):
# ── Chrome launcher ─────────────────────────────────────────────── # ── Chrome launcher ───────────────────────────────────────────────
def launch_chrome(): def launch_chrome():
profile_dir = pathlib.Path("/home/jgitta/chromium-kiosk") profile_dir = _HOME / "chromium-kiosk"
profile_dir.mkdir(parents=True, exist_ok=True) profile_dir.mkdir(parents=True, exist_ok=True)
for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"): for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"):
@@ -767,12 +768,16 @@ def launch_chrome():
cookies_db = profile_dir / "Default" / "Cookies" cookies_db = profile_dir / "Default" / "Cookies"
if cookies_db.exists(): if cookies_db.exists():
try: 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 = sqlite3.connect(str(cookies_db))
conn.execute( if _email_host:
"DELETE FROM cookies WHERE host_key LIKE '%mail.jgitta.com%'") conn.execute("DELETE FROM cookies WHERE host_key LIKE ?",
(f"%{_email_host}%",))
conn.commit() conn.commit()
print(f"Cleared stale session cookie for {_email_host}")
conn.close() conn.close()
print("Cleared stale Roundcube session cookie")
except Exception as e: except Exception as e:
print(f"Cookie clear warning: {e}") print(f"Cookie clear warning: {e}")
@@ -789,7 +794,7 @@ def launch_chrome():
"--disable-extensions", "--disable-extensions",
"--disable-gpu", "--disable-gpu",
"--disable-dev-shm-usage", "--disable-dev-shm-usage",
"--user-data-dir=/home/jgitta/chromium-kiosk", f"--user-data-dir={_HOME / 'chromium-kiosk'}",
f"--remote-debugging-port={CDP_PORT}", f"--remote-debugging-port={CDP_PORT}",
"--remote-allow-origins=*", "--remote-allow-origins=*",
f"--window-position=0,{TOOLBAR_H}", f"--window-position=0,{TOOLBAR_H}",
+109 -104
View File
@@ -1,119 +1,124 @@
#!/bin/bash #!/bin/bash
set -e set -e
# Build SeniorNet Kiosk ISO # SeniorNet Kiosk ISO Builder
# Run on workstation: ./build-iso.sh # Takes the official Debian Trixie netinstall ISO and repacks it with:
# Requires: debian-live-build, sudo # - preseed.cfg embedded for silent auto-install
# - GRUB + isolinux configured for auto-boot (UEFI + BIOS)
echo "==> SeniorNet Kiosk ISO Builder" #
echo # 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 if [ "$EUID" -ne 0 ]; then
echo "ERROR: Run as root (sudo ./build-iso.sh)" echo "ERROR: Run as root: sudo ./build-iso.sh"; exit 1
exit 1
fi 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)" 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" DEBIAN_ISO_URL="https://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/amd64/iso-cd/debian-testing-amd64-netinst.iso"
cd "$BUILD_DIR" ISO_CACHE="$OUTPUT_DIR/debian-netinst.iso"
OUTPUT_ISO="$OUTPUT_DIR/seniornet-kiosk.iso"
echo "==> Initializing live-build..." mkdir -p "$OUTPUT_DIR"
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
echo "==> Adding packages..." # ── Dependencies ───────────────────────────────────────────────────
mkdir -p config/package-lists echo "==> Checking dependencies..."
cat > config/package-lists/kiosk.list.chroot << 'PKGLIST' MISSING=""
# Base system for pkg in xorriso isolinux curl; do
openssh-server dpkg -l "$pkg" &>/dev/null || MISSING="$MISSING $pkg"
build-essential done
git if [ -n "$MISSING" ]; then
curl apt-get install -y $MISSING
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
fi fi
echo "==> Kiosk post-install complete" # ── Download Debian netinstall ISO (cached) ────────────────────────
HOOK if [ -f "$ISO_CACHE" ]; then
chmod +x config/hooks/normal/9999-kiosk-postinstall.chroot echo "==> Using cached Debian ISO: $ISO_CACHE"
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
else else
echo "ERROR: ISO build failed" echo "==> Downloading Debian Trixie netinstall (~700 MB)..."
exit 1 curl -L --progress-bar "$DEBIAN_ISO_URL" -o "$ISO_CACHE"
fi 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."
+122 -120
View File
@@ -1,161 +1,163 @@
#!/bin/bash #!/bin/bash
set -e set -e
# Post-install script for SeniorNet Kiosk ISO # SeniorNet Kiosk setup script.
# Runs after Debian installation completes # 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 echo
REPO_DIR="/home/jgitta/kiosk-browser" # ── Prompt for user details ────────────────────────────────────────
HOME_DIR="/home/jgitta/kiosk-home" while true; do
CHROMIUM_PROFILE="/home/jgitta/chromium-kiosk" 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 while true; do
if ! id "jgitta" &>/dev/null; then read -rp "Enter the senior's display name (e.g. Mary Smith): " KIOSK_NAME
useradd -m -s /bin/bash jgitta 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 fi
# Install packages # ── Packages ───────────────────────────────────────────────────────
echo "==> Installing dependencies..." echo "==> Installing packages..."
apt-get update apt-get update -qq
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" 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 websocket-client..."
echo "==> Installing Python packages..." pip3 install --break-system-packages websocket-client 2>&1 | tail -2
pip3 install --break-system-packages PyQt5 websocket-client 2>&1 | tail -5
# Clone repo # ── Kiosk repo ─────────────────────────────────────────────────────
echo "==> Cloning repository..." echo "==> Cloning kiosk repo..."
if [ ! -d "$REPO_DIR" ]; then if [ ! -d "$REPO_DIR" ]; then
git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git "$REPO_DIR" git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git "$REPO_DIR"
fi fi
cd "$REPO_DIR" # ── Home screen ────────────────────────────────────────────────────
# Deploy home screen with config
echo "==> Deploying home screen..." echo "==> Deploying home screen..."
mkdir -p "$HOME_DIR" mkdir -p "$HOME_DIR"
if [ -f config.json ] && [ -f home-screen-index.html ]; then config_json=$(cat "$REPO_DIR/config.json" | tr '\n' ' ')
config_json=$(cat config.json | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ') sed "s|const config = {};|const config = $config_json;|" \
sed "s|const config = {};|const config = $config_json;|" home-screen-index.html > "$HOME_DIR/index.html" "$REPO_DIR/home-screen-index.html" > "$HOME_DIR/index.html"
chown jgitta:jgitta "$HOME_DIR/index.html"
fi
# Create Chromium profile directory # ── Chromium profile ───────────────────────────────────────────────
mkdir -p "$CHROMIUM_PROFILE" mkdir -p "$CHROMIUM_PROFILE"
chown -R jgitta:jgitta "$CHROMIUM_PROFILE"
# Systemd units # ── LightDM auto-login ─────────────────────────────────────────────
echo "==> Installing systemd units..." 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' # ── Openbox ────────────────────────────────────────────────────────
[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
echo "==> Configuring 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'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<openbox_config xmlns="http://openbox.org/3.4/rc"> <openbox_config xmlns="http://openbox.org/3.4/rc">
<startup/> <startup/>
</openbox_config> </openbox_config>
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 #!/bin/bash
exec openbox pkill -f "remote-debugging-port=9222" || true
XINITRC pkill -f "python3.*browser" || true
chmod +x /home/jgitta/.xinitrc sleep 1
echo "Kiosk restarted — new code is live"
# X11 hardening EOF
echo "==> Configuring X11..." chmod +x /usr/local/bin/kiosk-restart
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
# ── X11 hardening ──────────────────────────────────────────────────
echo "==> Hardening X11..."
mkdir -p /etc/X11/xorg.conf.d
cat > /etc/X11/xorg.conf.d/50-kiosk.conf << 'EOF'
Section "ServerFlags" Section "ServerFlags"
Option "DontVTSwitch" "true" Option "DontVTSwitch" "true"
Option "DontZap" "true"
Option "BlankTime" "0" Option "BlankTime" "0"
Option "StandbyTime" "0" Option "StandbyTime" "0"
EndSection EndSection
XORG EOF
# Permissions # ── SSH: allow root login for admin ───────────────────────────────
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
# ── Permissions ────────────────────────────────────────────────────
echo "==> Setting 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) # ── Enable LightDM, remove first-boot service ─────────────────────
systemctl set-default multi-user.target 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
echo "==> Post-install complete" echo "============================================"
echo " Machine will auto-start kiosk on next boot" echo " Setup complete!"
echo " Kiosk will start after reboot as: $KIOSK_NAME ($KIOSK_USER)"
echo "============================================"
echo echo
read -rp "Press Enter to reboot..."
reboot
+40
View File
@@ -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."
+33 -26
View File
@@ -1,58 +1,65 @@
# Debian Preseed — SeniorNet Kiosk # 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 debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us d-i keyboard-configuration/xkb-keymap select us
# Network # ── Network ────────────────────────────────────────────────────────
d-i netcfg/choose_interface select auto d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string kiosk d-i netcfg/get_hostname string kiosk
d-i netcfg/get_domain string local d-i netcfg/get_domain string local
d-i netcfg/hostname string kiosk
# Mirror # ── Mirror ─────────────────────────────────────────────────────────
d-i mirror/country string manual d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian d-i mirror/http/directory string /debian
d-i mirror/http/proxy string d-i mirror/http/proxy string
# Partitioning — use entire disk # ── Clock / timezone ───────────────────────────────────────────────
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
d-i time/zone string UTC d-i time/zone string UTC
d-i clock-setup/utc boolean true d-i clock-setup/utc boolean true
d-i clock-setup/ntp 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/root-login boolean true
d-i passwd/make-user boolean false d-i passwd/make-user boolean false
d-i passwd/root-password password kiosk d-i passwd/root-password password kiosk
d-i passwd/root-password-again 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/services-select multiselect security, updates
d-i apt-setup/security_host string security.debian.org d-i apt-setup/security_host string security.debian.org
# Package selection # ── Package selection ──────────────────────────────────────────────
tasksel tasksel/first multiselect standard, ssh-server 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/only boolean true
d-i grub-installer/bootdev string default d-i grub-installer/bootdev string default
# Finish installation # ── Post-install bootstrap ─────────────────────────────────────────
d-i finish-install/reboot_in_background boolean true # Downloads preseed-bootstrap.sh which installs the first-boot setup service.
# Run post-install script
d-i preseed/late_command string \ 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 'curl -fsSL https://gitea.jgitta.com/jgitta/senior-kiosk/raw/branch/master/preseed-bootstrap.sh | bash'
in-target bash -c 'cd /root && git clone https://gitea.jgitta.com/jgitta/senior-kiosk.git && bash senior-kiosk/install-kiosk.sh'
# ── Finish ─────────────────────────────────────────────────────────
d-i finish-install/reboot_in_background boolean true