fix(opcua): a configured OpcUa:EnabledSecurityProfiles list replaces the default, so None can be dropped (#879)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
ConfigurationBinder appends configured collection items to an existing list, so the pre-filled [None, Sign, SignAndEncrypt] default made "drop None" a no-op: every deployment that listed only the secure profiles still advertised an unsigned/unencrypted None endpoint. EnabledSecurityProfiles is now null by default; ResolveEnabledSecurityProfiles() applies the unchanged three-profile default only when nothing is configured. A set-but-empty list fails startup validation and BuildSecurityPolicies no longer falls back to a None endpoint on empty input (it throws). Sweep of all options classes found no other non-empty pre-filled collection default. Tests: options binding (indexed, JSON, env forms, default, empty, bad name), a booted-server wire test (no None endpoint, None connection refused with BadSecurityPolicyRejected, secure connection works; default case is the control). Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
+2
-2
@@ -88,7 +88,7 @@ The profiles are the members of the `OpcUaSecurityProfile` enum (`src/Server/ZB.
|
||||
|
||||
`BuildSecurityPolicies` (`OpcUaApplicationHost.cs`) maps each configured profile to an SDK `ServerSecurityPolicy`. The server exposes a separate endpoint per configured profile and clients select the one they prefer at session open. The enum's XML doc notes that Aes128/Aes256 variants can be added later by extending the enum + `BuildSecurityPolicies` — the wiring is profile-agnostic — but they are **not implemented today**. There is no `SecurityProfileResolver` class.
|
||||
|
||||
> **Config value form.** The enum binds by member name, so a profile string with hyphens (e.g. `Basic256Sha256-Sign`) does **not** bind — use the exact enum-member spelling above. If `EnabledSecurityProfiles` is empty, the server falls back to a single `None` endpoint (logged, very visible) so it still has a listening endpoint.
|
||||
> **Config value form.** The enum binds by member name, so a profile string with hyphens (e.g. `Basic256Sha256-Sign`) does **not** bind — use the exact enum-member spelling above. An empty profile set is never accepted: startup validation rejects a set-but-empty list and `BuildSecurityPolicies` throws on empty input — the server no longer falls back to a `None` endpoint (lmxopcua#879).
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -108,7 +108,7 @@ Transport security is configured in the `OpcUa` section of the Host process's bo
|
||||
}
|
||||
```
|
||||
|
||||
`EnabledSecurityProfiles` is a **list** — the server publishes one endpoint per entry. The default (when the key is omitted) is all three baseline profiles (`None`, `Basic256Sha256Sign`, `Basic256Sha256SignAndEncrypt`); production deployments typically drop `None`. The list must contain at least one entry (`OpcUaApplicationHostOptionsValidator` enforces `MinCount(…, 1)`).
|
||||
`EnabledSecurityProfiles` is a **list** — the server publishes one endpoint per entry. **A configured list replaces the default; it is never merged with it** (lmxopcua#879 — before the fix the binder appended your entries to a pre-filled `[None, Sign, SignAndEncrypt]`, so `None` could not be dropped; any deployment that "dropped `None`" on an older build still advertised it). The default applies only when the key is absent (or an empty `[]`, which binds to nothing): all three baseline profiles (`None`, `Basic256Sha256Sign`, `Basic256Sha256SignAndEncrypt`). A list that is set but empty (reachable only from code) is a configuration error — `OpcUaApplicationHostOptionsValidator` fails startup — and never means "None only" or "everything". An unrecognised profile name fails the bind rather than being ignored. The same holds for the environment-variable form (`OpcUa__EnabledSecurityProfiles__0=…`). Verify with a `GetEndpoints` probe after changing it.
|
||||
|
||||
The server certificate is auto-generated on first start if none exists in `PkiStoreRoot/own/`. Always generated even for `None`-only deployments because UserName token encryption depends on it.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user