Services (daynivo-kiosk-*, daynivo-network-guard, daynivo-boot-import), programs in /usr/lib/daynivo-kiosk, folders /etc, /var/lib, /var/log and /var/cache daynivo-kiosk*, the system user daynivo, DAYNIVO_* settings, the plymouth theme, network profiles daynivo-eth0/wlan0, the image stage and its first user daynivoadmin. install.sh moves a kiosk installed up to 1.2.x over once: old services stopped and removed, folders moved (configuration, password, browser profile, logs) with symlinks under the old names, the voxip user renamed with the same uid, network profiles renamed unless a change awaits confirmation, and old leftovers removed. Tested by upgrading a configured 1.2.0 installation in a container, and running the install a second time. Old names keep working where people type or place them: voxip-config.json, voxip-authorized_keys, voxip-network-reset, voxip-kiosk-ctl, and VOXIP_DEFAULT_URL / VOXIP_DEVICE_NAME for image builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 KiB
Architectuur — Daynivo Kiosk OS
Dit document is het architectuurvoorstel waarop de implementatie in deze repository gebaseerd is. Het beschrijft de onderdelen, hun rechten, hoe ze samenwerken en waarom bepaalde keuzes zijn gemaakt.
Doel en randvoorwaarden
- Raspberry Pi 3 Model B/B+ (1 GB RAM), Raspberry Pi OS Lite 32-bit (Bookworm of Trixie), 24/7 in bedrijf.
- Eén HTTPS-pagina fullscreen in Chromium, zonder desktopomgeving.
- Lokale webbeheerinterface (Daynivo Kiosk Manager) op poort 8080.
- Geen klantspecifieke code: alles wat per klant verschilt staat in
/etc/daynivo-kiosk/config.jsonen kan bij uitrol via de bootpartitie worden meegegeven (daynivo-config.json).
Overzicht
┌──────────────────────── Raspberry Pi ────────────────────────┐
│ │
HDMI ◄────────┤ daynivo-kiosk-x.service Xorg :0 op vt7 (user kiosk) │
│ ▲ │
│ │ DISPLAY=:0 │
│ daynivo-kiosk-browser.service Chromium --kiosk (user kiosk) │
│ ▲ CDP 127.0.0.1:9222 │
│ │ ┌─────────────────────────────┐ │
│ ├──┤ daynivo-kiosk-agent.service │ navigatie, offline-│
│ │ │ (user kiosk) │ pagina, verversen, │
│ │ └─────────────────────────────┘ hang-detectie │
│ │ │
LAN :8080 ───►│ daynivo-kiosk-manager.service Flask + gunicorn (user daynivo) │
│ │ sudo -n (één commando) │
│ ▼ │
│ daynivo-kiosk-helper root, vaste werkwoorden, │
│ strikte invoervalidatie │
│ │
│ daynivo-kiosk-healthcheck.timer elke minuut, root │
│ daynivo-network-guard.service netwerk-rollback bij boot │
│ daynivo-boot-import.service config/SSH-sleutels vanaf │
│ de bootpartitie │
└──────────────────────────────────────────────────────────────┘
Processen en rechten
| Onderdeel | Gebruiker | Taak |
|---|---|---|
daynivo-kiosk-x |
kiosk (via Xorg.wrap) |
Minimale X-server op vt7, zonder cursor, zonder TCP. |
daynivo-kiosk-browser |
kiosk |
Past rotatie/resolutie/DPMS toe (xrandr/xset), ruimt een vastgelopen profiel op en start Chromium. |
daynivo-kiosk-agent |
kiosk |
Bestuurt Chromium via het DevTools Protocol op localhost: offline-pagina, URL-wissel zonder herstart, periodiek verversen, hang-detectie. |
daynivo-kiosk-manager |
daynivo |
Flask-webinterface (gunicorn, 1 worker, 4 threads). |
daynivo-kiosk-helper |
root (via sudo) |
Enige geprivilegieerde ingang: reboot, uitschakelen, Chromium herstarten, hostname, wifi-land, wifi-scan, netwerkconfiguratie + rollback, updates controleren en starten. |
daynivo-kiosk-updater |
root (timer, of eigen transient unit via de helper) |
Haalt de nieuwste release op (HTTPS, SHA-256), pakt hem uit in /opt/daynivo-kiosk-os/<versie> en draait update.sh --no-pull. Nooit een oudere of gelijke versie. |
daynivo-kiosk-healthcheck |
root |
Bewaakt alle services en herstelt ze, met logging. |
De webinterface draait nooit als root. De sudoers-regel staat
uitsluitend /usr/lib/daynivo-kiosk/daynivo-kiosk-helper toe. De helper:
- draait met
python3 -I(geenPYTHONPATH, geen user site-packages); - kent alleen een vaste lijst werkwoorden; interne werkwoorden (zoals
network-activate) weigert hij als hij via sudo wordt aangeroepen; - valideert iedere parameter opnieuw (de webinterface is niet vertrouwd);
- start alle subprocessen met een argumentenlijst, nooit via een shell;
- schrijft netwerkprofielen zelf als NetworkManager-keyfile (0600), zodat wachtwoorden nooit op een commandoregel verschijnen.
Waarom X11 + twee services
Xorg en Chromium zijn aparte services. Daardoor is Chromium herstarten
snel (X blijft draaien) en geeft het exitcode van Chromium direct aan
systemd door. daynivo-kiosk-browser heeft Requires= en PartOf= op
daynivo-kiosk-x: valt X weg, dan start de browser opnieuw mee.
Chromium besturen via CDP
Chromium luistert met --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222, dus alleen lokaal. De agent en de
webinterface gebruiken dit om:
- de pagina te herladen zonder de browser te herstarten;
- naar een nieuwe URL te navigeren na een configuratiewijziging;
- een screenshot te maken;
- te detecteren of de renderer nog reageert (
Runtime.evaluate).
Offline-gedrag
Chromium start altijd op een lokale startpagina
(/usr/share/daynivo-kiosk/www/start.html). De agent controleert met een
HTTPS-verzoek (mét certificaatvalidatie) of de doel-URL bereikbaar is en
navigeert pas dan. De offline-pagina verschijnt alleen als de pagina niet
geladen kan worden (startpagina, Chromium-foutpagina). Valt het
netwerk weg terwijl een pagina al draait, dan blijft die pagina staan.
Zo blijven applicaties met een eigen offline-cache (zoals Daynivo-
displays) gewoon hun laatste inhoud tonen. Automatisch verversen wordt
overgeslagen zolang de doel-URL onbereikbaar is.
Initiële configuratie
Zolang er geen beheerderswachtwoord is:
- maakt de webinterface een eenmalige setupcode aan;
- toont de kiosk op het HDMI-scherm een setuppagina met die code en de adressen van de beheerinterface;
- kan een wachtwoord alleen ingesteld worden met die code.
Wie fysiek bij het scherm staat (of via SSH daynivo-kiosk-ctl setup-code
draait) kan het apparaat dus claimen, iemand anders op het netwerk niet.
Netwerkwijzigingen met rollback
- De helper maakt een snapshot van
/etc/NetworkManager/system-connectionsen van de actieve verbindingen. - Hij schrijft het nieuwe profiel (
daynivo-eth0/daynivo-wlan0, prioriteit 100) en plant een rollback-timer (systemd-run, 180 s). - Activatie gebeurt asynchroon, zodat het HTTP-antwoord nog aankomt.
- De beheerder bevestigt binnen 180 s (eventueel via het nieuwe adres), anders zet de timer de snapshot terug.
- Valt de stroom uit tijdens die periode, dan zet
daynivo-network-guard.servicede snapshot terug bij het opstarten, nog vóór NetworkManager start. - Noodherstel: een leeg bestand
daynivo-network-resetop de bootpartitie verwijdert bij de volgende boot alle Daynivo-netwerkprofielen (terug naar DHCP).
Configuratie
/etc/daynivo-kiosk/config.json: eigenaardaynivo, groepkiosk, 0640. Wordt atomair opgeslagen (tijdelijk bestand,fsync,rename,fsyncvan de map) met een.bakvan de vorige versie. Ongeldige waarden vallen bij het laden terug op de standaard, zodat de kiosk altijd start. Onbekende sleutels blijven bewaard (vooruit-/achteruitcompatibel)./etc/daynivo-kiosk/admin.json: Argon2id-hash, 0600, alleendaynivo.- Updates (
install.sh --upgrade/update.sh/ de updater) overschrijven nooit bestaande configuratie. De updater draait in een eigen systemd-unit, zodat het herstarten van de beheerinterface tijdensinstall.shde update niet afbreekt. De checksum komt van dezelfde server als de download: hij vangt een kapotte download af, geen gehackte releaseserver.
Betrouwbaarheid (24/7)
- Alle services
Restart=metStartLimitBurst; de healthcheck logt een start-limit-hit als fout en probeert na 5 minuten opnieuw. Er zijn dus geen stille herstartloops en geen permanente uitval. - Hardware-watchdog (
RuntimeWatchdogSec=15s): een vastgelopen kernel zorgt voor een automatische reboot. - Chromium-profiel: profielvergrendelingen en de crash-vlag worden voor iedere start opgeruimd, zodat er na stroomuitval geen dialoog verschijnt.
- HTTP-cache van Chromium in RAM (
/run), het profiel zelf (cookies, localStorage/IndexedDB) blijft standaard persistent zodat offline-caches van webapps een reboot overleven (browser_profile: "ephemeral"zet ook dat in RAM). - journald maximaal 32 MB en synchroniseert eens per 15 minuten;
/tmpals tmpfs; zram in plaats van swap op de SD-kaart. - Logrotatie voor het audit-log van de beheerinterface.
Beveiliging
- Argon2id (
argon2-cffi, 64 MiB, t=3, p=2), geen standaardwachtwoord. - Sessiecookie:
HttpOnly,SameSite=Strict,Securebij HTTPS, 60 min inactiviteit, ongeldig na een wachtwoordwijziging (sessie-epoch). - CSRF-token op iedere POST, plus controle van de
Origin-header. - Login-throttling per IP (exponentieel tot 15 min) en globaal.
- Beheerinterface weigert standaard verzoeken van publieke IP-adressen (toegestaan: privé, link-local, loopback, CGNAT/VPN 100.64.0.0/10).
- Optioneel HTTPS (zelfondertekend certificaat, automatisch aangemaakt).
- SSH staat in de image standaard uit en wordt alleen ingeschakeld met
credentials uit Raspberry Pi Imager of
daynivo-authorized_keysop de bootpartitie (dan alleen sleutel-login). - Strikte security-headers en CSP (
default-src 'self').
Directorystructuur
daynivo-kiosk-os/
├── README.md bouw-, flash-, installatie-instructies
├── VERSION
├── Makefile test / lint / image
├── install.sh installatie op bestaande Pi OS + gebruikt door image-build
├── update.sh update met backup van configuratie
├── uninstall.sh verwijderen (--purge voor alles)
├── bin/ dunne launchers → /usr/lib/daynivo-kiosk/
├── src/daynivo_kiosk/ Python-package (alle logica)
│ ├── config.py laden/valideren/atomair opslaan
│ ├── validation.py gedeelde invoervalidatie
│ ├── cdp.py Chrome DevTools Protocol-client
│ ├── browser.py Chromium-launcher (xrandr, xset, profiel)
│ ├── agent.py kiosk-agent
│ ├── healthcheck.py healthcheck
│ ├── helper.py root-helper
│ ├── netconfig.py NetworkManager-keyfiles, nmcli-parsing
│ ├── netinfo.py netwerkstatus (zonder rechten)
│ ├── sysinfo.py CPU, temperatuur, geheugen, opslag, IP
│ ├── services.py systemd- en journal-status
│ ├── ctl.py CLI voor SSH-beheer (daynivo-kiosk-ctl)
│ └── web/ Flask-app, templates, statische bestanden
├── www/ lokale kioskpagina's (start, offline, setup)
├── systemd/ unit-bestanden
├── config/ standaardconfig, sudoers, journald, logrotate, …
├── image/ pi-gen image-builder (stage-daynivo)
├── tests/ pytest-suite
└── docs/ architectuur, hardwaretest