diff --git a/.claude/rules/drivers.md b/.claude/rules/drivers.md index 8cfe9688a..8586e567e 100644 --- a/.claude/rules/drivers.md +++ b/.claude/rules/drivers.md @@ -293,7 +293,8 @@ host logger factory (wired in `DriverFactoryBootstrap`; the FOCAS and TwinCAT fa - **OpcUaClient server host-name check (#816):** the SDK checks the server certificate's host only with `checkDomain: true`; its 8-arg `DefaultSessionFactory.CreateAsync` hard-codes `false`, so driver, AdminUI browser and Client.Shared never checked before #816. Now `certificateValidation.validateServerHostName` (tri-state, unset = off = - unchanged; AdminUI keeps it unset) / CLI `--validate-host-name`. It checks the **configured** host (the rewritten + unchanged; AdminUI keeps it unset; **stays opt-in by owner decision #837** — the toggle lives in the form's Security +panel with the NAT/IPv6-zone/loose-match caveats) / CLI `--validate-host-name`. It checks the **configured** host (the rewritten `ConfiguredEndpoint.EndpointUrl`) and auto-accept accepts a mismatch. ⚠️ The SDK checks in TWO places — `DefaultSessionFactory.CreateChannelAsync` (GetEndpoints cert, BEFORE any security-mode test, so it fired on `None`) and `Session.OpenAsync` — so pass `checkDomain` only for a non-`None` endpoint (`OpcUaEndpointSelection.CheckDomainFor`). diff --git a/docs/drivers/OpcUaClient.md b/docs/drivers/OpcUaClient.md index 677493e6a..fd7014681 100644 --- a/docs/drivers/OpcUaClient.md +++ b/docs/drivers/OpcUaClient.md @@ -189,8 +189,16 @@ Consequences: - Test Connect (`OpcUaClientDriverProbe`) only calls GetEndpoints on an unsecured discovery channel — no session, so no certificate or host-name check. -**Policy: `certificateValidation.validateServerHostName`** (driver JSON; AdminUI driver form "Server host-name check"; -the browser reads the same JSON): +**Decision (#837, owner, 2026-11-11): the check stays OPT-IN — unset means off, and it will not become a default.** +Turning it on by default would refuse every NAT / port-published / connect-by-IP endpoint whose certificate does not +list that exact host, and every link-local IPv6 endpoint, on upgrade; and because the match is loose (first label) it +would not deliver the identity guarantee a default implies. Operators enable it per driver where trust is CA-issued +and certificate names are fully-qualified and site-unique. The AdminUI shows the toggle in the driver form's +**Security** panel (beside mode/policy) as "Server certificate host-name check", with help text covering the NAT/IP +caveat, IPv6 zone ids and the loose first-label match. + +**Policy: `certificateValidation.validateServerHostName`** (driver JSON; AdminUI driver form, Security panel, +"Server certificate host-name check"; the browser reads the same JSON): | Value | Meaning | |---|---| diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/OpcUaClientDriverForm.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/OpcUaClientDriverForm.razor index f52566321..1cbb7f2bd 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/OpcUaClientDriverForm.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/OpcUaClientDriverForm.razor @@ -111,6 +111,30 @@ } + @* #816 / #837: opt-in by owner decision (unset = off). Kept in the Security panel so it is + found next to the mode/policy it depends on; "Default (off)" leaves the key absent. *@ +
BadCertificateHostNameInvalid). Applies to Sign / SignAndEncrypt only, never to
+ None, and auto-accept accepts a mismatch. Caveats:
+ fe80::1%eth0, link-local) never match — leave off.plc1.site-b.corp passes a certificate for PLC1), so use fully-qualified, site-unique certificate names.