#!/bin/bash # kiosk-setup.sh # OpenKiosk Browser Terminal Setup # Copyright (C) 2026 Rod Wright # SPDX-License-Identifier: GPL-2.0 APP_NAME="OpenKiosk" APP_VERSION="2.0.0" function clean_exit() { # Re-enable unattended-upgrades if $restart_uu; then systemctl start unattended-upgrades >/dev/null 2>&1; fi exit 0 } function abort_exit() { # Re-enable unattended-upgrades if $restart_uu; then systemctl start unattended-upgrades >/dev/null 2>&1; fi exit 1 } function privilege_check() { # test for superuser rights if [[ $EUID -ne 0 ]]; then echo "This script must be run with superuser privileges. Try sudo $0" exit 1 fi } function disable_unattended_upgrades() { # Prevent unattended-upgrades from interfering restart_uu=false while systemctl status unattended-upgrades >/dev/null 2>&1 do systemctl stop unattended-upgrades sleep 5 restart_uu=true done } function update_os() { # Make sure OS is up to date echo "Updating Operating System..." apt-get update -qq if [[ "$kconfig" == "standalone" ]] then if ((apt-get dist-upgrade -y >/dev/null 2>&1)& task_pid=$! spinner $task_pid unset task_pid) then echo "...done." echo "" else echo "A problem was encountered during installation." echo "the command that failed was:" echo " apt-get dist-upgrade -y" abort_exit fi unset task_result fi } function remove_snap() { # Remove snap if installed if snap list >/dev/null 2>&1; then echo "Removing snap..." snap remove lxd snap remove core20 snap remove snapd systemctl stop snapd systemctl disable snapd systemctl mask snapd apt-get purge snapd -y apt-mark hold snapd rm -rf /home/*/snap/ rm -rf /root/snap/ rm -rf /snap rm -rf /var/snap rm -rf /var/lib/snapd echo "...done." fi } function install_package() { # Install a specified package. local pkg=$1 echo "Checking for $pkg installation..." if dpkg -l|grep -q $pkg then echo "" echo "$pkg is installed. Continuing." echo "" else echo "Installing $pkg..." if ! ((apt-get install -y --no-install-recommends $pkg >/dev/null 2>&1)& task_pid=$! spinner $task_pid unset task_pid) then echo "A problem was encountered during installation." echo "the command that failed was:" echo " apt-get install -y --no-install-recommends $pkg" abort_exit fi fi echo "...done." } function install_webmin() { # Check for webmin installation and prompt for install echo "" echo "Checking for webmin installation..." echo "" if dpkg -l|grep webmin >/dev/null 2>&1 then echo "" echo "webmin is installed. Continuing." echo "" else echo "webmin doesn't seem to be installed." echo "It is an optional package that enables system administration" echo "using a friendly GUI. It is highly recommended, especially for" echo "headless servers without any way to log in directly on the machine." echo "Note that this has the potential to be a security risk, especially" echo "if your passwords aren't sufficiently strong." echo -n "Install it now? [Y/n]: " read reqinstall if [ "$reqinstall" = "N" -o "$reqinstall" = "n" ] then echo "Proceeding without installing webmin. System administration" echo "will require login to the server either directly or via ssh." else echo "" echo "Proceeding with webmin installation" echo "" sleep 3 echo "Installing webmin..." skipwebmin="false" if ! curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh >/dev/null 2>&1 then echo "Warning: failed to download Webmin repo setup script." echo -n "Ignore and [c]ontinue or [A]bort? [c/A]: " read wmfailchoice if [ "$wmfailchoice" = "C" -o "$wmfailchoice" = "c" ] then skipwebmin="true" echo "Continuing without installing Webmin. You should investigate the failure" echo "and install Webmin manually after the server is set up if you would like" echo "to be able to manage the server from another computer with a GUI instead" echo "of through ssh only." echo -n "Press enter to continue." read continueok echo "" fi fi if [ "$skipwebmin" = "false" ] then ( sh setup-repos.sh -f >/dev/null 2>&1 apt-get install -y --install-recommends webmin >/dev/null 2>&1 )& task_pid=$! spinner $task_pid unset task_pid fi echo "...done." echo "" echo "You can now access webmin using a web browser pointed to:" echo "https://$HOSTNAME:10000" echo "Unless you have obtained valid ssl certificates, your server is" echo "using self-signed certs. This is not necessarily a problem, but" echo "your browser will complain. You can tell the browser to accept the" echo "self signed certificates and the traffic will still be encrypted," echo "but you may be vulnerable to man-in-the-middle attacks. You can" echo "get free valid certificates through LetsEncrypt." echo "See http://letsencrypt.org for more information." echo "" echo -n "Press enter to continue." read continueok echo "" echo "Finished with webmin installation" echo "" sleep 3 fi fi } function install_cups() { # Prompt whether to install print server echo "" echo "Checking for cups print server and driver installation..." echo "" if dpkg -l|grep cups-common >/dev/null 2>&1 then echo "" echo "cups is installed. Continuing." echo "" else echo "The cups print server doesn't seem to be installed." echo "If this will be an interactive kiosk and you would like to" echo "be able to print from it, cups will need to be installed." read -p "Install it now? [Y/n]: " -r reqinstall if [ "$reqinstall" = "N" -o "$reqinstall" = "n" ] then echo "Proceeding without installing cups." else echo "" echo "Proceeding with cups installation" echo "" sleep 3 echo "Installing cups print server..." (apt-get install -y cups > /dev/null 2>&1)& task_pid=$! spinner $task_pid unset task_pid usermod -aG lpadmin $SUDO_USER myhn=`hostname` echo "" echo "" echo "" echo "The cups print server has been installed." echo "After the kiosk is set up and working, edit /etc/openkiosk/openkiosk.conf" echo "and set the TGT_URL parameter to:" echo " https://localhost:631" echo "to add or manage printers via the kiosk browser." echo "When you are finished, change it back to your normal URL." echo "Note that the kiosk browser will, by default, print to the server" echo "default printer." echo "" echo -n "Press enter to continue." read continueok echo "" echo "Finished with cups print server installation" echo "" sleep 3 fi fi } function create_standalone_user() { # Install standalone kiosk user config echo "" echo "Creating kiosk user..." cloneuser $SUDO_USER kioskuser >/dev/null 2>&1 mkdir -p /home/kioskuser/.config/openbox cp -R distfiles/home/kioskuser /home/ cp distfiles/home/kioskuser/.profile /home/kioskuser cp distfiles/home/kioskuser/.config/openbox/rc.xml /home/kioskuser/.config/openbox chown -R kioskuser:kioskuser /home/kioskuser/.config /home/kioskuser/.profile echo "...done." echo "" } function set_kiosk_url() { # Prompt for URL echo "" echo "This web browser based kiosk will need to know the site you would" echo "like to load. This can be a normal website or a file to be displayed" echo "in the browser, but must be available as a web URL (http://something)." echo "" echo "Note that in kiosk mode, your normal navigation buttons and mouse" echo "right-click menus are not available, so you will have no way to go" echo "back or forward in a page unless the page itself provides that" echo "capability." url_ok=false server_url="file:/etc/openkiosk/openkiosk_welcome.html" while ! $url_ok do echo "" echo "Please enter complete URL. Valid formats are:" echo "\"http://somesite.com/somepage\"" echo "\"file:/path/to/file.name\"" echo "" read -p "[file:/etc/openkiosk/openkiosk_welcome.html] :" -r server_url if [[ $server_url == "" ]] then server_url="file:/etc/openkiosk/openkiosk_welcome.html" fi if [[ $server_url == "test" ]] then url_ok=true elif echo "$server_url" | grep -q '^file:' then filepath=$(echo "$server_url"|cut -d":" -f2-) if [ ! -f "$filepath" ] then echo "" echo "There was an error reading the file you entered. The URL must" echo "be a complete valid path specification of the format:" echo "\"file:/path/to/file.name\" on the kiosk's filesystem." else url_ok=true fi else if ! `wget --no-check-certificate -q -o /dev/null -O /dev/null $server_url` then echo "" echo "There was an error retrieving the URL you entered. The URL must" echo "be a complete URL of the format \"http://somesite.com/somepage\"." echo "If this URL is on the local network, try using the IP address or" echo "the local domain. For example, instead of \"http://myserver/mypage\"," echo "try \"http://192.168.1.1/mypage\" or \"http://myserver.local/mypage\"." else url_ok=true fi fi done sed -i 's,'^TGT_URL=.*','TGT_URL="\"$server_url\""',' /etc/openkiosk/openkiosk.conf } function set_kiosk_type() { # Prompt for kiosk type echo "" type_choice="none" while [[ "$type_choice" == "none" ]] do echo "Will this kiosk be an interactive kiosk or a display-only kiosk?" echo -n "[I]nteractive or [D]isplay-only? [I] :" read kiosk_type echo "" case "$kiosk_type" in "d" | "D") echo "Configuring for display-only kiosk." echo "How often would you like to check for changes to the page being" echo "displayed? Specify the number of seconds. Enter 0 to disable" echo "periodic checking." echo -n "Seconds between checks? [60] :" read poll_interval echo "" if [[ $poll_interval == "" ]]; then poll_interval=60; fi sed -i 's,'POLL_INTERVAL=.*','POLL_INTERVAL="\"$poll_interval\""',' /etc/openkiosk/openkiosk.conf echo "Disabling screen blanking." sed -i 's,'INHIBIT_BLANK=.*','INHIBIT_BLANK="\"1\""',' /etc/openkiosk/openkiosk.conf type_choice="D" ;; "i" | "I" | "") echo "Configuring for interactive kiosk." type_choice="I" ;; *) echo "Please enter I for interactive or D for display-only." type_choice="none" ;; esac done } function install_required_packages() { # Positional parameters supplied to this function should be quoted # apt-get installable package names. # Note: Each package supplied to the for loop below can either be a # single package name or a list of alternatives separated by the word "or". # If supplying a list, the last one in the list will be the one that # gets installed. for prereq in "$@" do echo "Checking for $prereq installation..." gpattern="" for pkg_option in $(echo $prereq) do if [ "$pkg_option" != "or" ] then gpattern+=" -e $pkg_option" preferred_pkg=$pkg_option fi done if dpkg -l|grep $gpattern >/dev/null 2>&1 then echo "$prereq is installed. Continuing." else echo "$APP_NAME requires $prereq, but it doesn't seem to be installed. " echo -n "Install $preferred_pkg now? [Y/n]: " read reqinstall if [ "$reqinstall" = "N" -o "$reqinstall" = "n" ] then echo "$APP_NAME requires $prereq, but you have elected not to install it." echo "Installation cannot continue." abort_exit else echo "" echo "Proceeding with $preferred_pkg installation" echo "" sleep 3 (apt-get install -y $preferred_pkg > /dev/null 2>&1)& task_pid=$! spinner $task_pid unset task_pid echo "" echo "Finished with $preferred_pkg installation" echo "" sleep 3 fi fi done } function spinner() { local pid="$1" local delay="0.1" local spinstr='|/-\\' local i=0 while ps -p "$pid" > /dev/null; do i=$(( (i+1) % 4 )) printf "\r[%c]" "${spinstr:$i:1}" sleep "$delay" done printf "\r \r" # Clear the spinner line } # -------------- Begin execution -------------- privilege_check echo "" echo " Welcome to $APP_NAME $APP_VERSION installation" echo "" echo "" echo "This script will configure a Debian based system to be a kiosk browser terminal." echo "" echo "" continueok=false while ! $continueok do read -p "Would you like to continue with OpenKiosk setup [Y/n]: " -r continueok case "$continueok" in "n" | "N") echo "Cancelling setup." clean_exit ;; "y" | "Y" | "") continueok=true echo "Continuing with OpenKiosk setup." ;; *) continueok=false echo "Please answer Y (default) or N." ;; esac done echo "" echo "OpenKiosk can be set up as a standalone kiosk or a workstation kiosk." echo "" echo "A standalone kiosk will boot up to a display only or an interactive" echo "kiosk that requires no login. This is useful for a system that is to" echo "be used interactively by multiple people or will be in a public place" echo "displaying automatically updating information non-interactively " echo "(digital signage)." echo "" echo "A workstation kiosk is one that can be used on a normal workstation by" echo "users who log in normally and would like to be able to run a kiosk style" echo "browser or pdf viewer in a full screen or windowed configuration only" echo "on demand." echo "" echo "Which configuration would you like?" kconfig="none" while [[ "$kconfig" == "none" ]] do read -p "Would you like a [S]tandalone or a [W]orkstation configuration? [S/W]?: " -r kconfig case "$kconfig" in "s" | "S") echo "Continuing with standalone configuration." kconfig="standalone" ;; "w" | "W") echo "Continuing with workstation configuration." kconfig="workstation" ;; "c" | "C") echo "Cancelling installation." clean_exit ;; *) echo "Please enter S or W, or enter C to cancel installation." kconfig="none" ;; esac done echo "" echo "Preparing for $kconfig installation. Please wait..." disable_unattended_upgrades apt-get update -qq install_required_packages "rsync" "curl" "psmisc" chmod +x distfiles/usr/local/bin/* case "$kconfig" in "standalone") # Copy in the needed files echo "Copying files..." cp -f distfiles/usr/local/bin/* /usr/local/bin cp -Rf distfiles/etc/* /etc/ rm /lib/systemd/system/ctrl-alt-del.target ln -s /dev/null /lib/systemd/system/ctrl-alt-del.target echo "...done." echo "" update_os remove_snap install_package "xorg" install_package "openbox" install_package "xterm" echo "Enabling autologin for standalone kiosk mode." systemctl daemon-reload systemctl enable getty@tty1.service >/dev/null 2>&1 # If we are on a Raspberry Pi 5, we'll need to install this for the # xorg server to start if cat /proc/cpuinfo|grep -q "Raspberry Pi 5" >/dev/null 2>&1 then echo "Raspberry Pi 5 detected." install_package "gldriver-test" fi install_package "chromium" install_package "xpdf" install_webmin install_cups create_standalone_user set_kiosk_url set_kiosk_type echo "" echo "Kiosk configuration is now complete. " echo "" echo "For standalone installation:" echo " - You can change the configuration by editing the configuration file:" echo " /etc/openkiosk/openkiosk.conf" echo "" echo "The system must be rebooted for the configuration to take effect." echo "" read -p "Would you like to reboot now? [Y/n]: " -r rebootok case "$rebootok" in "y" | "Y" | "") reboot ;; *) clean_exit ;; esac ;; "workstation") # Copy in the needed files echo "Copying files..." cp -vf distfiles/usr/local/bin/* /usr/local/bin mkdir -p /etc/openkiosk cp -vf distfiles/etc/openkiosk/openkiosk.conf /etc/openkiosk/ cp -vf distfiles/etc/openkiosk/openkiosk_welcome.html /etc/openkiosk/ install_package "chromium" install_package "xpdf" install_cups set_kiosk_url set_kiosk_type echo "" echo "Kiosk configuration is now complete. " echo "" echo "For standalone installation:" echo " - You can change the configuration by editing the configuration file:" echo " /etc/openkiosk/openkiosk.conf" echo " - Reboot the system to launch OpenKiosk." echo "" echo "For workstation installation:" echo " - You can change the configuration by editing the configuration file:" echo " /etc/openkiosk/openkiosk.conf" echo " - Type openkiosk at a shell prompt to run it, or create a launcher for it." echo " The executable is /usr/local/bin/openkiosk." echo "" echo "" echo "Enjoy!" clean_exit ;; *) echo "Unknown configuration type. Aborting." abort_exit ;; esac