Add kiosk
This commit is contained in:
@@ -43,6 +43,8 @@ env:
|
|||||||
# per-environment DNS name (e.g. nvme.fleet.lab) resolved at boot so one UKI
|
# per-environment DNS name (e.g. nvme.fleet.lab) resolved at boot so one UKI
|
||||||
# works across environments — the serving stack's dnsmasq resolves it.
|
# works across environments — the serving stack's dnsmasq resolves it.
|
||||||
NVMET_TRADDR: "192.168.50.10"
|
NVMET_TRADDR: "192.168.50.10"
|
||||||
|
# URL the fullscreen Chromium kiosk loads at boot. Empty disables the kiosk.
|
||||||
|
KIOSK_URL: "https://www.reddit.com"
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# TEMPORARY: 802.1X + MACsec are DISABLED for this build (rd.dot1x=0) so the
|
# TEMPORARY: 802.1X + MACsec are DISABLED for this build (rd.dot1x=0) so the
|
||||||
# UKI / rootfs / NVMe-oF boot path can be validated on an OPEN switch port.
|
# UKI / rootfs / NVMe-oF boot path can be validated on an OPEN switch port.
|
||||||
@@ -156,4 +158,5 @@ jobs:
|
|||||||
-var disable_dot1x=${{ env.DISABLE_DOT1X }} \
|
-var disable_dot1x=${{ env.DISABLE_DOT1X }} \
|
||||||
-var nvmet_traddr="${{ env.NVMET_TRADDR }}" \
|
-var nvmet_traddr="${{ env.NVMET_TRADDR }}" \
|
||||||
-var ssh_authorized_key="${{ secrets.SSH_AUTHORIZED_KEY }}" \
|
-var ssh_authorized_key="${{ secrets.SSH_AUTHORIZED_KEY }}" \
|
||||||
|
-var kiosk_url="${{ env.KIOSK_URL }}" \
|
||||||
.
|
.
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ variable "admin_user" {
|
|||||||
default = "fleetadmin"
|
default = "fleetadmin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "kiosk_url" {
|
||||||
|
type = string
|
||||||
|
description = "URL the fullscreen Chromium kiosk loads at boot (cage Wayland session on tty1, baked into the immutable root). Empty disables the kiosk install entirely. A runtime config-agent could override it later via /etc/kiosk/kiosk.env."
|
||||||
|
default = "https://www.reddit.com"
|
||||||
|
}
|
||||||
|
|
||||||
variable "sb_key" {
|
variable "sb_key" {
|
||||||
type = string
|
type = string
|
||||||
description = "Secure Boot signing key (PEM) for the UKI. Empty = unsigned lab build. Production: CI/HSM-held; never enters the build VM."
|
description = "Secure Boot signing key (PEM) for the UKI. Empty = unsigned lab build. Production: CI/HSM-held; never enters the build VM."
|
||||||
@@ -186,6 +192,14 @@ build {
|
|||||||
destination = "/tmp/etc-stage"
|
destination = "/tmp/etc-stage"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Kiosk unit + session wrapper (installed by the kiosk provisioner below).
|
||||||
|
# Dir-without-trailing-slash upload -> /tmp/kiosk/ (mirrors the dracut module
|
||||||
|
# upload form, which stages reliably).
|
||||||
|
provisioner "file" {
|
||||||
|
source = "files/kiosk"
|
||||||
|
destination = "/tmp/"
|
||||||
|
}
|
||||||
|
|
||||||
# --- 2. Packages, configs, diskless adaptations --------------------------
|
# --- 2. Packages, configs, diskless adaptations --------------------------
|
||||||
provisioner "shell" {
|
provisioner "shell" {
|
||||||
inline = [
|
inline = [
|
||||||
@@ -244,6 +258,41 @@ build {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- 2b. Kiosk mode: Wayland (cage) + fullscreen Chromium ---------------
|
||||||
|
# Post-pivot graphical kiosk (no initramfs impact). Baked into the immutable
|
||||||
|
# root, fleet-generic. Empty kiosk_url skips it. EPEL is enabled for cage +
|
||||||
|
# chromium — a supply-chain surface (accepted for the lab; see TODO #4).
|
||||||
|
# NOTE: pulls in Chromium + Mesa (~hundreds of MB) — grows the uncompressed
|
||||||
|
# erofs namespace noticeably. Graphical bring-up needs a display to verify;
|
||||||
|
# likely tweak points: cage availability in EPEL, chromium ozone/wayland,
|
||||||
|
# WLR_RENDERER, --disable-gpu, and SELinux AVCs for the GUI stack.
|
||||||
|
provisioner "shell" {
|
||||||
|
environment_vars = ["KIOSK_URL=${var.kiosk_url}"]
|
||||||
|
inline = [
|
||||||
|
"set -e",
|
||||||
|
"if [ -z \"$KIOSK_URL\" ]; then echo 'kiosk_url empty -> skipping kiosk install.'; exit 0; fi",
|
||||||
|
"echo '==> Enabling EPEL + installing kiosk stack (cage, chromium, mesa)'",
|
||||||
|
"dnf install -y epel-release",
|
||||||
|
"dnf install -y cage chromium mesa-dri-drivers mesa-libEGL mesa-libgbm dejavu-sans-fonts",
|
||||||
|
# Unprivileged, passwordless kiosk user; video/input as a seat fallback.
|
||||||
|
"id kiosk >/dev/null 2>&1 || useradd -m -G video,input kiosk",
|
||||||
|
# Install the staged unit + wrapper.
|
||||||
|
"install -D -m0755 /tmp/kiosk/kiosk-session /usr/local/bin/kiosk-session",
|
||||||
|
"install -D -m0644 /tmp/kiosk/kiosk.service /etc/systemd/system/kiosk.service",
|
||||||
|
# URL (from the var) + software wlroots renderer for VMs without a GPU.
|
||||||
|
"install -d -m0755 /etc/kiosk",
|
||||||
|
"printf 'KIOSK_URL=%s\\nWLR_RENDERER=pixman\\n' \"$KIOSK_URL\" > /etc/kiosk/kiosk.env",
|
||||||
|
"chmod 0644 /etc/kiosk/kiosk.env",
|
||||||
|
# Label the new files so they survive into the enforcing erofs.
|
||||||
|
"restorecon -R /usr/local/bin/kiosk-session /etc/systemd/system/kiosk.service /etc/kiosk",
|
||||||
|
# Boot graphical and start the kiosk (replaces the tty1 getty).
|
||||||
|
"systemctl enable kiosk.service",
|
||||||
|
"systemctl set-default graphical.target",
|
||||||
|
"rm -rf /tmp/kiosk",
|
||||||
|
"echo \"==> kiosk enabled: $KIOSK_URL\"",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
# --- 2c. Admin access: sudo-enabled key user + locked root --------------
|
# --- 2c. Admin access: sudo-enabled key user + locked root --------------
|
||||||
# Fleet-generic (same user + key on every host => NOT per-host state). The
|
# Fleet-generic (same user + key on every host => NOT per-host state). The
|
||||||
# key comes from var.ssh_authorized_key (CI secret). If no key is supplied we
|
# key comes from var.ssh_authorized_key (CI secret). If no key is supplied we
|
||||||
|
|||||||
+31
-1
@@ -81,7 +81,37 @@ re-export a **v3** PDF (key change: `rootovl` → `systemd.volatile=overlay` in
|
|||||||
step 8 and §5). Retire/reset the errata afterward. Needs the design-doc source
|
step 8 and §5). Retire/reset the errata afterward. Needs the design-doc source
|
||||||
(not in this repo).
|
(not in this repo).
|
||||||
|
|
||||||
## [ ] 4. Kiosk mode — autologin + fullscreen Chromium loading a URL
|
## [~] 4. Kiosk mode — autologin + fullscreen Chromium loading a URL
|
||||||
|
|
||||||
|
**First cut implemented (2026-06-16) — needs a display to verify.** `cage`
|
||||||
|
(Wayland single-app compositor) + Chromium from EPEL, baked into the root:
|
||||||
|
- `kiosk_url` Packer var (default `https://www.reddit.com`; empty disables the
|
||||||
|
whole install). Wired in CI as `KIOSK_URL` env.
|
||||||
|
- Provisioner enables EPEL, installs `cage chromium mesa-dri-drivers mesa-libEGL
|
||||||
|
mesa-libgbm dejavu-sans-fonts`, creates a passwordless unprivileged `kiosk`
|
||||||
|
user (video/input groups).
|
||||||
|
- `files/kiosk/kiosk.service` — runs `cage -s -- /usr/local/bin/kiosk-session`
|
||||||
|
as `kiosk` on tty1 via `PAMName=login`+`TTYPath` (gets seat0 for DRM/input),
|
||||||
|
`Conflicts=getty@tty1`, `WantedBy=graphical.target`; default target set to
|
||||||
|
graphical.
|
||||||
|
- `files/kiosk/kiosk-session` — Chromium with `--kiosk --ozone-platform=wayland
|
||||||
|
--disable-gpu` + tmpfs profile/cache; URL from `/etc/kiosk/kiosk.env`.
|
||||||
|
- `WLR_RENDERER=pixman` in kiosk.env for VMs without a GPU.
|
||||||
|
- **Verify on a VM with a console (Proxmox VNC/SPICE):** cage comes up on tty1,
|
||||||
|
Chromium loads the URL over the booted network. Likely tweak points:
|
||||||
|
`cage` availability in EPEL 9 (else fall back to `weston --shell=kiosk-shell`),
|
||||||
|
Chromium ozone/wayland support, dropping `--disable-gpu` on real GPUs, and
|
||||||
|
**SELinux AVCs** for the GUI stack (check `ausearch -m avc`; now that the image
|
||||||
|
is enforcing with labels, the kiosk may need a small policy module or booleans
|
||||||
|
— do NOT just set permissive). Admin SSH (the key user) still works for debug
|
||||||
|
even if the kiosk fails (it lands at graphical.target either way).
|
||||||
|
- **Image size:** Chromium+Mesa add hundreds of MB to the *uncompressed* erofs —
|
||||||
|
feeds the "shrink the rootfs" concern (and #7); reconsider whether a lighter
|
||||||
|
browser/render path is viable.
|
||||||
|
|
||||||
|
### Original task
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Make the diskless workstation boot straight into a locked-down kiosk: a
|
Make the diskless workstation boot straight into a locked-down kiosk: a
|
||||||
fullscreen Chromium showing a configured site (initial target: Reddit). All
|
fullscreen Chromium showing a configured site (initial target: Reddit). All
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# kiosk-session — launched by cage (Wayland) from kiosk.service.
|
||||||
|
# Starts Chromium fullscreen on the configured URL.
|
||||||
|
#
|
||||||
|
# KIOSK_URL comes from /etc/kiosk/kiosk.env (baked from the kiosk_url Packer
|
||||||
|
# var; a runtime config-agent could override it later). The Chromium profile is
|
||||||
|
# pinned to a tmpfs path so writes land in RAM (the root is read-only erofs +
|
||||||
|
# volatile overlay) — watch RAM if the page is heavy.
|
||||||
|
#
|
||||||
|
# VM/GPU notes: --disable-gpu + the pixman wlroots renderer (WLR_RENDERER in
|
||||||
|
# kiosk.env) keep this working on VMs with no real GPU. On hardware with a GPU,
|
||||||
|
# drop --disable-gpu (and set WLR_RENDERER=gles2) for compositing.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BROWSER=$(command -v chromium-browser || command -v chromium)
|
||||||
|
|
||||||
|
exec "$BROWSER" \
|
||||||
|
--kiosk --noerrdialogs --disable-infobars --incognito --no-first-run \
|
||||||
|
--disable-session-crashed-bubble --start-maximized \
|
||||||
|
--ozone-platform=wayland --disable-gpu \
|
||||||
|
--user-data-dir=/tmp/kiosk-profile --disk-cache-dir=/tmp/kiosk-cache \
|
||||||
|
"${KIOSK_URL:?KIOSK_URL not set in /etc/kiosk/kiosk.env}"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Wayland kiosk (cage + Chromium)
|
||||||
|
Documentation=https://github.com/cage-kiosk/cage
|
||||||
|
# Take over tty1 from the login getty and start after the network is up (the
|
||||||
|
# kiosk loads a URL). plymouth-quit lets the splash hand off cleanly.
|
||||||
|
After=systemd-user-sessions.service plymouth-quit-wait.service getty@tty1.service network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
Conflicts=getty@tty1.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=kiosk
|
||||||
|
# A real PAM login session on tty1 so logind assigns seat0 — cage/wlroots needs
|
||||||
|
# a seat for DRM master + libinput. (PAMName runs account/session, not auth, so
|
||||||
|
# the passwordless kiosk user is fine.)
|
||||||
|
PAMName=login
|
||||||
|
TTYPath=/dev/tty1
|
||||||
|
TTYReset=yes
|
||||||
|
TTYVHangup=yes
|
||||||
|
TTYVTDisallocate=yes
|
||||||
|
StandardInput=tty-fail
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
UtmpIdentifier=tty1
|
||||||
|
UtmpMode=user
|
||||||
|
EnvironmentFile=/etc/kiosk/kiosk.env
|
||||||
|
# cage -s: single-client kiosk compositor; runs the wrapper, exits when it does.
|
||||||
|
ExecStart=/usr/bin/cage -s -- /usr/local/bin/kiosk-session
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical.target
|
||||||
Reference in New Issue
Block a user