feat: generic user, dynamic paths, simplified ISO build pipeline
This commit is contained in:
+12
-7
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user