diff --git a/image/build-live-usb.sh b/image/build-live-usb.sh index 5e9d1d67..21222ece 100755 --- a/image/build-live-usb.sh +++ b/image/build-live-usb.sh @@ -797,8 +797,8 @@ log "5/8 Installing SecuBox scripts..." mkdir -p "${ROOTFS}/usr/sbin" mkdir -p "${ROOTFS}/usr/lib/secubox" -# Copy scripts -for script in secubox-net-detect secubox-kiosk-setup secubox-cmdline-handler; do +# Copy scripts (including kiosk-launcher for robust startup) +for script in secubox-net-detect secubox-kiosk-setup secubox-cmdline-handler secubox-kiosk-launcher; do if [[ -f "${SCRIPT_DIR}/sbin/${script}" ]]; then cp "${SCRIPT_DIR}/sbin/${script}" "${ROOTFS}/usr/sbin/" chmod +x "${ROOTFS}/usr/sbin/${script}" @@ -811,9 +811,9 @@ if [[ -f "${SCRIPT_DIR}/firstboot.sh" ]]; then chmod +x "${ROOTFS}/usr/lib/secubox/firstboot.sh" fi -# Systemd services +# Systemd services (including wayland variant for kiosk) mkdir -p "${ROOTFS}/etc/systemd/system" -for svc in secubox-net-detect secubox-cmdline secubox-kiosk; do +for svc in secubox-net-detect secubox-cmdline secubox-kiosk secubox-kiosk-wayland; do if [[ -f "${SCRIPT_DIR}/systemd/${svc}.service" ]]; then cp "${SCRIPT_DIR}/systemd/${svc}.service" "${ROOTFS}/etc/systemd/system/" fi @@ -877,28 +877,26 @@ XWRAP cat > "${ROOTFS}/home/secubox-kiosk/.xinitrc" <<'XINITRC' #!/bin/bash # SecuBox Kiosk X11 Launcher +# Note: secubox-kiosk-launcher already waits for services before starting -# Wait for services to be ready -for i in {1..30}; do - if curl -sk https://192.168.255.1:9443/ >/dev/null 2>&1; then - break - fi - sleep 1 -done - -# Kiosk URL (local SecuBox WebUI via dummy interface) +# URL from environment (set by launcher) or fallback URL="${KIOSK_URL:-https://192.168.255.1:9443/}" -# Disable screen blanking -xset s off -xset -dpms -xset s noblank +# Log startup +logger -t secubox-kiosk "Starting Chromium X11 kiosk: $URL" + +# Disable screen blanking (ignore errors on headless) +xset s off 2>/dev/null || true +xset -dpms 2>/dev/null || true +xset s noblank 2>/dev/null || true # Hide cursor after 3 seconds of inactivity -unclutter -idle 3 -root & +if command -v unclutter &>/dev/null; then + unclutter -idle 3 -root & +fi # Set background to black -xsetroot -solid black +xsetroot -solid black 2>/dev/null || true # Chromium flags for kiosk mode (X11) CHROMIUM_FLAGS=( @@ -939,55 +937,13 @@ XINITRC touch "${ROOTFS}/var/lib/secubox/.kiosk-enabled" echo "x11" > "${ROOTFS}/var/lib/secubox/.kiosk-mode" - # Copy X11 kiosk service (not Wayland/Cage) - cat > "${ROOTFS}/etc/systemd/system/secubox-kiosk.service" <<'KIOSK_SVC' -[Unit] -Description=SecuBox Kiosk Mode (X11/Chromium) -After=systemd-user-sessions.service plymouth-quit-wait.service -Wants=network-online.target -Conflicts=getty@tty1.service - -[Service] -Type=simple - -# Check if kiosk is enabled -ExecStartPre=+/bin/sh -c '[ -f /var/lib/secubox/.kiosk-enabled ] || exit 1' - -# Run on tty7 -TTYPath=/dev/tty7 -TTYReset=yes -TTYVHangup=yes -TTYVTDisallocate=yes - -# Create runtime directory AS ROOT -ExecStartPre=+/bin/mkdir -p /run/user/1000 -ExecStartPre=+/bin/chown 1000:1000 /run/user/1000 -ExecStartPre=+/bin/chmod 700 /run/user/1000 - -# Switch VT to tty7 -ExecStartPre=+/bin/chvt 7 - -User=secubox-kiosk -Group=secubox-kiosk -SupplementaryGroups=video audio input render tty - -Environment=XDG_RUNTIME_DIR=/run/user/1000 -Environment=XDG_SESSION_TYPE=x11 -Environment=DISPLAY=:0 -Environment=HOME=/home/secubox-kiosk -Environment=KIOSK_URL=https://192.168.255.1:9443/ - -ExecStart=/usr/bin/startx /home/secubox-kiosk/.xinitrc -- :0 vt7 -nolisten tcp - -Restart=on-failure -RestartSec=5 -TimeoutStartSec=90 -MemoryMax=1G -TasksMax=200 - -[Install] -WantedBy=graphical.target -KIOSK_SVC + # Use the fixed kiosk service (already copied from systemd/ directory) + # The service uses secubox-kiosk-launcher which handles: + # - Dynamic UID detection (no hardcoded 1000) + # - Proper dependency waiting (nginx, network) + # - User creation if needed + # - Fallback URL handling + log "Kiosk service already copied from systemd/ directory" # Enable kiosk service and set graphical target chroot "${ROOTFS}" systemctl enable secubox-kiosk.service 2>/dev/null || true diff --git a/image/sbin/secubox-kiosk-launcher b/image/sbin/secubox-kiosk-launcher new file mode 100755 index 00000000..fb20ec83 --- /dev/null +++ b/image/sbin/secubox-kiosk-launcher @@ -0,0 +1,287 @@ +#!/bin/bash +# ══════════════════════════════════════════════════════════════════ +# secubox-kiosk-launcher — Robust Kiosk Startup Wrapper +# Handles all dependencies before starting X11/Chromium +# Fixes race conditions and provides proper error handling +# ══════════════════════════════════════════════════════════════════ +set -u + +# Configuration +KIOSK_USER="${KIOSK_USER:-secubox-kiosk}" +KIOSK_HOME="${KIOSK_HOME:-/home/secubox-kiosk}" +KIOSK_URL="${KIOSK_URL:-https://192.168.255.1:9443/}" +FALLBACK_URL="${FALLBACK_URL:-https://127.0.0.1/}" +MAX_WAIT="${MAX_WAIT:-60}" +LOG_TAG="secubox-kiosk" + +# Logging +log() { + echo "[kiosk-launcher] $*" + logger -t "$LOG_TAG" "$*" 2>/dev/null || true +} + +err() { + echo "[kiosk-launcher] ERROR: $*" >&2 + logger -t "$LOG_TAG" -p err "$*" 2>/dev/null || true +} + +# ── Step 1: Verify kiosk user exists ───────────────────────────── +verify_user() { + log "Checking kiosk user..." + + if ! id "$KIOSK_USER" &>/dev/null; then + log "Creating kiosk user $KIOSK_USER..." + useradd -r -m -d "$KIOSK_HOME" -s /usr/sbin/nologin \ + -G video,audio,input,render,tty "$KIOSK_USER" 2>/dev/null || { + err "Failed to create kiosk user" + return 1 + } + fi + + # Get actual UID + KIOSK_UID=$(id -u "$KIOSK_USER") + KIOSK_GID=$(id -g "$KIOSK_USER") + export KIOSK_UID KIOSK_GID + + log "Kiosk user: $KIOSK_USER (UID=$KIOSK_UID)" + return 0 +} + +# ── Step 2: Setup XDG runtime directory ────────────────────────── +setup_runtime_dir() { + local runtime_dir="/run/user/${KIOSK_UID}" + + log "Setting up XDG_RUNTIME_DIR: $runtime_dir" + + mkdir -p "$runtime_dir" + chown "${KIOSK_UID}:${KIOSK_GID}" "$runtime_dir" + chmod 700 "$runtime_dir" + + export XDG_RUNTIME_DIR="$runtime_dir" + return 0 +} + +# ── Step 3: Wait for network interface ─────────────────────────── +wait_for_network() { + log "Waiting for dummy0 interface..." + + local count=0 + while [[ $count -lt 30 ]]; do + # Check if dummy0 has the expected IP + if ip addr show dummy0 2>/dev/null | grep -q "192.168.255.1"; then + log "dummy0 interface ready" + return 0 + fi + + # Also check if any interface has connectivity + if ip route get 8.8.8.8 &>/dev/null; then + log "Network connectivity available" + break + fi + + sleep 1 + ((count++)) + done + + # Don't fail - we might still work with localhost + log "Network check complete (dummy0 may not be ready)" + return 0 +} + +# ── Step 4: Wait for nginx/webui ───────────────────────────────── +wait_for_webui() { + log "Waiting for SecuBox WebUI..." + + local count=0 + local url_to_use="$KIOSK_URL" + + while [[ $count -lt $MAX_WAIT ]]; do + # Try primary URL + if curl -sk --connect-timeout 2 "$KIOSK_URL" >/dev/null 2>&1; then + log "WebUI ready at $KIOSK_URL" + export KIOSK_URL + return 0 + fi + + # Try fallback URL + if curl -sk --connect-timeout 2 "$FALLBACK_URL" >/dev/null 2>&1; then + log "WebUI ready at fallback $FALLBACK_URL" + export KIOSK_URL="$FALLBACK_URL" + return 0 + fi + + # Check if nginx is at least running + if systemctl is-active nginx &>/dev/null; then + if [[ $count -gt 10 ]]; then + log "nginx running but URL not responding, trying localhost..." + if curl -sk --connect-timeout 2 "https://localhost/" >/dev/null 2>&1; then + export KIOSK_URL="https://localhost/" + log "Using localhost as fallback" + return 0 + fi + fi + fi + + sleep 1 + ((count++)) + done + + # Last resort: use the URL anyway, chromium will show error + err "WebUI not responding after ${MAX_WAIT}s, proceeding anyway" + export KIOSK_URL + return 0 +} + +# ── Step 5: Setup VT (virtual terminal) ────────────────────────── +setup_vt() { + local target_vt="${1:-7}" + + log "Setting up VT${target_vt}..." + + # Ensure tty device exists + if [[ ! -c "/dev/tty${target_vt}" ]]; then + log "Creating /dev/tty${target_vt}..." + mknod "/dev/tty${target_vt}" c 4 "$target_vt" 2>/dev/null || true + fi + + # Don't switch VT here - let X11 handle it with vt7 parameter + # chvt before X starts can cause issues + + return 0 +} + +# ── Step 6: Create/verify xinitrc ──────────────────────────────── +setup_xinitrc() { + local xinitrc="${KIOSK_HOME}/.xinitrc" + + if [[ ! -f "$xinitrc" ]]; then + log "Creating .xinitrc..." + + mkdir -p "${KIOSK_HOME}/.config" + + cat > "$xinitrc" <<'XINITRC' +#!/bin/bash +# SecuBox Kiosk X11 Launcher (generated by kiosk-launcher) + +# URL from environment (set by launcher) +URL="${KIOSK_URL:-https://192.168.255.1:9443/}" + +# Disable screen blanking +xset s off 2>/dev/null || true +xset -dpms 2>/dev/null || true +xset s noblank 2>/dev/null || true + +# Hide cursor after 3 seconds of inactivity +if command -v unclutter &>/dev/null; then + unclutter -idle 3 -root & +fi + +# Set background to black +xsetroot -solid black 2>/dev/null || true + +# Chromium flags for kiosk mode +CHROMIUM_FLAGS=( + --kiosk + --no-first-run + --no-sandbox + --disable-gpu-sandbox + --disable-translate + --disable-infobars + --disable-session-crashed-bubble + --disable-restore-background-contents + --disable-sync + --disable-features=TranslateUI + --ignore-certificate-errors + --noerrdialogs + --enable-features=OverlayScrollbar + --start-fullscreen + --window-position=0,0 + --check-for-update-interval=604800 + --disable-component-update + --disable-default-apps + --disable-extensions + --disable-background-networking + --disable-domain-reliability + --disable-client-side-phishing-detection + "$URL" +) + +# Run Chromium +exec chromium "${CHROMIUM_FLAGS[@]}" +XINITRC + + chmod +x "$xinitrc" + chown "${KIOSK_UID}:${KIOSK_GID}" "$xinitrc" + fi + + return 0 +} + +# ── Step 7: Launch X11 ─────────────────────────────────────────── +launch_x11() { + local vt="${1:-7}" + local display="${2:-:0}" + + log "Launching X11 on VT${vt} display ${display}..." + log "KIOSK_URL=$KIOSK_URL" + + # Export environment for xinitrc + export HOME="$KIOSK_HOME" + export DISPLAY="$display" + export XDG_SESSION_TYPE=x11 + export KIOSK_URL + + # Switch to kiosk user and run startx + exec su -s /bin/bash "$KIOSK_USER" -c \ + "export HOME='$KIOSK_HOME' DISPLAY='$display' XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR' XDG_SESSION_TYPE=x11 KIOSK_URL='$KIOSK_URL'; exec /usr/bin/startx '$KIOSK_HOME/.xinitrc' -- $display vt${vt} -nolisten tcp -keeptty" +} + +# ── Main ───────────────────────────────────────────────────────── +main() { + local mode="${1:-x11}" + local vt="${2:-7}" + + log "════════════════════════════════════════════════════" + log "SecuBox Kiosk Launcher starting (mode=$mode, vt=$vt)" + log "════════════════════════════════════════════════════" + + # Check if enabled + if [[ ! -f /var/lib/secubox/.kiosk-enabled ]]; then + err "Kiosk not enabled (missing /var/lib/secubox/.kiosk-enabled)" + exit 1 + fi + + # Run setup steps + verify_user || exit 1 + setup_runtime_dir || exit 1 + wait_for_network + wait_for_webui + setup_vt "$vt" + setup_xinitrc + + # Launch based on mode + case "$mode" in + x11) + launch_x11 "$vt" ":0" + ;; + wayland) + log "Launching Cage/Wayland..." + export HOME="$KIOSK_HOME" + export XDG_SESSION_TYPE=wayland + export KIOSK_URL + export WLR_LIBINPUT_NO_DEVICES=1 + export WLR_NO_HARDWARE_CURSORS=1 + export WLR_RENDERER_ALLOW_SOFTWARE=1 + export WLR_RENDERER=pixman + + exec su -s /bin/bash "$KIOSK_USER" -c \ + "export HOME='$KIOSK_HOME' XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR' XDG_SESSION_TYPE=wayland KIOSK_URL='$KIOSK_URL' WLR_LIBINPUT_NO_DEVICES=1 WLR_NO_HARDWARE_CURSORS=1 WLR_RENDERER_ALLOW_SOFTWARE=1 WLR_RENDERER=pixman; exec /usr/bin/cage -s -- '$KIOSK_HOME/start-kiosk.sh'" + ;; + *) + err "Unknown mode: $mode" + exit 1 + ;; + esac +} + +main "$@" diff --git a/image/sbin/secubox-kiosk-setup b/image/sbin/secubox-kiosk-setup index fbcbf1d3..d4678735 100755 --- a/image/sbin/secubox-kiosk-setup +++ b/image/sbin/secubox-kiosk-setup @@ -159,18 +159,14 @@ create_wayland_script() { cat > "${KIOSK_HOME}/start-kiosk.sh" <<'KIOSK' #!/bin/bash # SecuBox Kiosk Launcher (Wayland) +# Note: secubox-kiosk-launcher already waits for services before starting -# Wait for services to be ready -for i in {1..30}; do - if curl -sk https://192.168.255.1:9443/ >/dev/null 2>&1; then - break - fi - sleep 1 -done - -# Kiosk URL (local SecuBox WebUI via dummy interface) +# URL from environment (set by launcher) or fallback URL="${KIOSK_URL:-https://192.168.255.1:9443/}" +# Log startup +logger -t secubox-kiosk "Starting Chromium Wayland kiosk: $URL" + # Chromium flags for kiosk mode (native Wayland via Ozone) CHROMIUM_FLAGS=( --ozone-platform=wayland @@ -209,28 +205,26 @@ create_x11_xinitrc() { cat > "${KIOSK_HOME}/.xinitrc" <<'XINITRC' #!/bin/bash # SecuBox Kiosk X11 Launcher +# Note: secubox-kiosk-launcher already waits for services before starting -# Wait for services to be ready -for i in {1..30}; do - if curl -sk https://192.168.255.1:9443/ >/dev/null 2>&1; then - break - fi - sleep 1 -done - -# Kiosk URL (local SecuBox WebUI via dummy interface) +# URL from environment (set by launcher) or fallback URL="${KIOSK_URL:-https://192.168.255.1:9443/}" -# Disable screen blanking -xset s off -xset -dpms -xset s noblank +# Log startup +logger -t secubox-kiosk "Starting Chromium X11 kiosk: $URL" + +# Disable screen blanking (ignore errors on headless) +xset s off 2>/dev/null || true +xset -dpms 2>/dev/null || true +xset s noblank 2>/dev/null || true # Hide cursor after 3 seconds of inactivity -unclutter -idle 3 -root & +if command -v unclutter &>/dev/null; then + unclutter -idle 3 -root & +fi # Set background to black -xsetroot -solid black +xsetroot -solid black 2>/dev/null || true # Chromium flags for kiosk mode (X11) CHROMIUM_FLAGS=( @@ -283,118 +277,65 @@ EOF log "Root autologin enabled on tty2 (Ctrl+Alt+F2 to access)" } -# ── Update Service File with Correct UID ───────────────────────── +# ── Update Service File (using launcher script) ───────────────────────── update_service_file() { local KIOSK_UID="${1:-1000}" local mode="${2:-x11}" - if [[ "$mode" == "wayland" ]]; then - # Wayland service (Cage) - cat > /etc/systemd/system/secubox-kiosk.service < /etc/systemd/system/secubox-kiosk.service < /etc/systemd/system/secubox-kiosk.service <