diff --git a/docs/ServiceHosting.md b/docs/ServiceHosting.md index 4a04d8d87..5b1e9adb7 100644 --- a/docs/ServiceHosting.md +++ b/docs/ServiceHosting.md @@ -115,7 +115,7 @@ dotnet muxer. See `docs/VirtualTags.md` §"Out-of-process runner". | Pipe ACL | On Windows the worker pipe always carries an explicit, protected DACL: the host account and (with an identity) the worker identity, read/write, nobody else. No SYSTEM, no Administrators, no Everyone. The owner is the host token's owner, so the worker's `CurrentUserOnly` check of the server end still holds. | | `PeCacheDir` | Created with a protected ACL: SYSTEM, Administrators and the host account full control, no Users. `pe\` grants the identity Modify (workers compile and write entries). The HMAC key and `workers\` markers are host-only. | | Install folder | The identity is **denied** read and write on `` (inherited: ReadData/List, WriteData/AddFile, AppendData/AddSubdirectory, write attributes/EA, Delete, DeleteChild, ChangePermissions, TakeOwnership; Traverse/Execute, ReadAttributes and ReadPermissions stay so the loader reaches `script-worker\`). The worker cannot read `appsettings*.json`, logs or host binaries even where `Users` can. `script-worker\` grants it read + execute. | -| Sensitive folders (`-DenyPaths`, lmxopcua#890) | The same inherited deny on every listed folder. **Name every folder outside `` that holds keys, secrets, stores, logs or backups**: the worker's restricting SIDs include `Everyone` and `BUILTIN\Users`, so anything Users can read is otherwise readable to it (and a data drive usually lets Users create files in subfolders, e.g. a certificate in `pki\trusted\certs`). Name the deployment folder around `` (one ACE covers `pki\`, `data\`, `logs\`, `backup\`, old `app.bak.*`) **and** `pki\` and `data\` themselves, so the deny holds even on a folder whose DACL does not inherit. Drive and share roots, `script-worker\` and the PE cache are refused; each folder must exist. The list is recorded under `HKLM\…\Services\OtOpcUaScriptWorker\Parameters` (`DenyPaths`): a re-run removes the deny from folders dropped from the list, and `-Uninstall` removes every recorded one without repeating `-DenyPaths`. After an apply the installer WARNs about any item under a denied folder whose DACL is protected and has no deny of its own (the inherited deny does not reach it); without `-DenyPaths` it WARNs about `pki`/`data`/`logs`/`backup`/`config`/`secrets` siblings of ``. Wonder layout: `-InstallRoot E:\ApiInstall\OtOpcUa\app -DenyPaths E:\ApiInstall\OtOpcUa,E:\ApiInstall\OtOpcUa\pki,E:\ApiInstall\OtOpcUa\data`. | +| Sensitive folders (`-DenyPaths`, lmxopcua#890) | The same inherited deny on every listed folder. **Name every folder outside `` that holds keys, secrets, stores, logs or backups**: the worker's restricting SIDs include `Everyone` and `BUILTIN\Users`, so anything Users can read is otherwise readable to it (and a data drive usually lets Users create files in subfolders, e.g. a certificate in `pki\trusted\certs`). Name the deployment folder around `` (one ACE covers `pki\`, `data\`, `logs\`, `backup\`, old `app.bak.*`) **and** `pki\` and `data\` themselves, so the deny holds even on a folder whose DACL does not inherit. Drive and share roots, `script-worker\` and the PE cache are refused; each folder must exist. The list is recorded under `HKLM\…\Services\OtOpcUaScriptWorker\Parameters` (`DenyPaths`): a re-run removes the deny from folders dropped from the list, and `-Uninstall` removes every recorded one without repeating `-DenyPaths`. After an apply the installer WARNs about any item under a denied folder whose DACL is protected and has no deny of its own (the inherited deny does not reach it); without `-DenyPaths` it WARNs about `pki`/`data`/`logs`/`backup`/`config`/`secrets` siblings of ``. Wonder layout: `-InstallRoot E:\ApiInstall\OtOpcUa\app -DenyPaths E:\ApiInstall\OtOpcUa,E:\ApiInstall\OtOpcUa\pki,E:\ApiInstall\OtOpcUa\data,C:\ProgramData\ZB\kek` plus, box-wide (lmxopcua#891), every other app folder under `E:\ApiInstall` and the staging folders `E:\psrem-stage`, `E:\psrem`; keep deploy backups (`.reg` exports, appsettings copies) in a folder with a protected SYSTEM + Administrators ACL. | | Firewall | Outbound **block** rule `OtOpcUa-ScriptWorker-BlockOutbound` on `script-worker\ZB.MOM.WW.OtOpcUa.ScriptWorker.exe`, every profile. It covers the self-contained apphost the Windows installs run. A framework-dependent worker runs under `dotnet.exe` and is not covered, so there the analyzer's `System.Net` ban is the only network control. | The plan is pure data asserted by `scripts/install/tests/Test-ScriptWorkerIsolation.ps1` (`pwsh -File …`, any OS). diff --git a/docs/deploy/qa-upgrade-b8b4c18f-to-fea7de0a.md b/docs/deploy/qa-upgrade-b8b4c18f-to-fea7de0a.md index 52dc420dc..0247934e5 100644 --- a/docs/deploy/qa-upgrade-b8b4c18f-to-fea7de0a.md +++ b/docs/deploy/qa-upgrade-b8b4c18f-to-fea7de0a.md @@ -318,3 +318,34 @@ already passed on docker-dev (§12.4). - **Rollback of this run:** remove the canary by deleting its rows in the AdminUI and deploying (or restore `OtOpcUaConfig_pre-fea7de0a_20261112.bak`). The isolation rollback is unchanged: `… -ConfigureHostService -Uninstall` removes the four recorded denies. + +## Box-wide worker denies and backup lockdown (lmxopcua#891, 2026-11-12, owner-approved) + +**Owner approval:** the owner approved locking down the registry exports and adding the denies for the other apps' folders. Done on both vq03 and vq04 with the `f75ee7d0` installer staged at `E:\psrem-stage\otopcua-890-f75ee7d0\install`. Order: one `-WhatIf` run, then the apply, then a re-apply that reported all items `unchanged`. No service was restarted. + +**1. Registry exports locked down.** These six deploy-backup `.reg` files per box now have a protected DACL allowing only `SYSTEM` and `BUILTIN\Administrators` (FullControl); every other ACE was removed: +- `hg-d8d890c\backup\HistorianGateway-svc-*.reg` +- `mxgw-4f931e4\backup\MxAccessGw-servicekey-*.reg` (2 files) +- `otopcua-b8b4c18f\backup\OtOpcUaHost-service*.reg` (2 files) +- `otopcua-fea7de0a\backup\OtOpcUaHost-pre-fea7de0a.reg` + +The files themselves were not deleted, so they still serve as rollback copies. + +**2. Box-wide denies.** `-DenyPaths` now lists: +- `E:\ApiInstall\OtOpcUa`, `\pki`, `\data`, and `C:\ProgramData\ZB\kek` (all from before); +- `E:\ApiInstall\ScadaBridge`, `HistorianGateway`, `MxAccessGateway`, `Overview` and `btdbmanager`; +- `E:\psrem-stage` and `E:\psrem`. + +`icacls` shows `NT SERVICE\OtOpcUaScriptWorker:(OI)(CI)(DENY)(W,D,WDAC,WO,RD,DC)` on each of these folders, and `reg query` shows the 11 recorded paths. The deny names only the worker identity, so the other apps' service accounts are not affected. + +**3. Installer warnings, all checked.** +- The six locked `.reg` files have protected DACLs on purpose. +- On vq04, `C:\ProgramData\ZB\kek\historiangw.kek` and `mxgateway.kek` have protected DACLs that allow only `SYSTEM` and `Administrators`. + +None of these grants anything to `Everyone`, `BUILTIN\Users` or `RESTRICTED`. A restricted worker token fails the restricting-SID check on all of them, so they need no deny of their own. + +**4. After the change.** `/health/ready` returns 200 on both nodes for OtOpcUa `:9000`, Overview `:5320`, ScadaBridge `:8085` and mxgw `:5130`. + +**Deploy procedure from now on.** Write registry exports and appsettings backups into a backup folder that you create with a protected ACL (SYSTEM + Administrators only), and run the installer with the box-wide `-DenyPaths` list above. Any new app folder under `E:\ApiInstall` must be added to the list when it is installed. + +**Rollback:** re-run the installer with the previous four-path list to remove the new denies, or use `-Uninstall`. To restore the `.reg` ACLs, run `icacls /reset`.