From e81b411cd58c1e129a3f7554beff5cd0bc16fbd8 Mon Sep 17 00:00:00 2001 From: Thomas Schneider <thomas@fsmpi.rwth-aachen.de> Date: Fri, 10 May 2024 15:03:55 +0200 Subject: [PATCH] Remodel for kiosk raspi setup --- .gitignore | 7 ++- Makefile | 13 ++--- README.md | 85 +++++++++++++++++++++++++++++ launch.sh | 31 ----------- make-image.sh | 144 +++++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 225 insertions(+), 55 deletions(-) create mode 100644 README.md delete mode 100755 launch.sh diff --git a/.gitignore b/.gitignore index 49765b9..c836431 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/image.sqfs -/initramfs-virt -/vmlinuz-virt -/apk-cache +apk-cache +root.img +boot.tar diff --git a/Makefile b/Makefile index 9bf2a8c..5258dea 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ IMAGE=docker.io/library/alpine -TAG=3.18 +TAG=3.19 +ARCH=arm64 -image.sqfs: - podman run --rm --volume $$PWD:/build --workdir /build \ +image: + podman run --arch $(ARCH) \ + --rm --volume $$PWD:/build --workdir /build \ $(IMAGE):$(TAG) \ - ./make-image.sh - -.PHONY: image.sqfs + ./make-image.sh $(ARCH) +.PHONY: image diff --git a/README.md b/README.md new file mode 100644 index 0000000..4f073bb --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# Kiosk terminal image builder for Raspberry Pi + +This project builds an image for a Raspberry Pi that boots directly into a +browser with a predefined web page. It is a soft fork of +https://git.fsmpi.rwth-aachen.de/thomas/ci-vm-image (which was designed for CI +VM images on amd64). + +The system runs from RAM, almost all changes at runtime are ephemeral. The +device can safely be unplugged without shutting down with no real risk of data +loss. As the boot partition is mounted read-write in order to regularly update +the software clock, there is still a minor chance, but in practice it is very +unlikely to corrupt the file system. + +On boot, the `kiosk` user is automatically logged in and launches Firefox in +kiosk mode with `/boot/CONFIG/kiosk.html` (see below). Terminal switching is +possible, both `root` and `kiosk` passwords are empty. When Firefox quits, it +will automatically be relaunched, unless `/tmp/noautologin` exists. + +:::warning +**Bug**: Firefox will not show (only blank screen) until the first user input, +such as mouse movement or a key press. +::: + +## Building + +Requirements: Podman and a native arm64 host or a correctly configured +and container-compatible qemu-binfmt setup. + +``` +% make # for arm64, RPi 3 and later +% make ARCH=arm # for armv7, RPi 2 +``` + +RPi 1 (armv6/armhf) is currently untested and unsupported, but could work. One +would need to pass `--variant` to the `podman` invocation or use a single-arch +image such as `docker.io/arm32v6/alpine`. + +The results (`boot.tar`, `root.img`) are placed in the `arm64` or `arm` +directory. + +### SELinux + +On SELinux-enabled systems (such as Fedora), the container may not have access +to the directories. Either move the whole building directory to an +appropriately labelled place, or customise the scripts to place the in-container +directories elsewhere, or set the type context label of the build directory to +`container_file_t` (e. g., `chcon -R -t container_file_t .`). You may +need to install `container-selinux` manually on Fedora. + +## Flashing + + 1. Partition an SD card with the typical RPi layout: + 1. a small-ish (~200MiB should be enough, cf. size of `boot.tar`) + partition at the beginning of MBR type `0x0c` for the boot file system, + 2. and a partition of at least 2GiB and type `0x83` for the root file + system. + 2. Create a FAT32 file system (e. g., `mkfs.vfat /dev/mmcblk0p1`) on the + first partition. + 3. Mount the first partition and extract `boot.tar` to its root. You can + ignore the error that the `boot` symlink cannot be created. + 4. Copy `root.img` to the second partition (e. g., `pv -pterbaY root.img + > /dev/mmcblk0p2`) + +## Configuration + +All configurables are stored in the `CONFIG` directory of the boot partition, +i. e., `/boot/CONFIG/` in the running system. + +`swclock` +: Software clock. On boot, system time is set to the time stamp of this file. + Every hour, the time stamp of this file is updated. During normal operation, + time should be updated via NTP. + +`interfaces` +: Configuration for [ifupdown-ng](https://github.com/ifupdown-ng/ifupdown-ng). + +`hostname` +: System host name. + +`kiosk.html` +: Opened on boot, would typically contain a redirect to a web site. + +`wpa_supplicant.conf` +: Additional WLAN configuration, required for WPA-EAP (PSK is possible via + `interfaces` alone). diff --git a/launch.sh b/launch.sh deleted file mode 100755 index 7c42af1..0000000 --- a/launch.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -set -e -set -x -exec qemu-system-x86_64 \ - -M microvm,x-option-roms=off,pit=off,pic=off,rtc=off,isa-serial=off,pcie=on \ - -bios /usr/share/qemu/qboot.rom \ - -enable-kvm \ - -cpu host \ - -m 1536M \ - -nodefaults \ - -no-user-config \ - -nographic \ - -device virtio-balloon-device \ - -device virtio-rng-device \ - -drive id=root,file=image.sqfs,format=raw,if=none \ - -device virtio-blk-device,drive=root \ - -kernel vmlinuz-virt \ - -initrd initramfs-virt \ - -append "root=/dev/vda overlaytmpfs console=hvc0 earlyprintk=hvc0" \ - -chardev stdio,mux=on,id=stdio \ - -device virtio-serial-device \ - -device virtconsole,chardev=stdio,name=console.0 \ - -mon chardev=stdio,mode=readline \ - -netdev user,id=net \ - -device virtio-net-device,netdev=net \ - -chardev socket,path=qga,server=on,wait=off,id=qga \ - -device virtserialport,chardev=qga,name=org.qemu.guest_agent.0 \ - -chardev socket,id=virtfs,path=/tmp/virtfs.sock \ - -device vhost-user-fs-pci,chardev=virtfs,tag=virtfs,queue-size=1024 \ - -object memory-backend-memfd,id=mem,size=1536M,share=on \ - -numa node,memdev=mem diff --git a/make-image.sh b/make-image.sh index bf4c68b..d66d270 100755 --- a/make-image.sh +++ b/make-image.sh @@ -1,5 +1,7 @@ #!/bin/sh +# Environment variable from outside +# shellcheck disable=SC2154 if test x"$container" != xpodman; then echo This script is supposed to be run inside the container. >&2 exit 1 @@ -7,42 +9,156 @@ fi set -e set -x +mkdir -p "$1" +cd "$1" + mkdir -p /image/etc/apk mkdir -p apk-cache ln -s "$PWD/apk-cache" /etc/apk/cache ln -s "$PWD/apk-cache" /image/etc/apk/cache cp -r /etc/apk/repositories /etc/apk/keys /image/etc/apk -apk add --root /image --update-cache --initdb alpine-base mkinitfs \ - qemu-guest-agent git git-lfs gitlab-runner bash buildah -echo 'features="base squashfs virtio"' > /image/etc/mkinitfs/mkinitfs.conf -apk add --root /image linux-virt + +apk add --root /image --update-cache --initdb \ + alpine-base mkinitfs raspberrypi raspberrypi-utils rng-tools chrony \ + kbd-bkeymaps font-terminus \ + dosfstools e2fsprogs \ + weston weston-backend-drm weston-shell-desktop weston-terminal \ + mesa-dri-gallium \ + pam-rundir seatd shadow-login agetty \ + wpa_supplicant wireless-tools iw iwd \ + eudev udev-init-scripts udev-init-scripts-openrc \ + firefox-esr font-dejavu font-noto +echo 'features="base ext4 kms mmc usb"' > /image/etc/mkinitfs/mkinitfs.conf +apk add --root /image linux-rpi raspberrypi-bootloader oldpwd="$PWD" cd /image/etc/runlevels -for i in devfs dmesg mdev hwdrivers cgroups; do +for i in devfs dmesg cgroups localmount udev udev-trigger udev-settle udev-postmount; do ln -s /etc/init.d/$i sysinit/$i done -for i in modules hostname sysctl bootmisc syslog osclock networking; do +for i in bootmisc consolefont hostname klogd loadkmap modules networking rngd seedrng swclock sysctl syslog; do ln -s /etc/init.d/$i boot/$i done -ln -s /etc/init.d/killprocs shutdown/killprocs -for i in qemu-guest-agent; do +for i in killprocs mount-ro savecache; do + ln -s /etc/init.d/$i shutdown/$i +done +for i in crond chronyd seatd; do ln -s /etc/init.d/$i default/$i done cd "$oldpwd" -echo hvc0::respawn:/sbin/getty 115200 hvc0 >> /image/etc/inittab -cat <<EOF >/image/etc/network/interfaces +mkdir /image/boot/CONFIG + +echo 'swclock_file=/boot/CONFIG/swclock' > /image/etc/conf.d/swclock +touch /image/boot/CONFIG/swclock + +cat <<EOF > /image/etc/periodic/hourly/swclock.sh +#!/bin/sh +touch /boot/CONFIG/swclock +EOF +chmod +x /image/etc/periodic/hourly/swclock.sh + +ln -s /boot/CONFIG/interfaces /image/etc/network/interfaces +cat <<EOF >/image/boot/CONFIG/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp + +#auto wlan0 +#iface wlan0 +# use dhcp +# use wifi +# wifi-config-path /boot/CONFIG/wpa_supplicant.conf + +#auto wlan0 +#iface wlan0 +# use dhcp +# use wifi +# wifi-ssid Freifunk + +#auto wlan0 +#iface wlan0 +# use dhcp +# use wifi +# wifi-ssid FranzBox1337 +# wifi-psk hunter2 +EOF + +ln -fs /boot/CONFIG/hostname /image/etc/hostname +echo localhost > /image/boot/CONFIG/hostname + +cat <<EOF >> /image/etc/fstab +/dev/mmcblk0p1 /boot vfat fmask=0133,dmask=0022 0 2 +tmpfs /tmp tmpfs nosuid,nodev 0 0 +EOF + +cat <<EOF > /image/boot/CONFIG/kiosk.html +<!doctype html> +<meta http-equiv="refresh" content="0; url=https://kiosk.kif.rocks" /> +EOF + +cat <<EOF > /image/boot/CONFIG/wpa_supplicant.conf +network={ + ssid="eduroam" + key_mgmt=WPA_EAP + eap=PWD + identity="u1138@jku-lummerland.edu" + password="hunter2" +} EOF +chroot /image setup-user -u -g seat kiosk + +cat <<EOF > /image/home/kiosk/.profile +if [ \$(tty) = /dev/tty1 -a ! -e /tmp/noautologin ] ; then + exec weston +fi +EOF + +cat <<EOF > /image/home/kiosk/kiosk.sh +#!/bin/sh +exec firefox-esr --kiosk /boot/CONFIG/kiosk.html +EOF +chmod +x /image/home/kiosk/kiosk.sh +chown 1000:1000 /image/home/kiosk/kiosk.sh + +mkdir /image/home/kiosk/.config +cat <<EOF > /image/home/kiosk/.config/weston.ini +[core] +shell=kiosk +xwayland=false + +[keyboard] +keymap_layout=de + +[autolaunch] +path=/home/kiosk/kiosk.sh +watch=true +EOF + +chown -R 1000:1000 /image/home/kiosk + +chroot /image /sbin/setup-keymap de de + +echo 'consolefont="ter-128n.psf.gz"' > /image/etc/conf.d/consolefont + +sed \ + -e '/tty1/c\tty1::respawn:/sbin/agetty --autologin kiosk --noclear tty1' \ + -i /image/etc/inittab + +echo 'root=/dev/mmcblk0p2 rootfstype=ext4 fsck.repair=yes rootwait overlaytmpfs' > /image/boot/cmdline.txt +echo 'dtoverlay=vc4-kms-v3d' > /image/boot/usercfg.txt + find /image/var/cache/apk -name APKINDEX.\* -delete rm -f /image/etc/apk/cache -mv /image/boot/vmlinuz-virt /image/boot/initramfs-virt . -apk add squashfs-tools -rm -f image.sqfs -mksquashfs /image/ image.sqfs + +rm -f boot.tar +tar -C /image/boot -cf boot.tar . +find /image/boot -mindepth 1 -delete + +apk add e2fsprogs +rm -f root.img +truncate -s 2G root.img +mkfs.ext4 -d /image/ root.img -- GitLab