Initial commit

This commit is contained in:
Manawyrm 2023-06-29 22:14:05 +02:00
commit 85decda2a1
35 changed files with 951 additions and 0 deletions

39
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: CI
on:
create: { }
push: { }
pull_request: { }
jobs:
build:
runs-on: [self-hosted, hetzner-cax21]
outputs:
pkgfile: ${{ steps.pkgname.outputs.pkgfile }}
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update -qq
sudo apt install -yqq libguestfs-tools qemu-utils rsync sudo wget xz pigz
- name: Build firmware
run: |
./build_raspberry_pi.sh
- name: Compress firmware
run: |
pigz -9 raspikiosk.img
- name: Release build artifacts
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
./raspikiosk.img.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
raspikiosk.img
raspios.img.xz
work/

31
README.md Normal file
View File

@ -0,0 +1,31 @@
Another kiosk browser OS?
Yes, this one is a little bit opinionated :)
The author ran several similar setups in production for years and has seen a lot of problems and strange failure modes.
This project aims to solve a lot of those (at least for the author), it might also be useful for others :)
Key features:
- Images built via CI
- WiFi connection support
- Raspberry Pi (Arm64) compatibility
- USB flash drive, USB SSD, etc. compatible
- aarch64 mode for Raspberry Pis (_significant_ performance improvements over armv7/32bit ARM)
- Read-only filesystem handling (no more broken SD cards)
- Configurable cache clear functionality
- HTTP watchdog (website needs to send heartbeat messages via XHR/AJAX to localhost)
- Force specific resolution (1080p on 4k screens, broken EDID, etc.)
- Hard NTP handling (will wait for NTP at boot)
- SSH support
- VNC support
- SSH tunneling support (for remote-access without port-forwarding, etc.)
Planned features:
- PC (x86) compatibility
- Raspberry Pi PXE/network boot support
- Network connectivity watchdog (configurable ping, etc. timeout)
- Automatic reboot at specified time
Security considerations:
- Autossh does not check SSH host keys. This is okay-ish as long as the target server only allows tunneling, nothing else.
- nginx/PHP are allowed to use sudo/NOPASSWD (because it needs to query the VideoCore, manage service, etc.), more priviledge seperation would be nice
- due to the skeleton mechanism, the system has some ... creative permissions. some cleanup required.

66
build_raspberry_pi.sh Executable file
View File

@ -0,0 +1,66 @@
#!/bin/bash
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
BUILD_DIR="${SCRIPT_DIR}/work/root/"
umount -fl "${BUILD_DIR}" || true
losetup -D /dev/loop0 || true
rm -rf "${BUILD_DIR}" || true
mkdir -p "${BUILD_DIR}"
if [ ! -f raspios.img.xz ]
then
wget -O raspios.img.xz "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-05-03/2023-05-03-raspios-bullseye-arm64-lite.img.xz"
echo "bf982e56b0374712d93e185780d121e3f5c3d5e33052a95f72f9aed468d58fa7 raspios.img.xz" | sha256sum --check --status
if [ $? -ne 0 ];
echo "downloaded raspios does not match checksum";
return -1;
fi
fi
rm -f raspios.img
xz -kd raspios.img.xz
# Repartition image
virt-filesystems --long -h --all -a raspios.img
truncate -r raspios.img raspikiosk.img
truncate -s +3G raspikiosk.img
virt-resize --expand /dev/sda2 raspios.img raspikiosk.img
rm -f raspios.img
# Setup loop device for Raspberry Pi image (with partition scanning)
sudo losetup -P /dev/loop0 raspikiosk.img
# Mount partitions
sudo mount /dev/loop0p2 "${BUILD_DIR}"
sudo mount /dev/loop0p1 "${BUILD_DIR}/boot"
# Copy the (raspberry pi-specific) skeleton files
rsync -a "${SCRIPT_DIR}/raspberry_pi_skeleton/." "${BUILD_DIR}"
rsync -a "${SCRIPT_DIR}/kiosk_skeleton/." "${BUILD_DIR}/kiosk_skeleton"
# Mount system partitions (from the build host)
sudo mount -t proc /proc "${BUILD_DIR}/proc/"
sudo mount --rbind /sys "${BUILD_DIR}/sys/"
sudo mount --rbind /dev "${BUILD_DIR}/dev/"
sudo chroot "${BUILD_DIR}" /raspberry_pi_bullseye.sh
sudo chroot "${BUILD_DIR}" /kiosk_skeleton/build.sh
sudo rm -r "${BUILD_DIR}/kiosk_skeleton"
sudo rm "${BUILD_DIR}/raspberry_pi_bullseye.sh"
sudo umount -fl "${BUILD_DIR}/proc"
sudo umount -fl "${BUILD_DIR}/sys"
sudo umount -fl "${BUILD_DIR}/dev"
sudo umount "${BUILD_DIR}/proc"
sudo umount "${BUILD_DIR}/sys"
sudo umount "${BUILD_DIR}/dev"
sudo umount "${BUILD_DIR}/boot"
sudo umount "${BUILD_DIR}"
sudo losetup -D /dev/loop0

61
kiosk_skeleton/build.sh Executable file
View File

@ -0,0 +1,61 @@
#!/bin/bash
# This script is being run on the target debian platform
apt update
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt dist-upgrade -y
DEBIAN_FRONTEND=noninteractive apt install -y lightdm openbox nginx php-fpm php-cli chromium autossh unclutter x11-xserver-utils xdotool htop nano openssh-server rsync x11vnc lm-sensors ntpdate scrot
rsync -a --chown=root:root "/kiosk_skeleton/." "/"
chown -hR pi:pi /home/pi
# Raspberry Pi specific modifications
# raspberrypi-net-mods does things like copying /boot/wpa_supplicant.conf to the root FS
apt remove -y raspberrypi-net-mods || true
# userconf-pi prevents lightdm from starting unless the default "pi" user is changed
apt remove -y userconf-pi || true
# RF emissions are blocked by default
rfkill unblock wlan || true
# fix file system permissions
chown -hR 0:0 /etc/sudoers.d/
chown -hR www-data:www-data /var/www/html/
mkdir -p /home/pi/.config/chromium/
chown -hR 1000:1000 /home/pi/.config/chromium/
mkdir -p /home/pi/.pki/
chown -hR 1000:1000 /home/pi/.pki/
# FIXME: readonly in /etc/fstab
echo "tmpfs /dev/shm tmpfs mode=0777 0 0" >> /etc/fstab
echo "tmpfs /tmp tmpfs mode=1777 0 0" >> /etc/fstab
echo "tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0" >> /etc/fstab
echo "tmpfs /var/log tmpfs defaults,noatime,nosuid,mode=0755,size=100m 0 0" >> /etc/fstab
echo "tmpfs /var/lib/lightdm tmpfs defaults,noatime,nosuid,size=30m 0 0" >> /etc/fstab
echo "tmpfs /home/pi/.cache tmpfs mode=0755,nosuid,nodev,uid=1000,gid=1000 0 0" >> /etc/fstab
echo "tmpfs /home/pi/.config/chromium/ tmpfs mode=0755,nosuid,nodev,uid=1000,gid=1000 0 0" >> /etc/fstab
echo "tmpfs /home/pi/.pki/ tmpfs mode=0755,nosuid,nodev,uid=1000,gid=1000 0 0" >> /etc/fstab
# Create symlinks for configuration files which will later get created at runtime (in /tmp)
rm /etc/hosts
rm /etc/hostname
mkdir -p /etc/wpa_supplicant/
ln -sf /tmp/hosts /etc/hosts
ln -sf /tmp/hostname /etc/hostname
ln -sf /tmp/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
systemctl daemon-reload
# remove unneccessary clutter
systemctl disable dphys-swapfile
systemctl disable ModemManager
systemctl disable avahi-daemon
systemctl disable bluetooth
systemctl enable kiosk-wifi
systemctl enable kiosk-autossh
systemctl enable kiosk-watchdog
systemctl enable kiosk-set-hostname
systemctl enable ntpdate
systemctl enable lightdm
systemctl enable nginx

View File

@ -0,0 +1,169 @@
#
# General configuration
#
# start-default-seat = True to always start one seat if none are defined in the configuration
# greeter-user = User to run greeter as
# minimum-display-number = Minimum display number to use for X servers
# minimum-vt = First VT to run displays on
# lock-memory = True to prevent memory from being paged to disk
# user-authority-in-system-dir = True if session authority should be in the system location
# guest-account-script = Script to be run to setup guest account
# logind-check-graphical = True to on start seats that are marked as graphical by logind
# log-directory = Directory to log information to
# run-directory = Directory to put running state in
# cache-directory = Directory to cache to
# sessions-directory = Directory to find sessions
# remote-sessions-directory = Directory to find remote sessions
# greeters-directory = Directory to find greeters
# backup-logs = True to move add a .old suffix to old log files when opening new ones
# dbus-service = True if LightDM provides a D-Bus service to control it
#
[LightDM]
#start-default-seat=true
#greeter-user=lightdm
#minimum-display-number=0
#minimum-vt=7
#lock-memory=true
user-authority-in-system-dir=true
#guest-account-script=guest-account
#logind-check-graphical=false
#log-directory=/var/log/lightdm
#run-directory=/var/run/lightdm
#cache-directory=/var/cache/lightdm
#sessions-directory=/usr/share/lightdm/sessions:/usr/share/xsessions:/usr/share/wayland-sessions
#remote-sessions-directory=/usr/share/lightdm/remote-sessions
#greeters-directory=$XDG_DATA_DIRS/lightdm/greeters:$XDG_DATA_DIRS/xgreeters
#backup-logs=true
#dbus-service=true
#
# Seat configuration
#
# Seat configuration is matched against the seat name glob in the section, for example:
# [Seat:*] matches all seats and is applied first.
# [Seat:seat0] matches the seat named "seat0".
# [Seat:seat-thin-client*] matches all seats that have names that start with "seat-thin-client".
#
# type = Seat type (local, xremote, unity)
# pam-service = PAM service to use for login
# pam-autologin-service = PAM service to use for autologin
# pam-greeter-service = PAM service to use for greeters
# xserver-backend = X backend to use (mir)
# xserver-command = X server command to run (can also contain arguments e.g. X -special-option)
# xmir-command = Xmir server command to run (can also contain arguments e.g. Xmir -special-option)
# xserver-config = Config file to pass to X server
# xserver-layout = Layout to pass to X server
# xserver-allow-tcp = True if TCP/IP connections are allowed to this X server
# xserver-share = True if the X server is shared for both greeter and session
# xserver-hostname = Hostname of X server (only for type=xremote)
# xserver-display-number = Display number of X server (only for type=xremote)
# xdmcp-manager = XDMCP manager to connect to (implies xserver-allow-tcp=true)
# xdmcp-port = XDMCP UDP/IP port to communicate on
# xdmcp-key = Authentication key to use for XDM-AUTHENTICATION-1 (stored in keys.conf)
# unity-compositor-command = Unity compositor command to run (can also contain arguments e.g. unity-system-compositor -special-option)
# unity-compositor-timeout = Number of seconds to wait for compositor to start
# greeter-session = Session to load for greeter
# greeter-hide-users = True to hide the user list
# greeter-allow-guest = True if the greeter should show a guest login option
# greeter-show-manual-login = True if the greeter should offer a manual login option
# greeter-show-remote-login = True if the greeter should offer a remote login option
# user-session = Session to load for users
# allow-user-switching = True if allowed to switch users
# allow-guest = True if guest login is allowed
# guest-session = Session to load for guests (overrides user-session)
# session-wrapper = Wrapper script to run session with
# greeter-wrapper = Wrapper script to run greeter with
# guest-wrapper = Wrapper script to run guest sessions with
# display-setup-script = Script to run when starting a greeter session (runs as root)
# display-stopped-script = Script to run after stopping the display server (runs as root)
# greeter-setup-script = Script to run when starting a greeter (runs as root)
# session-setup-script = Script to run when starting a user session (runs as root)
# session-cleanup-script = Script to run when quitting a user session (runs as root)
# autologin-guest = True to log in as guest by default
# autologin-user = User to log in with by default (overrides autologin-guest)
# autologin-user-timeout = Number of seconds to wait before loading default user
# autologin-session = Session to load for automatic login (overrides user-session)
# autologin-in-background = True if autologin session should not be immediately activated
# exit-on-failure = True if the daemon should exit if this seat fails
#
[Seat:*]
#type=local
#pam-service=lightdm
#pam-autologin-service=lightdm-autologin
#pam-greeter-service=lightdm-greeter
#xserver-backend=
#xserver-command=X
#xmir-command=Xmir
#xserver-config=
#xserver-layout=
#xserver-allow-tcp=false
#xserver-share=true
#xserver-hostname=
#xserver-display-number=
#xdmcp-manager=
#xdmcp-port=177
#xdmcp-key=
#unity-compositor-command=unity-system-compositor
#unity-compositor-timeout=60
#greeter-session=example-gtk-gnome
#greeter-hide-users=false
#greeter-allow-guest=true
#greeter-show-manual-login=false
#greeter-show-remote-login=true
#user-session=default
#allow-user-switching=true
#allow-guest=true
#guest-session=
#session-wrapper=lightdm-session
#greeter-wrapper=
#guest-wrapper=
#display-setup-script=
#display-stopped-script=
#greeter-setup-script=
#session-setup-script=
#session-cleanup-script=
#autologin-guest=false
autologin-user=pi
#autologin-user-timeout=0
#autologin-in-background=false
#autologin-session=
#exit-on-failure=false
#
# XDMCP Server configuration
#
# enabled = True if XDMCP connections should be allowed
# port = UDP/IP port to listen for connections on
# listen-address = Host/address to listen for XDMCP connections (use all addresses if not present)
# key = Authentication key to use for XDM-AUTHENTICATION-1 or blank to not use authentication (stored in keys.conf)
# hostname = Hostname to report to XDMCP clients (defaults to system hostname if unset)
#
# The authentication key is a 56 bit DES key specified in hex as 0xnnnnnnnnnnnnnn. Alternatively
# it can be a word and the first 7 characters are used as the key.
#
[XDMCPServer]
#enabled=false
#port=177
#listen-address=
#key=
#hostname=
#
# VNC Server configuration
#
# enabled = True if VNC connections should be allowed
# command = Command to run Xvnc server with
# port = TCP/IP port to listen for connections on
# listen-address = Host/address to listen for VNC connections (use all addresses if not present)
# width = Width of display to use
# height = Height of display to use
# depth = Color depth of display to use
#
[VNCServer]
#enabled=false
#command=Xvnc
#port=5900
#listen-address=
#width=1024
#height=768
#depth=8

View File

@ -0,0 +1,61 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /dev/null;
error_log /dev/null;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

View File

@ -0,0 +1,19 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php;
server_name _;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php-fpm.sock;
}
}

View File

@ -0,0 +1,145 @@
;;;;;;;;;;;;;;;;;;;;;
; FPM Configuration ;
;;;;;;;;;;;;;;;;;;;;;
; All relative paths in this configuration file are relative to PHP's install
; prefix (/usr). This prefix can be dynamically changed by using the
; '-p' argument from the command line.
;;;;;;;;;;;;;;;;;;
; Global Options ;
;;;;;;;;;;;;;;;;;;
[global]
; Pid file
; Note: the default prefix is /var
; Default Value: none
; Warning: if you change the value here, you need to modify systemd
; service PIDFile= setting to match the value here.
pid = /run/php8.2-fpm.pid
; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /var
; Default Value: log/php-fpm.log
error_log = syslog
; syslog_facility is used to specify what type of program is logging the
; message. This lets syslogd specify that messages from different facilities
; will be handled differently.
; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON)
; Default Value: daemon
;syslog.facility = daemon
; syslog_ident is prepended to every message. If you have multiple FPM
; instances running on the same server, you can change the default value
; which must suit common needs.
; Default Value: php-fpm
;syslog.ident = php-fpm
; Log level
; Possible Values: alert, error, warning, notice, debug
; Default Value: notice
;log_level = notice
; Log limit on number of characters in the single line (log entry). If the
; line is over the limit, it is wrapped on multiple lines. The limit is for
; all logged characters including message prefix and suffix if present. However
; the new line character does not count into it as it is present only when
; logging to a file descriptor. It means the new line character is not present
; when logging to syslog.
; Default Value: 1024
;log_limit = 4096
; Log buffering specifies if the log line is buffered which means that the
; line is written in a single write operation. If the value is false, then the
; data is written directly into the file descriptor. It is an experimental
; option that can potentially improve logging performance and memory usage
; for some heavy logging scenarios. This option is ignored if logging to syslog
; as it has to be always buffered.
; Default value: yes
;log_buffering = no
; If this number of child processes exit with SIGSEGV or SIGBUS within the time
; interval set by emergency_restart_interval then FPM will restart. A value
; of '0' means 'Off'.
; Default Value: 0
;emergency_restart_threshold = 0
; Interval of time used by emergency_restart_interval to determine when
; a graceful restart will be initiated. This can be useful to work around
; accidental corruptions in an accelerator's shared memory.
; Available Units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
;emergency_restart_interval = 0
; Time limit for child processes to wait for a reaction on signals from master.
; Available units: s(econds), m(inutes), h(ours), or d(ays)
; Default Unit: seconds
; Default Value: 0
;process_control_timeout = 0
; The maximum number of processes FPM will fork. This has been designed to control
; the global number of processes when using dynamic PM within a lot of pools.
; Use it with caution.
; Note: A value of 0 indicates no limit
; Default Value: 0
; process.max = 128
; Specify the nice(2) priority to apply to the master process (only if set)
; The value can vary from -19 (highest priority) to 20 (lowest priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool process will inherit the master process priority
; unless specified otherwise
; Default Value: no set
; process.priority = -19
; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging.
; Default Value: yes
;daemonize = yes
; Set open file descriptor rlimit for the master process.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit for the master process.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Specify the event mechanism FPM will use. The following is available:
; - select (any POSIX os)
; - poll (any POSIX os)
; - epoll (linux >= 2.5.44)
; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
; - /dev/poll (Solaris >= 7)
; - port (Solaris >= 10)
; Default Value: not set (auto detection)
;events.mechanism = epoll
; When FPM is built with systemd integration, specify the interval,
; in seconds, between health report notification to systemd.
; Set to 0 to disable.
; Available Units: s(econds), m(inutes), h(ours)
; Default Unit: seconds
; Default value: 10
;systemd_interval = 10
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;
; Multiple pools of child processes may be started with different listening
; ports and different management options. The name of the pool will be
; used in logs and stats. There is no limitation on the number of pools which
; FPM can handle. Your system will tell you anyway :)
; Include one or more files. If glob(3) exists, it is used to include a bunch of
; files from a glob(3) pattern. This directive can be used everywhere in the
; file.
; Relative path can also be used. They will be prefixed by:
; - the global prefix if it's been set (-p argument)
; - /usr otherwise
include=/etc/php/8.2/fpm/pool.d/*.conf

View File

@ -0,0 +1,14 @@
[www]
user = www-data
group = www-data
listen = /run/php-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

View File

@ -0,0 +1,2 @@
PermitRootLogin prohibit-password
PasswordAuthentication no

View File

@ -0,0 +1 @@
www-data ALL=(ALL) NOPASSWD: ALL

View File

@ -0,0 +1,10 @@
[Unit]
Description=Keeps an SSH tunnel open
After=network.target
[Service]
User=root
ExecStart=/usr/bin/kiosk-autossh
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,10 @@
[Unit]
Description=Set hostname to the one from kioskbrowser.ini
Before=networking.service
[Service]
Type=oneshot
ExecStart=/usr/bin/kiosk-set-hostname
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,10 @@
[Unit]
Description=HTTP based watchdog
After=lightdm.service
[Service]
Type=simple
ExecStart=/usr/bin/kiosk-watchdog
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,10 @@
[Unit]
Description=Generate wpa_supplicant.conf from kioskbrowser.ini
Before=wpa_supplicant.service
[Service]
Type=oneshot
ExecStart=/usr/bin/kiosk-wifi
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,12 @@
[Unit]
Description=Force ntpdate to synchronise clock
Requires=networking.service
After=syslog.target networking.service dhcpcd.service
Before=lightdm.service
[Service]
Type=oneshot
ExecStart=ntpdate ptbtime2.ptb.de ptbtime3.ptb.de
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,36 @@
#!/bin/bash
VNC_ENABLED=$(get-ini /boot/kioskbrowser.ini vnc enabled)
if [ "${VNC_ENABLED}" -eq 1 ]
then
x11vnc -localhost &
fi
# move the cursor out of the way
xdotool mousemove 0 0
xset s off # don't activate screensaver
xset -dpms # disable DPMS (Energy Star) features.
xset s noblank # don't blank the video device
# hide mouse cursor after 1 second
unclutter -idle 1 -root &
# set a custom resolution (if specified)
RESOLUTION=$(get-ini /boot/kioskbrowser.ini screen force_resolution)
if [ -n "${RESOLUTION}" ]
then
MONITOR=$(xrandr -q | grep " connected" | awk '{ print $1; }')
xrandr --output "${MONITOR}" --mode "${RESOLUTION}"
fi
# start chromium
URL=$(get-ini /boot/kioskbrowser.ini browser url)
chromium --start-fullscreen --check-for-update-interval=1 --simulate-critical-update --noerrdialogs --disable-infobars --kiosk --allow-insecure-localhost ${URL} &
# if a cache clearing interval is specified, launch the cache-clear-timer (while true, sleep, rm -rf)
CACHE_CLEAR=$(get-ini /boot/kioskbrowser.ini browser cache_clear_interval)
if [ -n "${CACHE_CLEAR}" ]
then
/usr/bin/cache-clear-timer "${CACHE_CLEAR}" &
fi

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDMEtunV3AKNmKM3NeK5ZERUiQRKtpqFcmbH8ubNPy+zp7MfKiWwypltc5CStA95av76WeFHOQhRC75mjE98HOsyAgx9dgq4KvqSnqPu5j3hQI/BA0kp9a1oC+8DvoqWqrSop5+BPIQp23a0ILjlRsJ/JWfGTOp+4WAk9QEbQ4IBTDa/htMnI6/BYJHpvYB5QxT3BhjPCe00mCAvLIuO5xYwMDi1zl+VHsLjheSRjNku46Ivfgec8FFQVG9f5OTCT86TNWxO63Nvyk275JvyVRZdk8GfBS/tB9t2/JXCIPuEiE4p1VYHLcoGtNz6WLo8LNxQ8upmsMgXI6oX12Zysxnqsyt/L1YpD72rR3MLTdhLFrZO6EVFnzU9rqfx5Nr4krw+vuDx4LQ6sFc/IaCEmPwclgILFZi/zKNyypjRkuT1F3ejjoSkq3CA/WrEwWNVR/vOGbqQjkJ9eZJ6v3771bAbfY6yJOYjhRgDpaF4AUtYBDunOAaw0aEbopOqISzIZ0KydHQ/KAlNxPzWpIVVjvz8eqGTTy/xDggCddOTaj9mt6BeLK/xUyqu0gh/OD8ZqgNmyeNYfkAtV4mfrSV3MiwpJG2ItgMsPUZt8L87e0//s6K2XOZ0Dx3OeZyXdMLzwG+YR6Bj+qKG5TMPw5YKFfmVBxcwDNovthapOKkPT3fhw== Manawyrm

View File

@ -0,0 +1,6 @@
#!/bin/bash
while true
do
sleep $1
rm -r /home/pi/.cache/chromium/Default || true
done

22
kiosk_skeleton/usr/bin/get-ini Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env php
<?php
if ($argc != 4 && $argc != 5)
{
error_log("Usage: get-ini FILE SECTION NAME [DEFAULT]");
error_log("Fetches a single configuration item from an ini file");
exit(1);
}
$config = parse_ini_file($argv[1], true, INI_SCANNER_NORMAL);
if (isset($config[$argv[2]]))
{
if (isset($config[$argv[2]][$argv[3]]))
{
echo $config[$argv[2]][$argv[3]];
exit(0);
}
}
echo $argv[4] ?? "";
exit(1);

View File

@ -0,0 +1,10 @@
#!/bin/bash
AUTOSSH_ENABLED=$(get-ini /boot/kioskbrowser.ini autossh enabled)
if [ "${AUTOSSH_ENABLED}" -eq 1 ]
then
AUTOSSH_ARGS=$(get-ini /boot/kioskbrowser.ini autossh args)
export AUTOSSH_GATETIME=0
/usr/bin/autossh -N -q -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" ${AUTOSSH_ARGS}
fi

View File

@ -0,0 +1,15 @@
#!/bin/bash
HOSTNAME=$(get-ini /boot/kioskbrowser.ini general hostname kiosk)
if [ -n "${HOSTNAME}" ]
then
echo "${HOSTNAME}" > /tmp/hostname
hostnamectl --transient hostname "${HOSTNAME}"
echo "127.0.0.1 localhost" > /tmp/hosts
echo "::1 localhost ip6-localhost ip6-loopback" >> /tmp/hosts
echo "ff02::1 ip6-allnodes" >> /tmp/hosts
echo "ff02::2 ip6-allrouters" >> /tmp/hosts
echo "" >> /tmp/hosts
echo "127.0.1.1 ${HOSTNAME}" >> /tmp/hosts
fi

View File

@ -0,0 +1,33 @@
#!/usr/bin/env php
<?php
// Wait 60 seconds (after boot) before doing any watchdog actions.
sleep(60);
$config = parse_ini_file("/boot/kioskbrowser.ini", true, INI_SCANNER_NORMAL);
if (isset($config["watchdog"]) && isset($config["watchdog"]["enabled"]))
{
if (trim($config["watchdog"]["enabled"]) == 1)
{
while (true)
{
clearstatcache();
$last_heartbeat = filemtime("/dev/shm/heartbeat");
if ((time() - $last_heartbeat) > $config["watchdog"]["timeout"])
{
error_log("Exceeded timeout! Restarting lightdm.");
exec("systemctl restart lightdm");
// Cooldown delay (give the system time to reinitialize)
sleep(60);
}
if ((time() - $last_heartbeat) > $config["watchdog"]["timeout_reboot"])
{
error_log("Exceeded timeout_reboot! Rebooting system.");
exec("reboot");
}
sleep(5);
}
}
}

View File

@ -0,0 +1,27 @@
#!/bin/bash
if [ -f "/boot/wpa_supplicant.conf" ]; then
ln -s /boot/wpa_supplicant.conf /tmp/wpa_supplicant.conf
exit
fi
WIFI_SSID=$(get-ini /boot/kioskbrowser.ini wifi ssid)
WIFI_PSK=$(get-ini /boot/kioskbrowser.ini wifi psk)
WIFI_COUNTRY=$(get-ini /boot/kioskbrowser.ini wifi country)
if [ -n "${WIFI_SSID}" ]
then
echo "country=${WIFI_COUNTRY}" > /tmp/wpa_supplicant.conf
echo "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev" >> /tmp/wpa_supplicant.conf
echo "update_config=1" >> /tmp/wpa_supplicant.conf
echo "network={" >> /tmp/wpa_supplicant.conf
echo " ssid=\"${WIFI_SSID}\"" >> /tmp/wpa_supplicant.conf
if [ -n "${WIFI_PSK}" ]
then
echo " psk=\"${WIFI_PSK}\"" >> /tmp/wpa_supplicant.conf
else
echo " key_mgmt=NONE" >> /tmp/wpa_supplicant.conf
fi
echo "}" >> /tmp/wpa_supplicant.conf
fi

View File

@ -0,0 +1,8 @@
<?php
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
touch("/dev/shm/heartbeat");

View File

@ -0,0 +1,17 @@
<h1>Kioskbrowser</h1>
CPU temperature: <br>
<?php passthru("sudo vcgencmd measure_temp"); ?>
<br>
CPU voltage: <br>
<?php passthru("sudo vcgencmd measure_volts"); ?>
<br>
Throttling status (everything except 0x0 means throttling, get a better power supply!): <br>
<?php passthru("sudo vcgencmd get_throttled"); ?>
<br>
Last heartbeat:
<?php echo date("Y-m-d H:i:s", filemtime("/dev/shm/heartbeat")); ?>
<br>
<br><br>
<img src="/screenshot.php?<?php echo microtime(); ?>">

View File

@ -0,0 +1,3 @@
<?php
header("Content-Type: image/png");
passthru("sudo -u pi DISPLAY=:0 bash -c \"scrot - | cat\" 2>&1");

View File

@ -0,0 +1 @@
console=serial0,115200 console=tty1 root=PARTUUID=544c6228-02 rootfstype=ext4 ro rootwait quiet

View File

@ -0,0 +1,55 @@
#hdmi_safe=1
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16
# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2
# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4
enable_uart=1
disable_splash=1
dtparam=audio=on
gpu_mem=128
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Run in 64-bit mode
arm_64bit=1
# Disable compensation for displays with overscan
disable_overscan=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
[pi4]
# Run as fast as firmware / board allows
arm_boost=1
[all]

View File

@ -0,0 +1,43 @@
; KioskBrowser configuration file
[general]
hostname = "kioskpi"
[screen]
; can be used to force 1080p on 4k screens or workaround broken EDID communication
;force_resolution = "1920x1080"
[wifi]
; If you need more complex WiFi settings (like WPA2-Enterprise, hidden SSIDs, etc.)
; create a file called wpa_supplicant.conf on this partition.
country=DE
; Leave SSID empty to disable WiFi
ssid="My WiFi"
; Leave PSK empty (or comment) to use an open network
psk="My Passphrase"
[browser]
url="https://tbspace.de/"
; clear the browser cache every 10 minutes
cache_clear_interval=600
[watchdog]
; The watchdog can be used to ensure that a website is always being displayed (and JS is running).
; Javascript needs to send regular XHR/AJAX/etc. requests to http://localhost/heartbeat.php
enabled=0
; Seconds until the browser is restarted (to fix problems like error pages being displayed, temporary connection problems)
timeout=60
; Seconds until the whole computer is restarted (to fix worse issues like crashed firmware, lost time sync, broken certificates, etc.)
timeout_reboot=600
[vnc]
; VNC is pretty insecure, so it's always bound to localhost. Use an SSH tunnel to connect via VNC.
enabled=0
[autossh]
; autossh can be used to keep a connection to a specified SSH server up-and-running to allow for remote access
; without the need for port forwarding, public IPv4 addressing, dynamic DNS, etc.
enabled=0
args = "-p 22 -R 1234:127.0.0.1:22 tunnel@mydomain.de"
; security warning: ensure sshd_config "GatewayPorts" is set to "clientspecified" or "no".
; GatewayPorts=yes will cause the kioskpi to be globally bound (0.0.0.0, regardless of the bind-address specified above) and be reachable from the internet!
; This might be a huge risk.

View File

View File

@ -0,0 +1,9 @@
#!/bin/bash
echo > /etc/apt/sources.list
echo "deb http://deb.debian.org/debian bookworm main contrib non-free" >> /etc/apt/sources.list
echo "deb http://security.debian.org/debian-security bookworm-security main contrib non-free" >> /etc/apt/sources.list
echo "deb http://deb.debian.org/debian bookworm-updates main contrib non-free" >> /etc/apt/sources.list
apt update
APT_LISTCHANGES_FRONTEND=none DEBIAN_FRONTEND=noninteractive apt -o Dpkg::Options::="--force-confold" -f -y dist-upgrade