Skip to content

patatetom/AWK

Repository files navigation

Alpine Linux Web Kiosk

Alpine Web Kiosk

AWK is a web kiosk based on Alpine Linux (3.23)

with this repo, you can (quickly and easily) set up a web kiosk based on Alpine Linux and Chromium web browser : « a web kiosk is a self-service computer terminal accessible to the public that allows users to view informations or perform actions via web interfaces in a limited and controlled manner »

installation

changes may be necessary (keyboard fr, disk sda, …)
be sure to select correct disk if there are multiple available
installation must be performed under EFI/UEFI mode
Secure Boot must be disabled on EFI/UEFI platform

  • boot Alpine Linux ISO image on PC containing media storage for the future web kiosk
  • login as root without a password (empty password)
  • start installation with
KERNELOPTS="quiet mitigations=off" \
DISKLABEL=gpt \
BOOTLOADER=grub \
BOOTFS=vfat \
ROOTFS=btrfs \
setup-alpine
  • and then set
    • keymap fr
    • keyboard layout fr
    • hostname AWK
    • initialize interface eth0
    • ip address dhcp
    • root password **********
    • timezone Europe
    • sub timezone Paris
    • proxy none
    • ntp client busybox
    • apk mirror c (community) 1 (mirros.ircam.fr)
    • user browser
    • full name Chromium
    • password chromium
    • retype password chromium
    • ssh key or URL none
    • ssh server openssh
    • allow root ssh prohibit-password
    • ssh key or URL none
    • disk sda
    • type sys
    • erase y
  • reboot reboot

configuration

changes may be necessary (SSH public key, disk sda, …)
be sure to select correct disk if there are multiple available

  • boot AWK from media storage selected during installation
  • login as root with defined password
  • update and upgrade system apk update && apk upgrade

💡 entire configuration process described below can be automated using setup-AWK script 🚀

  • change domain name resolution

simple AD blocker can be easily added

apk add dnsmasq
rc-update add dnsmasq
cat > /etc/dnsmasq.conf << 'xxxxxxxx'
resolv-file=/etc/resolv.dnsmasq
domain-needed
bogus-priv
interface=lo
no-dhcp-interface=lo
listen-address=127.0.0.1,::1
bind-interfaces
cache-size=2048
xxxxxxxx
chattr -i /etc/resolv.conf
cat > /etc/resolv.conf << 'xxxxxxxx'
nameserver 127.0.0.1
nameserver ::1
xxxxxxxx
chattr +i /etc/resolv.conf
rc-service dnsmasq start
echo 'RESOLV_CONF="/etc/resolv.dnsmasq"' > /etc/udhcpc/udhcpc.conf
rc-service networking restart
  • modify EFI System Partition (ESP)
apk add sgdisk mtools dosfstools
mkdir -p /boot/efi
if [ -b /dev/sda4 ]; then
    boot=/dev/sda2
    sgdisk /dev/sda \
        --typecode=1:ef02 --change-name=1:GRUB \
        --typecode=2:0700 --change-name=2:AWK \
        --typecode=3:8200 --change-name=3:SWAP \
        --typecode=4:8300 --change-name=4:ROOT
else
    boot=/dev/sda1
    sgdisk /dev/sda \
        --typecode=1:0700 --change-name=1:AWK \
        --typecode=2:8200 --change-name=2:SWAP \
        --typecode=3:8300 --change-name=3:ROOT
fi
echo "drive x: file=\"$boot\"" > /etc/mtools.conf
mattrib +h +s x:/efi 2> /dev/null
dosfslabel "$boot" AWK > /dev/null 2>&1
  • make few minor changes
: | tee /etc/issue > /etc/motd
sed -i 's/^wheel:x:10:root,browser/wheel:x:10:root/' /etc/group
sed -i -E \
  -e '/^\/dev\/(cdrom|usb)/d' \
  -e 's/iocharset=utf8/iocharset=iso8859-1,utf8/' \
  /etc/fstab
cat > /etc/network/interfaces << 'xxxxxxxxxx'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
udhcpc_opts -t 1 -b -S
xxxxxxxxxx
  • add widest hardware support (AWK on a USB device)
apk add linux-firmware
  • modify GRUB loader (quiet boot)
chmod -x /etc/grub.d/*
chmod +x /etc/grub.d/00_header
chmod +x /etc/grub.d/10_linux
if ! grep -q '^GRUB_TIMEOUT_STYLE=hidden' /etc/default/grub; then
  cat >> /etc/default/grub << 'xxxxxxxx'

GRUB_TIMEOUT_STYLE=hidden
GRUB_DISABLE_OS_PROBER=true
xxxxxxxx
fi
rc-update add local default
cat > /etc/local.d/grub.echo.stop << 'xxxxxxxx'
#!/bin/sh
if grep -q 'Loading Linux lts' /boot/grub/grub.cfg; then
  sed -i /boot/grub/grub.cfg \
      -e "s/'Loading Linux lts/; echo '  Loading AWK/" \
      -e '/Loading initial ramdisk/d'
fi
xxxxxxxx
chmod +x /etc/local.d/grub.echo.stop
  • make dynamic hostname (MAC based / multiple kiosks on same LAN)
cat > /etc/init.d/machostname << 'xxxxxxxx'
#!/sbin/openrc-run
depend()
{
  after hostname
}
start()
{
  host="AWK-$( tr -d ':' < /sys/class/net/eth0/address )"
  echo "$host" > /etc/hostname
  hostname "$host"
  cat > /etc/hosts << oooooooo
127.0.0.1 $host.localdomain $host localhost.localdomain localhost
::1       localhost localhost.localdomain
oooooooo
}
xxxxxxxx
chmod +x /etc/init.d/machostname
rc-update add machostname boot
rc-service machostname start
  • configure remote access (remote administration)

generate an SSH key pair from administration workstation ssh-keygen -t ed25519 -C comment -f ./AWK.key
use content of public key cat ./AWK.key.pub below or copy public key to ~/.ssh/authorized_keys at AWK

mkdir -p ~/.ssh/
echo "ssh-ed25519 … … … … … comment" > ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
  • install graphics server, applications and extensions
setup-xorg-base
apk add setxkbmap
apk add font-dejavu font-inconsolata font-liberation font-linux-libertine font-noto-emoji ttf-freefont
apk add jwm
apk add chromium chromium-lang
cat > /etc/X11/xorg.conf << 'xxxxxxxx'
Section "ServerFlags"
  Option "DontVTSwitch" "true"
  Option "DontZap" "true"
EndSection
Section "InputClass"
  Identifier "touchpad"
  MatchDriver "libinput"
  MatchIsTouchpad "on"
  Option "Tapping" "on"
  Option "NaturalScrolling" "on"
EndSection
xxxxxxxx
  • setup default web page
cat > /etc/local.d/default.web.page.start << 'xxxxxxxx'
#!/bin/sh
mkdir -p "${root:=/boot/efi/www}"
ln -sf "$root" /
if [ ! -f "${index:=$root/AWK.html}" ]; then
  cat > "$index" << 'oooooooo'
<html style="background-color:#0E5980;color:#00ccff;font-family:sans;font-style:italic;font-weight:bold">
<title>AWK</title>
<div style="font-size:4em;text-align:center"><br/><br/>
<svg width="256" height="132" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#0e5980;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke" d="M16 0h224c8.864 0 16 7.136 16 16v100c0 8.864-7.136 16-16 16H16c-8.864 0-16-7.136-16-16V16C0 7.136 7.136 0 16 0Z"/>
<g stroke="#000" stroke-linejoin="round" stroke-width="16.83"><path d="m9.662 93.904 85.171-83.137 85.171 83.137M248.142 93.904 171.487 19.08l-17.034 16.628M79.282 68.963v24.941" style="fill:none;stroke:#fff;stroke-opacity:1" transform="translate(-.902 -1.227)"/></g>
<path stroke="#000" stroke-linejoin="round" d="m62.975 115.321-11.9 11.9" stroke-width="13.804" style="fill:none;stroke:#1b93c0;stroke-opacity:1" transform="translate(0 -1.227)"/>
<path style="fill:none;stroke:#1b93c0;stroke-width:17.0001;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="M22.684 114.909h210.632Z" transform="translate(0 -1.227)"/>
<text xml:space="preserve" style="font-size:85.3333px;line-height:1.25;font-family:sans-serif;fill:#0cf" x="17.313" y="103.104"><tspan x="17.313" y="103.104" style="font-style:oblique;font-variant:normal;font-weight:700;font-stretch:normal;font-size:85.3333px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Oblique';fill:#0cf;fill-opacity:1">AWK</tspan></text>
</svg>
</br>Alpine Web Kiosk
</div>
</html>
oooooooo
fi
xxxxxxxx
chmod +x /etc/local.d/default.web.page.start
rc-service local start
  • configure system initialization (minimum, silent, and auto-login for browser user)

no console access with this /etc/inittab configuration
uncomment #tty2::respawn:/sbin/getty 38400 tty2 to retain console access
and/or uncomment #ttyS0::respawn:/sbin/getty -L 0 ttyS0 vt100 for serial console access
and/or access AWK via secure shell (prefered)
sleep may be added (uncomment) to make it easier to read assigned network address (see also conky)

cat > /etc/inittab << 'xxxxxxxx'
::sysinit:clear
::sysinit:printf '\n\n  Starting AWK ...\n\n'
::sysinit:/sbin/openrc sysinit -q > /dev/null
::sysinit:/sbin/openrc boot -q > /dev/null
#::sysinit:sleep 3s
::wait:/sbin/openrc default -q > /dev/null
tty1::respawn:/bin/login -f browser
#tty2::respawn:/sbin/getty 38400 tty2
#ttyS0::respawn:/sbin/getty -L 0 ttyS0 vt100
::ctrlaltdel:clear
::ctrlaltdel:/sbin/reboot > /dev/null 2>&1
::shutdown:clear
::shutdown:/sbin/openrc shutdown -q > /dev/null 2>&1
xxxxxxxx
  • configure login for browser user (graphics server automatic startup at login)

~/.chromium.tgz is a pre-configuration for Chromium (view file in repository)

cat > /home/browser/.profile << 'xxxxxxxx'
clear
printf '\n\n  Starting browser ...\n'
export LANG=fr
export LC_COLLATE=C
rm -rf \
  ~/.Xauthority* \
  ~/.serverauth.* \
  ~/.cache/chromium \
  ~/.config/chromium \
  > /dev/null 2>&1
tar -C ~ -xf ~/.chromium.tgz > /dev/null 2>&1
exec startx > /dev/null 2>&1
xxxxxxxx
  • configure window manager's startup
cat > /home/browser/.xinitrc << 'xxxxxxxx'
setxkbmap fr
xset -dpms
xset s off
xset s noblank
exec jwm
xxxxxxxx
  • set up web browser auto-start
cat > /home/browser/.jwmrc << 'xxxxxxxx'
<?xml version="1.0" encoding="UTF-8"?>
<JWM>
<StartupCommand>
clear > /dev/tty1
if [ -f "${urls:=/boot/efi/urls.txt}" ]; then
  urls=$( grep -E -o '^(file|http(s)?)://[^ ]+' "$urls" )
else
  urls=file:///www/AWK.html
fi
chromium \
  --start-maximized \
  --no-first-run \
  --autoplay-policy=no-user-gesture-required \
  --disable-infobars \
  --disable-session-crashed-bubble \
  --disable-restore-session-state \
  --disable-component-update \
  --check-for-update-interval=315360000 \
  --disable-pinch \
  --disable-features=TranslateUI \
  --disable-extensions \
  --disable-background-networking \
  --disable-sync \
  --disable-default-apps \
  --process-per-site \
  --disk-cache-size=0 \
  --password-store=basic \
  --noerrdialogs \
  $urls
rm -rf \
  ~/.serverauth.* \
  ~/.cache/chromium \
  ~/.config/chromium
jwm -exit
</StartupCommand>
</JWM>
xxxxxxxx

Chromium configuration

  • disable file:// scheme (except for default web page) et fix download directory
mkdir -p /etc/chromium/policies/managed/
cat > /etc/chromium/policies/managed/AWK.json << 'xxxxxxxx'
{
  "URLAllowlist": ["file:///www/"],
  "URLBlocklist": ["file://"],
  "DownloadRestrictions": 3,
  "DownloadDirectory": "/tmp/"
}
xxxxxxxx
  • install provided pre-configuration (optional)

this file is used in /home/browser/.profile

wget -O /home/browser/.chromium.tgz \
https://github.com/patatetom/AWK/raw/refs/heads/main/chromium.config.tgz
# check tgz file
# tar tvzf /home/browser/.chromium.tgz

web kiosk customization

%part1%/urls.txt

/boot/efi/urls.txt on AWK

urls.txt file, located in root directory of first partition, tells browser which web page(s) to open and can be easily installed and configured

# default updated web page for kiosk user guide
file:///www/AWK.html
# DuckDuckGo
https://duckduckgo.com/
# AWK ;-)
https://github.com/patatetom/AWK/

%part1%/www/AWK.html

/boot/efi/www/AWK.html or /www/AWK.html (symlink) on AWK

AWK.html file, stored in /www/ folder located in root of first partition, is default web page opened by browser and can be easily updated and expanded

notes

  • AWK is « French-oriented »
  • ESP partition is redesigned when installing on removable USB media (Windows with portable kiosk)
  • BTRFS file system is preferred over EXT4 when installing on removable USB media (portable kiosk)
  • audio and printing functions are not built-in but can be easily added
  • WiFi connectivity is not built-in but can be easily added
  • press ESC at boot to access GRUB bootloader and press E to edit/add/change kernel parameters
  • transferring AWK from one media storage to another usually requires fixing GPT (some EFI/UEFI platforms refuse to boot if backup GPT is not valid)

screencast

AWK in action

Contributors

Languages