fix(install): worker isolation survives a folder swap: recorded settings, -Reapply, -Verify, swap script (#901)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
An upgrade that swaps the install folder dropped the explicit ScriptWorker isolation ACEs on script-worker\, the install folder and a copied-back pki\ (vd03 2026-11-13). Re-applying needed the operator to repeat the arguments. - Install-ScriptWorkerIsolation.ps1 records its effective settings (InstallRoot, HostServiceAccount, PeCacheDir, HostServiceName, ConfigureHostService) beside DenyPaths under ...\Services\OtOpcUaScriptWorker\Parameters (new plan step SettingsRecord) and locks that key to SYSTEM + Administrators (protected DACL; no secrets recorded). - -Reapply: re-apply from the record, no arguments; prints a summary line (changed/unchanged). - -Verify: check only, [ok]/[MISSING] per step, exit 1 when anything is missing. -Uninstall also works from the record. A node isolated by a pre-#901 installer (DenyPaths only) must run the full-argument install once; both modes say so. - Invoke-OtOpcUaInstallSwap.ps1: preflight -> stop -> move live/candidate -> carry appsettings/pki/logs -> -Reapply -> -Verify -> start; a failed -Verify leaves the service stopped. Also the rollback (-NoCarry). - Tests: dry-run harness 101/101 (record + argument rules; record-key SDDL mutation turns 2 red). The Windows test adds swap -> Verify fails (exactly the 3 lost ACEs) -> Reapply (3 changed) -> Verify ok -> Reapply 12/12 unchanged, PE-cache and firewall negative controls, the swap script, and the pre-#901 refusal; 52/52 on windev under 5.1 and 7, with a scratch local host account and with SYSTEM. Evidence: scripts/validation/opcua-windows-service/evidence/2026-11-13-windev-901-reapply-verify. - Docs: ServiceHosting isolation section; vd03 pack (swap step, rollback, post-deploy gate, one-time full-argument run), the 2026-11-13 vd03 record, and the QA pack (QA swap preceded the isolation install; ACLs checked intact over SMB on vq03/vq04 2026-11-13; next-deploy procedure). Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
+50
@@ -0,0 +1,50 @@
|
||||
# lmxopcua#901: re-apply and verify the ScriptWorker isolation after a folder swap (windev, 2026-11-13)
|
||||
|
||||
**Result: PASS on both PowerShell hosts.** `scripts/install/tests/Test-ScriptWorkerIsolation.Windows.ps1` (elevated, scratch
|
||||
folder tree under `%TEMP%`, scratch worker/host service names, never touches `OtOpcUaHost`; windev has no OtOpcUa service and
|
||||
no real isolation installed):
|
||||
|
||||
| Host | Host account | Assertions |
|
||||
|---|---|---|
|
||||
| Windows PowerShell 5.1.19041.6456 | scratch local user `.\otopcuaIsoTestAcct` (`-LocalHostAccount`, random password, deleted) | 52 passed, 0 failed |
|
||||
| PowerShell 7 | scratch local user (`-LocalHostAccount`) | 52 passed, 0 failed |
|
||||
| Windows PowerShell 5.1 | `NT AUTHORITY\SYSTEM` | 52 passed, 0 failed |
|
||||
|
||||
The scratch layout mirrors vd03: `pki\` inside the install folder and in `-DenyPaths`, the deployment folder around it denied
|
||||
too. The "swap" is the deploy packs' sequence: build the candidate beside the live folder, move live aside, move the
|
||||
candidate in, robocopy `pki\` (no ACLs) and copy `appsettings.json` back.
|
||||
|
||||
Transcript of the #901 part (Windows PowerShell 5.1, SYSTEM; the scratch path is shown as `<scratch>`):
|
||||
|
||||
```
|
||||
Swap -> Verify fails -> Reapply -> Verify passes -> Reapply unchanged (lmxopcua#901)
|
||||
Verify (installed) exit 0; 12 ok
|
||||
Verify (after the swap) exit 1; MISSING: WorkerFolderAcl <scratch>\install\script-worker | InstallRootDeny <scratch>\install | SensitiveDirDeny <scratch>\install\pki
|
||||
Reapply (no arguments) exit 0; 9 unchanged, 3 changed
|
||||
Verify (after Reapply) exit 0; 12 ok
|
||||
Reapply again exit 0; 12 unchanged
|
||||
Negative control: Verify catches each kind of loss
|
||||
Verify (cache ACL + rule broken) exit 1; MISSING: PeCacheDir <scratch>\pe-cache | FirewallBlockOutbound OtOpcUa-ScriptWorker-BlockOutbound
|
||||
Verify (after Reapply) exit 0; 12 ok
|
||||
Invoke-OtOpcUaInstallSwap.ps1: swap + Reapply + Verify in one step (no service control in the scratch test)
|
||||
Invoke-OtOpcUaInstallSwap exit 0; VERIFY OK after its Reapply
|
||||
Swap refuses a pre-#901 record (DenyPaths only) before changing anything
|
||||
Invoke-OtOpcUaInstallSwap (pre-#901) exit 1; refused: settings not recorded
|
||||
Reapply (pre-#901 record) exit 1;
|
||||
Uninstall WITHOUT -DenyPaths (the record drives it), twice
|
||||
```
|
||||
|
||||
- After the swap `-Verify` exits **1** and lists exactly the three ACEs the 2026-11-13 vd03 swap lost: `WorkerFolderAcl`
|
||||
(`script-worker\`), `InstallRootDeny` (install folder), `SensitiveDirDeny` (`pki\`).
|
||||
- `-Reapply` with no settings argument changes exactly those three (9 unchanged); `-Verify` then exits 0; a second `-Reapply`
|
||||
reports 12/12 unchanged (`summary: 12 step(s), 0 changed, 12 unchanged`).
|
||||
- Negative controls: a Users grant on the PE-cache root and a deleted firewall rule each show as `[MISSING]` (exit 1).
|
||||
- `Invoke-OtOpcUaInstallSwap.ps1` (`-NoServiceControl`) swaps, carries `appsettings*.json` + `pki`, re-applies and verifies in
|
||||
one run (exit 0, `VERIFY OK`); with a pre-#901 record (no `InstallRoot` value) it refuses before moving anything (exit 1,
|
||||
candidate still in place, no previous-install folder), and `-Reapply` refuses with the "run the full-argument install once" message.
|
||||
- The record key's DACL is `D:PAI(A;CI;KA;;;SY)(A;CI;KA;;;BA)`; the five settings read back.
|
||||
- Cleanup verified afterwards: no firewall rule, no scratch services, no scratch local user, no scratch folder, no scratch
|
||||
registry key; the staged scripts were removed from windev.
|
||||
|
||||
The dry-run harness (`pwsh -File scripts/install/tests/Test-ScriptWorkerIsolation.ps1`, macOS) passes 101/101; a mutation of the
|
||||
record-key SDDL (unprotected + a Users read ACE) turns 2 assertions red.
|
||||
Reference in New Issue
Block a user