Hi @PatrickRitchie
I am having issues connecting the agent to a broker in my local network when using TLS with PEM certificates. I am testing with v6.4.1 on Ubuntu 20.04
My configuration for the mqtt-relay part of the agent looks like this
- mqtt-relay:
QoS: 1
allowUntrustedCertificates: true
clientId: simulation
currentInterval: 10000
documentFormat: JSON
port: 8883
sampleInterval: 500
server: 192.168.161.202
tls:
pem:
certificateAuthority: /home/victor/venice_data/misc/certificates/gateway-SAF-Test-ID-2/GroupCACertificate.pem
certificatePath: /home/victor/venice_data/misc/certificates/gateway-SAF-Test-ID-2/certificate.pem
privateKeyPath: /home/victor/venice_data/misc/certificates/gateway-SAF-Test-ID-2/privateKey.pem
verifyClientCertificate: false
topicPrefix: MTConnect/Entity
topicStructure: Entity
When the agent tries to connect, I get the following error on the logs
2024-06-18 03:19:10.4204|WARN|modules.mqtt-relay|MQTT Relay Connection Error : error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certifica>
2024-06-18 03:19:10.4204|WARN|modules.mqtt-relay|MQTT Relay Connection Error : Authentication failed, see inner exception.
2024-06-18 03:19:10.4204|INFO|modules.mqtt-relay|MQTT Relay Disconnected from External Broker (192.168.161.202:8883)
I am sure I can connect to the broker because using a python mqtt client with the same certificates and client ID I am able to connect without issues.
Looking into the code of the mqtt relay module I noticed that the part where we setup the certificates, the CA certificate is appended first and then we append the certificate .
|
// Set TLS Certificate |
|
if (_configuration.Tls != null) |
|
{ |
|
var certificateResults = _configuration.Tls.GetCertificate(); |
|
if (certificateResults.Success && certificateResults.Certificate != null) |
|
{ |
|
var certificateAuthorityResults = _configuration.Tls.GetCertificateAuthority(); |
|
|
|
var certificates = new List<X509Certificate2>(); |
|
if (certificateAuthorityResults.Certificate != null) |
|
{ |
|
certificates.Add(certificateAuthorityResults.Certificate); |
|
} |
|
certificates.Add(certificateResults.Certificate); |
Further down in the code, however, the certificate validation handler is declared and that uses again the CA certificate for the validation.
|
#if NET5_0_OR_GREATER |
|
// Setup CA Certificate |
|
if (certificateAuthorityResults.Certificate != null) |
|
{ |
|
tlsOptionsBuilder.WithCertificateValidationHandler((certContext) => |
|
{ |
|
var chain = new X509Chain(); |
|
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; |
|
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; |
|
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; |
|
chain.ChainPolicy.VerificationTime = DateTime.Now; |
|
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0); |
|
chain.ChainPolicy.CustomTrustStore.Add(certificateAuthorityResults.Certificate); |
|
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; |
|
|
|
// convert provided X509Certificate to X509Certificate2 |
|
var x5092 = new X509Certificate2(certContext.Certificate); |
|
|
|
return chain.Build(x5092); |
|
}); |
|
} |
|
#endif |
On the MQTTnet repo I cannot find exact references to how this setup is done but I imagine the CA certificate, being in that list first, will fail to validate on the broker side since it is not the "main" certificate.
As an experiment I removed the appending of the CA certificate from the certificate lists and now the relay is able to connect to the broker without an issue. Is this something that makes sense? I am not an expert on this so I might be doing something wrong.
Side notes
Exception logging
I noticed that when the agent was throwing exceptions I was seeing log lines with the following
2024-06-18 03:19:10.4204|WARN|modules.mqtt-relay|MQTT Relay Connection Error : Authentication failed, see inner exception.
When the exceptions are handled then only the last message is printed in the main connection loop, but it will not print any inner exceptions
|
catch (Exception ex) |
|
{ |
|
Log(MTConnectLogLevel.Warning, $"MQTT Relay Connection Error : {ex.Message}"); |
|
} |
|
finally |
|
{ |
|
if (_documentServer != null) _documentServer.Stop(); |
|
} |
|
|
|
Log(MTConnectLogLevel.Information, $"MQTT Relay Disconnected from External Broker ({_configuration.Server}:{_configuration.Port})"); |
|
|
|
await Task.Delay(_configuration.ReconnectInterval, _stop.Token); |
|
} |
|
catch (TaskCanceledException) { } |
|
catch (Exception) { } |
I have added some calls to exception.getBaseException()
and checking for inner messages to aid on the debugging, something like
Log(MTConnectLogLevel.Warning, $"MQTT Relay Connection Error : {ex.GetBaseException().Message}")
Maybe is worth to add some of this inner exception logging or logging the full traceback?
VerifyClientCertificate option
This option allows untrusted certificates to be handled, and that is setup as expected. There are a couple of options that might be relevant to set when this parameter is set in the configuration, WithIgnoreCertificateRevocationErrors()
and WithIgnoreCertificateChainErrors()
. Would this be relevant?