Upgrade folder swap drops the script-worker isolation ACLs; deploy must re-apply and verify them #901
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on the vd03 upgrade
fea7de0a→6c4d2990(2026-11-13, recorddocs/deploy/2026-11-13-vd03-fea7de0a-to-6c4d2990.md,9375c9e2).What happened. The deploy swapped the install folder (rename the old one, move the new build into place). That dropped three of the #794/#890/#891 worker-isolation permissions: the ones on
script-worker\, on the install folder and onpki\. These are set on the folders themselves, and a freshly moved-in folder doesn't carry them. Re-running the isolation installer with the recorded arguments restored them; a second run reported every step unchanged.Why it matters. Every OtOpcUa upgrade on a box with isolation installed silently weakens it: the QA pair vq03/vq04 and vd03. On QA the out-of-process runner is live, so after a swap the worker token can read paths it is meant to be denied, until someone notices.
To do.
docs/deploy/packs and the vd03/QA runbooks.Fixed on master in
2cb8a2c3(code + docs only; nothing deployed, nothing changed on vd03 or QA).What changed
Install-ScriptWorkerIsolation.ps1now records its effective settings (InstallRoot,HostServiceAccount,PeCacheDir,HostServiceName,ConfigureHostService) besideDenyPathsunderHKLM\SYSTEM\CurrentControlSet\Services\OtOpcUaScriptWorker\Parameters(new stepSettingsRecord), and sets that key's DACL protected, SYSTEM + Administrators only. No secrets are recorded.-Reapply: re-applies from the record with no other arguments and printssummary: N step(s), X changed, Y unchanged.-Verify: check only. Prints[ok]/[MISSING]per step (anchor service, record + key ACL, PE cache ACL,pe\,script-worker\, install-folder deny, every-DenyPathsdeny, the firewall rule and the worker exe it names, and the host env entries when they are recorded). Exits 1 withVERIFY FAILED: …when anything is missing.-Uninstallalso works from the record now.scripts/install/Invoke-OtOpcUaInstallSwap.ps1runs preflight → stop → move live/candidate → carryappsettings*.json/pki/logs(QA:'appsettings*.json','logs') →-Reapply→-Verify→ start. If-Verifyfails, the service stays stopped. With-NoCarryit is also the rollback, and-NoStartcovers the QA pair window. It refuses to run, before changing anything, on a node whose isolation was installed by a pre-#901 installer, which recorded onlyDenyPaths.docs/ServiceHosting.md(isolation section), the vd03 pack (§e swap + rollback, §f post-deploy gate, one-time step), the 2026-11-13 vd03 record, and the QA pack (new section "Worker isolation across the next swap").Tests
Test-ScriptWorkerIsolation.Windows.ps1on windev: 52/52 under Windows PowerShell 5.1 and under PowerShell 7, run once with a scratch local host account and once with SYSTEM. Scratch tree, scratch services; everything was cleaned up afterwards. Transcript (5.1):Evidence:
scripts/validation/opcua-windows-service/evidence/2026-11-13-windev-901-reapply-verify/RESULTS.md.QA: was the 2026-11-12 swap before or after the isolation install? Before. The window swapped
app\at 16:48. The #794 isolation went onto the newapp\after that (window step 3), and the #890/#891 re-applies came later that evening with no further swap. Checked 2026-11-13, read-only over SMB from the Mac (one DACL query per folder, no remote exec). vq03 and vq04 are identical:E:\ApiInstall\OtOpcUa,…\app,…\pki,…\data,C:\ProgramData\ZB\kek,E:\ApiInstall\ScadaBridgeandE:\psrem-stageeach carry the explicit worker Deny0xD0157(OI)(CI).…\app\script-workerhas its explicit Allow0x1200A9;script-pe-cache\pehas its explicit Modify0x1301BF.C:\Windowsand the PE-cache root show no worker ACE.vd03 also reads intact after the 2026-11-13 manual re-apply. The anchor service, the firewall rule and the env entries can't be seen over SMB;
-Verifychecks them at the next deploy.What the next deploy must do (vd03 and QA)
reg query …\OtOpcUaScriptWorker\Parameters /v DenyPathsfirst. ExpectSettingsRecordto show changed and every other step unchanged, then-Verify→VERIFY OK.Invoke-OtOpcUaInstallSwap.ps1. If you swap by hand instead, run-Reapplyand then-Verifybefore the start.Install-ScriptWorkerIsolation.ps1 -Verify→ exit 0, and record the output.-NoCarry).Deployed to DEV vd03 + QA vq03/vq04 in
c9640f5con 2026-11-13.SettingsRecordchanged, every other step unchanged (QA: plus a reorder-onlyHostServiceEnv; sorted env SHA unchanged). DenyPaths unchanged (vd03 5, QA 12 incl.E:\ApiInstall\OtelCollector).Invoke-OtOpcUaInstallSwap.ps1:-Reapplychanged exactlyWorkerFolderAcl+InstallRootDeny(+SensitiveDirDenypkion vd03), then the gate:VERIFY OK: 13/13 step(s) in place.after the swap and again after the start (exit 0).-NoStart):VERIFY OK: 21/21 step(s) in place.after each swap, after each start, and after the failover restart (exit 0).-Reapply/-Verifyruns with the service stopped. On vd03 (deny on all ofE:\ApiInstall, 48 WARN items) that made the stop→ready window ~101 s; ~50–60 s per QA node.Record:
docs/deploy/2026-11-13-vd03-qa-to-c9640f5c.md(c0ba450d).