Vestibule/DEPLOYMENT.md

382 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Deployment — Vestibule
This is the operator runbook for turning a stock machine into a Vestibule
kiosk: OS-level lockdown provisioning and packaging, on both platforms,
with one-command deprovision.
```
What provisioning configures (Windows) What it configures (Linux)
──────────────────────────────────────── ──────────────────────────
AssignedAccess single-app kiosk (MDM bridge) cage Wayland session on a VT
└ step-down: Shell Launcher (Ent/Edu) └ /etc/systemd/system/
Start Menu shortcut with stable AUMID vestibule-kiosk.service
Dedicated kiosk local account /usr/local/bin/usher
Automatic logon (registry) /usr/local/bin/vestibule-kiosk-launch
C:\ProgramData\Vestibule\kiosk.env /etc/vestibule/kiosk.env
policies.json (extension force-installed) policies.json (same mechanism)
Per-user bootstrap at kiosk logon Native Messaging manifests for
(kiosk-launch.ps1: usher, HKCU host the kiosk user
registration, profile, crash restart) /opt/vestibule staging tree
```
Everything the scripts do is reversible — see
[Deprovision](#deprovision) for the one-command exit on each platform.
**Platform priority: Linux.** The Linux path is the supported
production target — provisioned, packaged, and installed with no
signing gate. The Windows path is complete and CI-validated but ships
unsigned: a code-signing certificate is an unfunded line item and
stays that way unless a donation earmarks it (info@dcos.net), so
SmartScreen warns on the installer and the usher binary. Windows
operators verify the SHA-256 from the release notes; Linux operators
install and go.
---
## Choosing a path
| Situation | Command |
|---|---|
| Linux kiosk, native LibreWolf | `sudo scripts/provision-kiosk.sh --librewolf native` |
| Linux kiosk, Flatpak LibreWolf | `sudo scripts/provision-kiosk.sh --librewolf flatpak` |
| Linux kiosk, native Firefox | `sudo scripts/provision-kiosk.sh --firefox native` |
| Linux kiosk, Flatpak Firefox | `sudo scripts/provision-kiosk.sh --firefox flatpak` |
| Windows kiosk, interactive | Run the installer, tick the wizard checkbox — or `provision-kiosk.ps1` standalone |
| Windows kiosk, unattended (MDM/CI) | `Vestibule-Setup-1.2.2.exe /VERYSILENT` then `provision-kiosk.ps1 -Quiet` (exit codes below) |
| Just want to see what it would do | `--check` on either platform |
## Browser support
Vestibule runs on two Gecko browsers. The extension, usher, and
lockdown policies are identical for both — only the deployment paths
differ.
| Browser / flavor | Windows | Linux native | Linux Flatpak | Linux snap |
|---|---|---|---|---|
| LibreWolf | Program Files install, `distribution\policies.json` | install-dir `distribution/` | system Flatpak (policies in app dir) | — |
| Firefox / ESR | Program Files install, `distribution\policies.json` | distro package → `/etc/firefox/policies` | system Flatpak (policies + XPI in kiosk home) | Ubuntu snap (policies + XPI in kiosk home) |
Notes:
- **Auto-detect order**: LibreWolf native → LibreWolf Flatpak → Firefox
native → Firefox Flatpak → Firefox snap. Override with
`--librewolf`/`--firefox` (Linux) or `-Browser` (Windows).
- **Firefox ESR is recommended** for kiosks — slower release cadence,
same enterprise-policy engine.
- **Flatpak/snap Firefox keep policies and the XPI in the kiosk user's
home** (`~/.var/app/...` / `~/snap/firefox/common`), so they survive
browser updates — unlike LibreWolf's system-Flatpak deploy.
- **Trademark**: Vestibule configures an existing Firefox install; it
never downloads or redistributes Firefox. (The original choice of
LibreWolf as the default base was about redistribution — deploying
against an installed Firefox has no trademark exposure.)
- `librewolf.overrides.cfg` is LibreWolf-only; on Firefox it is skipped
with a notice (Firefox ignores it safely anyway).
---
## Windows
### Prerequisites
- Windows 10/11 **Pro, Enterprise, or Education** (Home has no
AssignedAccess/Shell Launcher — the script fails fast with exit 2)
- LibreWolf **or Firefox/ESR** installed (`C:\Program Files\...` —
auto-detected; override with `-Browser librewolf|firefox`)
- The Vestibule installer run (or a repo checkout with a built usher:
`cd helper; cargo build --release`)
- PowerShell run **as administrator**
### Interactive provisioning
```powershell
powershell -ExecutionPolicy Bypass -File scripts\provision-kiosk.ps1
```
The wizard prompts for the home URL and generates the kiosk account
password. Everything else is automatic: staging, XPI build, policies
merge, shortcut + AUMID, AssignedAccess, autologon. Exit code 10 means
"reboot to activate".
### Unattended provisioning
```powershell
powershell -ExecutionPolicy Bypass -File scripts\provision-kiosk.ps1 `
-KioskUser Kiosk `
-KioskPassword '<from-your-secrets-store>' `
-HomeUrl https://checkin.example.org `
-Quiet -Restart
```
Parameters:
| Parameter | Default | Meaning |
|---|---|---|
| `-Browser` | `auto` | `librewolf` / `firefox` / `auto` (LibreWolf preferred) |
| `-KioskUser` | `VestibuleKiosk` | Dedicated local account (created or reused) |
| `-KioskPassword` | generated + printed | Account password (also used for autologon) |
| `-HomeUrl` | `about:blank` | Page the kiosk session opens |
| `-InstallRoot` | detected | Staged install location |
| `-Quiet` | off | No prompts — for MDM/CI rollout |
| `-Check` | off | Validate prerequisites only, change nothing |
| `-ShellLauncher` | off | Force Shell Launcher instead of the AssignedAccess bridge |
| `-NoAutoLogon` | off | Skip autologon (kiosk starts after manual logon) |
| `-Restart` | off | Reboot automatically when required |
| `-InstallOverridesCfg` | off | Also deploy `librewolf.overrides.cfg` (SSO/telehealth) |
Exit codes (also used by CI):
| Code | Meaning |
|---|---|
| 0 | Success, no reboot needed |
| 10 | Success — reboot required to activate |
| 2 | Unsupported (Home edition / not elevated / not Windows) |
| 3 | Prerequisite missing (LibreWolf, usher) |
| 4 | Kiosk account error |
| 5 | Lockdown apply failed (AssignedAccess AND Shell Launcher) |
| 6 | Invalid parameters |
### How the lockdown is applied
1. **AssignedAccess via the MDM WMI bridge** (`MDM_AssignedAccess`
`SetSingleAppKiosk`) — the supported scriptable path on Pro and
higher. The kiosk app is a Start Menu shortcut carrying a stable
AppUserModelID (`Vestibule.Kiosk`), verified through `Get-StartApps`
before the XML is applied.
2. **Shell Launcher step-down** (`WESL_UserSetting.SetCustomShell`) on
Enterprise/Education when the bridge rejects the config. The feature
is enabled on demand; that path needs one extra reboot.
3. **kiosk-launch.ps1** is the actual shell process. It runs as the
kiosk user (no admin): installs usher per-user, registers the Native
Messaging host under HKCU, creates `vestibule-profile`, then launches
`librewolf --kiosk -P vestibule-profile <home-url>` and relaunches it
if it exits. Log: `%LOCALAPPDATA%\Vestibule\kiosk-launch.log`.
### Verification checklist
- [ ] Reboot → machine logs in as the kiosk account automatically
- [ ] LibreWolf opens full-screen on the home URL, no tabs/URL bar
- [ ] Browser Console (`Ctrl+Shift+J`) shows `[vestibule] usher hello: usher 1.2.2`
- [ ] Any domain not on the safelist shows the Vestibule block page (default policy; the home URL's domain is always permitted)
- [ ] Idle 5 min → session resets to home URL, data cleared
### Admin escape hatches
- **Ctrl+Alt+Del** still works under AssignedAccess — Sign out / switch
user to reach an admin account.
- **Hold Shift during boot** to bypass automatic logon once.
- Kiosk account has no interactive way to change its password (locked
via `UserMayNotChangePassword`).
### Building the installer
```powershell
cd helper; cargo build --release; cd ..
powershell -ExecutionPolicy Bypass -File packaging\build-installer.ps1
```
Requires Inno Setup 6 (`choco install innosetup -y`). Output:
`packaging\Output\Vestibule-Setup-1.2.2.exe`. CI builds it on every push
(see [CI artifacts](#ci-artifacts)).
---
## Linux
### Prerequisites
- Any systemd distribution
- **cage** (the Wayland kiosk compositor) — Debian 12+/Ubuntu 24.04+/
Fedora/Arch packages it; elsewhere build from
[cage-kiosk/cage](https://github.com/cage-kiosk/cage)
- **LibreWolf or Firefox** (ESR recommended) — native package
([LibreWolf install docs](https://librewolf.net/installation/linux/),
distro Firefox, system-wide Flatpak, or the Ubuntu snap)
- `python3` (policies merge + XPI build), `dbus` (session bus for cage
children)
- A built usher: `cd helper && cargo build --release`
The script never auto-installs anything: if something is missing it
prints the exact per-distro commands and exits 3.
### Interactive provisioning
```sh
sudo ./scripts/provision-kiosk.sh
```
### Unattended provisioning
```sh
sudo ./scripts/provision-kiosk.sh \
--home-url https://checkin.example.org \
--kiosk-user kiosk \
--tty 2 \
--librewolf native \
--yes --start
```
Parameters:
| Parameter | Default | Meaning |
|---|---|---|
| `--home-url URL` | `about:blank` | Page the kiosk session opens |
| `--kiosk-user NAME` | `vestibule-kiosk` | Account (created with locked password) |
| `--tty N` | `2` | VT for the cage session (1–12) |
| `--librewolf FLAVOR` | auto | Use LibreWolf: `native` / `flatpak` |
| `--firefox FLAVOR` | auto | Use Firefox: `native` / `flatpak` / `snap` (mutually exclusive with `--librewolf`) |
| `--usher-bin PATH` | auto | Explicit usher binary |
| `--start` | off | Start the session immediately (otherwise: enable only) |
| `--yes` | off | Skip confirmation |
| `--check` | off | Validate prerequisites only |
Exit codes: 0 success · 2 not root / no systemd · 3 prerequisite missing
· 4 account error · 5 unit failure · 6 bad parameters.
### How the lockdown is applied
- `vestibule-kiosk.service` is a **system unit that IS the graphical
session**: cage starts on the chosen VT at boot as the kiosk user via
`PAMName=login` (runtime dir from pam_systemd), no display manager
involved, `Restart=always` for crash recovery.
- `/usr/local/bin/vestibule-kiosk-launch` execs
`dbus-run-session -- cage -d -- librewolf --kiosk -P vestibule-profile
<url>` (or `flatpak run io.gitlab.librewolf-community …` for the
Flatpak flavor). `MOZ_ENABLE_WAYLAND=1` is mandatory — Gecko defaults
to X11 and cage ships no Xwayland.
- The kiosk account is created with a **locked password**: it can never
be logged into interactively, only entered via the systemd session.
- policies.json is deep-merged into LibreWolf's `distribution/` dir
(existing file backed up to `policies.json.vestibule-bak`) and
force-installs the extension XPI on every browser start — no
about:debugging step on the kiosk.
Reconfigure at any time by editing `/etc/vestibule/kiosk.env` (home URL,
browser + flavor, cage flags) — the unit re-reads it on every start.
### Where policies land per browser (Linux)
| Flavor | policies.json | XPI | Update-safe? |
|---|---|---|---|
| LibreWolf native | `<install>/distribution/` | `/opt/vestibule/extension/` | yes |
| LibreWolf Flatpak | Flatpak app dir `files/librewolf/distribution/` | kiosk home `~/.var/app/<id>/` | no — re-run after updates |
| Firefox native | `/etc/firefox/policies/` | `/opt/vestibule/extension/` | yes |
| Firefox Flatpak | kiosk home `~/.var/app/org.mozilla.firefox/.mozilla/policies/` | kiosk home | yes |
| Firefox snap | kiosk home `~/snap/firefox/common/.mozilla/policies/` | kiosk home | yes |
For Flatpak/snap flavors the browser's filesystem is the kiosk home
remap — that is why the XPI is copied there and `install_url` points
inside the sandbox-visible home rather than `/opt/vestibule`.
### Verification checklist
- [ ] `systemctl status vestibule-kiosk` — active (running)
- [ ] The VT shows LibreWolf full-screen on the home URL
- [ ] `journalctl -u vestibule-kiosk -f` shows the launcher + cage
- [ ] Ctrl+Alt+F3 switches to a text VT (`cage -d`); Ctrl+Alt+F2 returns
- [ ] `sudo systemctl restart vestibule-kiosk` recovers from a killed
browser within seconds
### Flatpak LibreWolf notes (honest limitations)
1. **Updates wipe the policy layer.** The policies live inside
`/var/lib/flatpak/app/io.gitlab.librewolf-community/…`, which is
replaced on every update. Re-run `provision-kiosk.sh` afterwards.
The extension's own session sanitization (layers 2–3) is unaffected.
2. **Native Messaging under the Flatpak depends on the flatpak's host
visibility.** Manifests are written to all five conventional
locations for the kiosk user; if the flatpak ignores them, unlock
falls back to "not configured" while URL policy and session resets
keep working. Native LibreWolf (or Firefox native) has no such
caveat.
The Firefox Flatpak and snap do not share limitation 1: their policies
and XPI live in the kiosk user's home, which updates never touch.
### Building the Flatpak
```sh
./packaging/build-flatpak.sh
```
Output: `packaging/Vestibule-1.2.2.flatpak` (requires flatpak-builder;
first run downloads the Freedesktop 24.08 SDK + Rust extension). The
Flatpak carries the whole deployment kit — `flatpak run
net.dcos.Vestibule provision` prints the exact host-side command.
---
## Deprovision
Both deprovision scripts reset the machine to its pre-Vestibule state.
Each policy directory is returned to its baseline: the operator's backed-up
policies.json is reinstalled, or Vestibule's generated file is deleted where
no baseline exists (byte-for-byte equality is asserted by the test suite).
```powershell
# Windows — remove lockdown, autologon, shortcut, policies.
# Optional: -RemoveKioskAccount -RemoveInstall -Restart
powershell -ExecutionPolicy Bypass -File scripts\deprovision-kiosk.ps1
```
```sh
# Linux — stop/disable/remove the unit, launcher, env, policies, manifests.
# Optional: --remove-user --remove-opt --remove-usher
sudo ./scripts/deprovision-kiosk.sh
```
The Windows uninstaller (Add/Remove Programs) offers to run the
deprovision step automatically.
---
## Security notes operators must read
1. **Windows autologon stores the kiosk password in plaintext registry**
(`Winlogon \ DefaultPassword`). On a locked-down single-purpose
appliance this is an accepted trade-off — the account is the least
privileged thing on the machine and the browser is the only shell.
Use `-NoAutoLogon` and manual logon if your threat model disagrees.
2. **The installer and usher binary are unsigned — a standing decision,
not an oversight.** A code-signing certificate costs $200–500/year
and stays an unfunded line item unless a donation earmarks it
(info@dcos.net). SmartScreen will warn; verify the SHA-256 from the
release notes. This is why Linux is the primary target — it has no
signing gate. See README "Honest limitations".
3. **The extension is policy-installed from disk** (force_installed).
That is deliberate: kiosks have no AMO session, and the policy
re-installs the XPI on every start if it is removed.
4. **Linux kiosk account password stays locked.** There is nothing to
brute-force; the session is entered only via systemd.
---
## Troubleshooting
| Symptom | Platform | Fix |
|---|---|---|
| Black screen after enabling cage | Linux | `MOZ_ENABLE_WAYLAND=1` missing (our launcher sets it); check `journalctl -u vestibule-kiosk` |
| cage fails in a VM | Linux | No DRM: add `WLR_LIBINPUT_NO_DEVICES=1` and `WLR_RENDERER=pixman` to the unit (`systemctl edit vestibule-kiosk`) |
| Flatpak LibreWolf won't start under cage | Linux | Missing session bus — the launcher wraps everything in `dbus-run-session`; check flatpak is installed system-wide, not per-user |
| Snap Firefox detected but policies ignored | Linux | Verify `~/snap/firefox/common/.mozilla/policies/policies.json` exists for the **kiosk user** (not your own); re-run provision with `--firefox snap` |
| AssignedAccess applies but kiosk shows black/Start | Windows | AUMID didn't resolve — check `Get-StartApps \| ? AppID -eq Vestibule.Kiosk`; re-run provisioning after a reboot |
| `[vestibule] usher hello` missing | both | Per-user bootstrap failed: read `%LOCALAPPDATA%\Vestibule\kiosk-launch.log` (Windows) or check NM manifests in the kiosk home (Linux) |
| AssignedAccess XML rejected (exit 5) | Windows | Use `-ShellLauncher` on Enterprise/Education, or apply via Settings → Accounts → Other users → Set up kiosk |
| Policies not applied | both | policies.json needs a full browser restart; verify it parses and that the distribution dir matches the running LibreWolf |
| exit 3 on `--check` | both | Prerequisite missing — the script prints the exact install commands |
---
## CI artifacts
Every push to `main` builds (`.github/workflows/ci.yml`):
| Artifact | Job | Contents |
|---|---|---|
| `vestibule-setup-windows` | package-windows | `Vestibule-Setup-1.2.2.exe` (Inno Setup), the extension XPI |
| `vestibule-flatpak-linux` | package-linux | `Vestibule-1.2.2.flatpak` (Freedesktop 24.08) |
| `vestibule-usher-linux` | package-linux | The Linux usher binary from the same commit |
Download from the workflow run page. Tag a release to attach them to a
GitHub Release.