fix(kiosk): pivot to Explorer + policy lockdown (WebView2 wizard renders blank as the SL shell)
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 7m31s

5th VM e2e: with the kiosk fully working mechanically (SL engages, silent UAC,
app launches fullscreen as the shell), the MAUI/WebView2 wizard STILL renders
blank — WebView2 never initializes when the app is the bare Shell Launcher shell
with no Explorer (the same app rendered fine in the earlier build launched with
Explorer present). Operator decision: pivot.

- autounattend.xml: restore FirstLogonCommands to launch the wizard elevated over
  the normal (Explorer) first-logon session — where WebView2 works.
- Configure-Kiosk.ps1: drop Shell-Launcher-as-shell entirely; keep the lockdown —
  Keyboard Filter (Win/Start/lock/task-switch/Task-Mgr/Alt+F4), DisableTaskMgr /
  LockWorkstation / FastUserSwitch, and silent-elevation UAC. The wizard runs
  fullscreen-topmost over the locked-down Explorer (covers the taskbar).
- RevertKioskAsync: disable the Keyboard Filter rules for the real user (no SL to
  undo); keep escape-policy + secure-UAC restore. Tests updated.

Keeps the diagnostics from #10 (welcome.log) to confirm the wizard renders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sysadmin
2026-06-09 18:52:15 +01:00
parent d54a5cb8db
commit e3b010530c
4 changed files with 50 additions and 87 deletions

View File

@@ -1,88 +1,46 @@
#Requires -Version 5.1
<#
.SYNOPSIS Configure the one-time sm-bootstrap onboarding kiosk.
.SYNOPSIS Lock down the one-time sm-bootstrap onboarding session.
.DESCRIPTION
Runs from SetupComplete.cmd as SYSTEM, after accounts exist, before first
logon. Sets the sm-bootstrap shell to an elevating launcher for the Welcome
app (no Explorer => no taskbar/Start), turns on the Keyboard Filter for shell
hotkeys, and disables Task Manager / lock / fast-user-switch escapes.
Reverted by the Welcome app's ApplyService on wizard success.
Runs from SetupComplete.cmd as SYSTEM, after accounts exist, before first logon.
Explorer stays the session shell so the MAUI/WebView2 Welcome wizard RENDERS
(it does not render when launched as a bare Shell Launcher shell with no
Explorer). The wizard is launched fullscreen-topmost by autounattend
FirstLogonCommands; this script applies the lockdown around it:
- Keyboard Filter: block Win/Start, lock, task-switch and Task-Manager hotkeys
- DisableTaskMgr / DisableLockWorkstation / HideFastUserSwitching
- silent-elevation UAC policy (so the unsigned wizard elevates with no prompt)
All reverted by the Welcome app's ApplyService on wizard success, so the real
end-user gets a normal, secure desktop.
#>
[CmdletBinding()]
param([string]$BootstrapUser='sm-bootstrap',
[string]$WelcomeExe='C:\Program Files\SilverOS\Welcome\SilverOS.Welcome.App.exe')
param([string]$BootstrapUser='sm-bootstrap')
Set-StrictMode -Version Latest
$ErrorActionPreference='Stop'
$log='C:\Windows\Setup\Scripts\silvermetal-kiosk.log'
function Log($m){ "$(Get-Date -f s) $m" | Add-Content $log }
Log 'configuring onboarding lockdown (Explorer shell + policy)'
# Elevating launcher: Shell Launcher runs this as the shell; it relaunches the
# Welcome app elevated (silent via the baked UAC auto-approve).
$launcher='C:\Windows\Setup\Scripts\Start-WelcomeShell.cmd'
$welcomeEscaped = $WelcomeExe.Replace("'","''")
@"
@echo off
powershell -NoProfile -ExecutionPolicy Bypass -Command "Start-Process -FilePath '$welcomeEscaped' -Verb RunAs"
REM Shell Launcher tracks this CMD process; the Welcome app runs detached above.
REM Loop keeps the process alive so Shell Launcher doesn't restart it on idle.
:loop
timeout /t 3600 >nul
goto loop
"@ | Set-Content $launcher -Encoding ASCII
Log "wrote launcher $launcher"
# --- Shell Launcher v2 (WMI bridge) ---
# WESL_UserSetting exposes STATIC methods on the CLASS — Get-CimInstance returns
# no instance, so every method must be called class-level (-Namespace/-ClassName),
# NOT via -InputObject on a (null) instance. Getting this wrong enables Shell
# Launcher with NO shell configured, which bricks EVERY logon (incl. OOBE's
# defaultuser0) into a reboot loop. So: set the DEFAULT shell to explorer.exe for
# all users first (this is what keeps OOBE/normal logons working), set the
# sm-bootstrap custom shell, and roll back SetEnabled(false) + fall back to a
# RunOnce launch if anything fails — never leave SL enabled-but-unconfigured.
$cls='root\standardcimv2\embedded'
$sid=(New-Object System.Security.Principal.NTAccount($BootstrapUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value
try {
Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$true} | Out-Null
# Default shell = Explorer for everyone else (incl. OOBE) — critical so non-kiosk logons don't break.
Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetDefaultShell -Arguments @{Shell='explorer.exe';DefaultAction=[int32]0} | Out-Null
# sm-bootstrap => the elevating launcher; on exit, restart the shell (action 0).
Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetCustomShell -Arguments @{
Sid=$sid; Shell="cmd.exe /c `"$launcher`""; DefaultAction=[int32]0 } | Out-Null
$set=Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName GetCustomShell -Arguments @{Sid=$sid} -ErrorAction SilentlyContinue
if (-not $set -or [string]::IsNullOrEmpty($set.Shell)) { throw "custom shell did not take for $BootstrapUser" }
Log "shell launcher configured for sm-bootstrap (shell=$($set.Shell))"
}
catch {
Log "SHELL LAUNCHER CONFIG FAILED: $($_.Exception.Message) -- rolling back (SetEnabled false) + RunOnce fallback so onboarding still launches"
Invoke-CimMethod -Namespace $cls -ClassName WESL_UserSetting -MethodName SetEnabled -Arguments @{Enabled=$false} -ErrorAction SilentlyContinue | Out-Null
# Fail-OPEN: no kiosk, but the Welcome wizard must still launch (we removed FirstLogonCommands).
$ro='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce'
New-Item $ro -Force | Out-Null
Set-ItemProperty $ro -Name 'SilverOSWelcome' -Type String -Value "cmd /c powershell -NoProfile -ExecutionPolicy Bypass -Command `"Start-Process -FilePath '$welcomeEscaped' -Verb RunAs`""
}
# --- Keyboard Filter (block shell hotkeys) ---
# --- Keyboard Filter: block shell/escape hotkeys for the locked-down session ---
Enable-WindowsOptionalFeature -Online -FeatureName Client-KeyboardFilter -NoRestart -ErrorAction SilentlyContinue | Out-Null
$kf='root\standardcimv2\embedded'
foreach($combo in 'Win','Win+L','Ctrl+Esc','Ctrl+Win+F','Win+R'){
foreach($combo in 'Win','Win+L','Ctrl+Esc','Ctrl+Win+F','Win+R','Alt+Tab','Ctrl+Shift+Esc','Alt+F4'){
$p=Get-CimInstance -Namespace $kf -ClassName WEKF_PredefinedKey -Filter "Id='$combo'" -ErrorAction SilentlyContinue
if($p){ $p.Enabled=$true; Set-CimInstance -InputObject $p }
if($p){ $p.Enabled=$true; Set-CimInstance -InputObject $p -ErrorAction SilentlyContinue }
}
Log 'keyboard filter rules enabled'
# --- escape policies (machine-wide; reverted at teardown) ---
$sys='HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
New-Item $sys -Force | Out-Null
Set-ItemProperty $sys -Name DisableTaskMgr -Value 1 -Type DWord
Set-ItemProperty $sys -Name DisableTaskMgr -Value 1 -Type DWord
Set-ItemProperty $sys -Name DisableLockWorkstation -Value 1 -Type DWord
Set-ItemProperty $sys -Name HideFastUserSwitching -Value 1 -Type DWord
# Silent elevation for the sm-bootstrap launcher's 'Start-Process -Verb RunAs':
# the offline-baked UAC auto-approve (build.ps1) is RESET by Windows during OOBE,
# so re-assert it online here (runs before the autologon shell). Otherwise the
# kiosk shows a UAC consent prompt for the (unsigned) Welcome app. Reverted at
# teardown so the real end-user keeps normal UAC.
# Silent elevation for the FirstLogonCommands 'Start-Process -Verb RunAs' launch:
# the offline-baked UAC auto-approve is RESET by Windows during OOBE, so re-assert
# it online here (before the autologon). Otherwise a UAC consent prompt appears for
# the unsigned Welcome app. Restored to SECURE UAC at teardown for the real user.
Set-ItemProperty $sys -Name ConsentPromptBehaviorAdmin -Value 0 -Type DWord
Set-ItemProperty $sys -Name PromptOnSecureDesktop -Value 0 -Type DWord
Log 'escape policies + UAC auto-approve set; kiosk ready'
Log 'escape policies + UAC auto-approve set; lockdown ready'