Serilog Email sink enableSSL false checks

Viewed 2449

I am trying to use EnableSsl = false in my EmailConnectionInfo but it seems like the smtp client being used for the smtp connection is trying to use SSL because the default SecureSocketOptions is set to Auto. When I created a client manually and used the overload with SecureSocketOptions = None it worked.

The error:

Failed to send email: MailKit.Security.SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.

The SSL certificate presented by the server is not trusted by the system for one or more of the following reasons:
1. The server is using a self-signed certificate which cannot be verified.
2. The local system is missing a Root or Intermediate certificate needed to verify the server's certificate.
3. The certificate presented by the server is expired or invalid.

See https://github.com/jstedfast/MailKit/blob/master/FAQ.md#InvalidSslCertificate for possible solutions. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

The workarround:

I am setting the ServerCertificateValidationCallback to true

When used in my Program.cs, it works:

.UseSerilog((hostingContext, loggerConfiguration) =>{
   Serilog.Debugging.SelfLog.Enable(Console.WriteLine);           
   loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)
   .WriteTo.Email(
      new EmailConnectionInfo {
      FromEmail = "{email}",
      ToEmail = "{email}",
      MailServer = "{SMTP IP}",
      Port = {PORT},
      EnableSsl = false,
      EmailSubject = "Something to log",
      ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => true
   },
   outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
   batchPostingLimit: 1,
   restrictedToMinimumLevel: LogEventLevel.Warning);
});

But, when I try to include all my serilog settings for Email sink in appsettings.json the "ServerCertificateValidationCallback": "(senderX, certificate, chain, sslPolicyErrors) => true" cannot be read (my assumption)

my appsettings.json

"Serilog": {
    "Using": [ "Serilog.Sinks.File","Serilog.Sinks.Email" ],
    "MinimumLevel": "Information",
    "WriteTo": [
      ...
      {
        "Name": "Email",
        "Args": {
          "connectionInfo": {
            "MailServer": "{SMTP IP}",
            "Port": {PORT},
            "EnableSsl": false,
            "FromEmail": "{email}",
            "ToEmail": "{email}",
            "EmailSubject": "Something went wrong",
            "ServerCertificateValidationCallback": "(s, cert, chain, sslPolicyErrors) => true"
          },
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}",
          "restrictedToMinimumLevel": "Warning",
          "batchPostingLimit": 1
        }
      }
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
  }
...

Any ideas?

Edit: The githut issue I opened

2 Answers

As you can see in Mailkit's source code, it uses SecureSocketOptions.StartTlsWhenAvailable when enableSsl is false. TLS is available in your Exchange server, so it's trying to use it...

If Serilog.Sinks.Email ever exposes a way of changing this behavior (issue 69 that you reported), that would be a workaround, but I believe the correct fix is to update your Exchange configuration to use the correct certificate that was installed for TLS communication on this connector.

When TLS is established inbound, the server is likely grabbing the closest match based upon name (self-signed certificate).

Your IT Administrator should be able to update the TLS certificate following the steps described on this post "Configuring the TLS Certificate Name for Exchange Server Receive Connectors".

I was able to get this working leveraging a few other questions and without the support for the callback being able to set this directly via configuration. This question's answer (https://stackoverflow.com/a/59914193/116208) was particularly helpful. If you follow his example there with a few modifications.

You can add the ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => true to the EmailConnectionInfo in the SerilogEmailExtension extension class

Adjust the Serilog configuration "Using" statement to include your compiled application's name so it can find the extension class instead of "MyApp":

"Using": [ "Serilog", "Serilog.Sinks.Console", "Serilog.Sinks.File", "MyApp" ],

This should let you achieve everything to be configuration driven, and still add in the callback to bypass the exception.

Related