fix(opcuaclient): never pass checkDomain for a None endpoint; document both SDK check sites (#816 review)
SDK 2.0.1 checks the server host name in TWO places: Session.OpenAsync (after the chain is trusted) and DefaultSessionFactory.CreateChannelAsync, against the certificate in the GetEndpoints reply and before any security-mode test. opc-plc sends a certificate on its None endpoint, so `connect -u opc.tcp://10.100.0.35:50000 --validate-host-name` was refused on an unauthenticated channel, where a host check proves nothing. All three call sites now pass checkDomain only when the selected endpoint's SecurityMode != None: OpcUaEndpointSelection.CheckDomainFor for the driver (OpenSessionOnEndpointAsync, so the failover sweep too) and the AdminUI browser; the same expression in Client.Shared ConnectToEndpointAsync (shared by failover). New live tests: driver None + flag on vs :50000 reads; browser None + flag on vs the NAT fixture browses; the secure + flag refusals still hold. Docs: name both check sites; a zone-qualified IPv6 host never matches (FindDomain compares the zone too), so link-local needs validateServerHostName=false; the match is loose (first label alone, subject DC= parts count, no wildcards), which caveats the CA recommendation.
This commit is contained in:
@@ -61,7 +61,7 @@ public abstract class CommandBase : ICommand
|
||||
/// Gets a value indicating whether the server certificate must name the host given in <see cref="Url" /> (#816).
|
||||
/// </summary>
|
||||
[CommandOption("validate-host-name",
|
||||
Description = "Require the server certificate to list the --url host (secured connections only; off by default — leave off behind NAT)")]
|
||||
Description = "Require the server certificate to list the --url host (sign/encrypt only, never with --security none; off by default — leave off behind NAT)")]
|
||||
public bool ValidateHostName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -44,8 +44,10 @@ public sealed class ConnectionSettings
|
||||
/// Gitea #816 — require the server certificate to list the host of the URL being connected (the operator's
|
||||
/// host: discovery keeps it in place of the server-advertised one) among its subject-alternative names;
|
||||
/// a mismatch fails the connect with <c>BadCertificateHostNameInvalid</c>. Defaults to <c>false</c>, the
|
||||
/// behaviour before #816 (the SDK overload used never checked). Secured connections only. Leave it off behind
|
||||
/// NAT / a port publish / when connecting by an IP the certificate does not name.
|
||||
/// behaviour before #816 (the SDK overload used never checked). Applied to Sign / SignAndEncrypt endpoints only
|
||||
/// — never passed for <see cref="Models.SecurityMode.None"/>, where SDK 2.0.1 would check the GetEndpoints
|
||||
/// certificate at channel creation anyway. Leave it off behind NAT / a port publish / when connecting by an IP
|
||||
/// the certificate does not name / for a zone-qualified IPv6 host (the SDK compares the zone too).
|
||||
/// </summary>
|
||||
public bool ValidateServerHostName { get; set; }
|
||||
|
||||
|
||||
@@ -794,8 +794,12 @@ public sealed class OpcUaClientService : IOpcUaClientService
|
||||
: new UserIdentity();
|
||||
|
||||
var sessionTimeoutMs = (uint)(settings.SessionTimeoutSeconds * 1000);
|
||||
// #816: never on a None endpoint — SDK 2.0.1 also checks the host at channel creation against the
|
||||
// GetEndpoints certificate, before any security-mode test, and on an unauthenticated channel that proves
|
||||
// nothing (same rule as OpcUaEndpointSelection.CheckDomainFor for the driver and the AdminUI browser).
|
||||
var checkDomain = settings.ValidateServerHostName && endpoint.SecurityMode != MessageSecurityMode.None;
|
||||
return await _sessionFactory.CreateSessionAsync(config, endpoint, "OtOpcUaClient", sessionTimeoutMs, identity,
|
||||
settings.ValidateServerHostName, ct);
|
||||
checkDomain, ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user