Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 5.0 TLS Errors #904

Closed
iamcarbon opened this issue Nov 11, 2020 · 23 comments · Fixed by #907
Closed

.NET 5.0 TLS Errors #904

iamcarbon opened this issue Nov 11, 2020 · 23 comments · Fixed by #907
Assignees

Comments

@iamcarbon
Copy link
Contributor

iamcarbon commented Nov 11, 2020

When upgrading to .NET 5.0 on Linux and connecting to Aurora (v5.7), we're getting a OpenSslCryptographicException.

This occurs due to the default TLS cipher suites changing on .NET 5.0 Linux. It can be fixed by providing a custom CipherSuitesPolicy with a less restrictive cipher set.

This is a request to be able to configure the CipherSuitesPolicy (directly, or via a connection string) on .NET5.0.

Stack Trace

OpenSslCryptographicException: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
AuthenticationException: Authentication failed, see inner exception.
MySqlException: SSL Authentication Error

.NET Breaking Changes Notes

Default TLS cipher suites for .NET on Linux
.NET, on Linux, now respects the OpenSSL configuration for default cipher suites when doing TLS/SSL via the SslStream class or higher-level operations, such as HTTPS via the HttpClient class. When default cipher suites aren't explicitly configured, .NET on Linux uses a tightly restricted list of permitted cipher suites.

Change description
In previous .NET versions, .NET does not respect system configuration for default cipher suites. The default cipher suite list for .NET on Linux is very permissive.

Starting in .NET 5.0, .NET on Linux respects the OpenSSL configuration for default cipher suites when it's specified in openssl.cnf. When cipher suites aren't explicitly configured, the only permitted cipher suites are as follows:

TLS 1.3 cipher suites
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Since this fallback default doesn't include any cipher suites that are compatible with TLS 1.0 or TLS 1.1, these older protocol versions are effectively disabled by default.

Supplying a CipherSuitePolicy value to SslStream for a specific session will still replace the configuration file content and/or .NET fallback default.
@iamcarbon
Copy link
Contributor Author

@bgrainger Is it possible to configure a CipherSuitePolicy directly on the SSL stream?

@iamcarbon
Copy link
Contributor Author

iamcarbon commented Nov 12, 2020

Manually configuring the CipherSuitesPolicy policy in ServerSession fixes the problem. Still trying to find documentation on what ciphers Aurora supports.

@bgrainger What are your thoughts on adding a configuration option to specify a custom cipher suite policy passed to AuthenticateAsClientAsync?

Something like...

ciphers=Auora2

This would enable something like....

var options = new SslClientAuthenticationOptions
{
	EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11,
	ClientCertificates = clientCertificates,
	TargetHost = HostName,
	CipherSuitesPolicy = new CipherSuitesPolicy(new[] {
		TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
		TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
		TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
	}),
	CertificateRevocationCheckMode =  X509RevocationMode.NoCheck
};

await sslStream.AuthenticateAsClientAsync(options).ConfigureAwait(false);

@iamcarbon iamcarbon changed the title .NET 5.0 TLS Errors .NET 5.0 TLS Errors (Request to manual configure CipherSuitesPolicy) Nov 12, 2020
@iamcarbon
Copy link
Contributor Author

Submitted a PR #905

@iamcarbon iamcarbon changed the title .NET 5.0 TLS Errors (Request to manual configure CipherSuitesPolicy) .NET 5.0 TLS Errors Nov 12, 2020
@bgrainger
Copy link
Member

@bgrainger
Copy link
Member

@iamcarbon Is modifying the configuration in /etc/ssl/openssl.cnf (as suggested in the docs), e.g., in the Dockerfile for your application, a viable workaround in your situation?

Obviously there is an advantage to having the library work out-of-the-box with Aurora without extra configuration; I need to research that more.

@iamcarbon
Copy link
Contributor Author

iamcarbon commented Nov 12, 2020

@bgrainger : Yes, this also works, but forgoes the other benefits of a locked down default policy for the rest of the application.

I'm also not sure if this will work when we upgrade our Lambda functions in the future as the openssl.cnf won't be editable there.

Another approach would be to allow a CipherSuitesPolicy to be set globally (perhaps off a static setter). This would help users having to edit the openssl.cnf and give them explicit control over what ciphers are negotiated.

@iamcarbon
Copy link
Contributor Author

iamcarbon commented Nov 12, 2020

Revised the PR to allow defining the DefaultCipherSuitesPolicy on .NET5.0. This provides a super simple hook to configure the cipher suites (and avoid touching the openssl.cnf -- which may not be possible in certain environments). This also eliminates any magic behavior -- which may not be relevant for future Aurora updates.

4a4033a

@bgrainger
Copy link
Member

bgrainger commented Nov 13, 2020

This is a request to be able to configure the CipherSuitesPolicy (directly, or via a connection string)

Since this setting is connection-specific (more specifically, connection-pool specific), I'm tempted to make it part of the connection string. This would let it be configured without using code, is more like every other connection setting, and would be less "magic" than setting a static property. The downside is that is could be extremely verbose to specify multiple cipher suites. MySqlConnector could support specifying any TlsCipherSuite enum value either as a named value (with or without TLS_ prefix) or the underlying integral value. Your thoughts?

Some examples:

Server=aurora;User=test;TlsCipherSuites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Server=aurora;User=test;TlsCipherSuites=ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_128_GCM_SHA256,ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Server=aurora;User=test;TlsCipherSuites=49196,49195,49200,49199,49188

@bgrainger
Copy link
Member

bgrainger commented Nov 13, 2020

To reduce connection string verbosity, an additional mechanism could be TlsCipherSuiteFile={path-to-file}, where the specified plain text file contains a list of supported cipher suites, one per line. This would be like CertificateFile, CaCertificateFile, etc., where the file path is provided in the connection string, but the file itself has to be deployed with the consuming application through some other means.

@bgrainger
Copy link
Member

MySQL Connector/J uses enabledSSLCipherSuites in its connection options: https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html; MariaDB Connector/J supports the same option: https://mariadb.com/kb/en/using-tls-ssl-with-mariadb-java-connector/

@bgrainger
Copy link
Member

bgrainger commented Nov 13, 2020

In practice, the connection string may not be too verbose; only one (mutually supported) cipher suite needs to be listed in order to establish a connection. That is, the minimal Aurora connection string (for .NET 5.0 on Linux) might just be: Server=server.region.rds.amazonaws.com;User Id=user;Password=***;CACertificateFile=/rds-combined-ca-bundle.pem;TlsCipherSuites=TLS_DHE_RSA_WITH_AES_256_GCM_SHA384.

So I'm inclined to just go with that (and not implement TlsCipherSuiteFile).

@iamcarbon
Copy link
Contributor Author

A single TlsCipherSuite matching the .NET enum name is also elegant - ensures a strong cipher is used for non-aurora servers - and allows the library to connect to multiple servers in the same process with different settings. ❤️

@bgrainger
Copy link
Member

Added a PR for this: #907

@bgrainger bgrainger self-assigned this Nov 13, 2020
@bgrainger
Copy link
Member

The right answer is probably for Aurora to support modern cipher suites 😀 but I'll try to have a beta release for testing available later today.

@bgrainger
Copy link
Member

1.1.1-beta.0.1 is available for download from GHPR; are you able to test with that? https://github.com/mysql-net/MySqlConnector/packages/39735

@iamcarbon
Copy link
Contributor Author

Works, and in production now. Thanks again for the thoughtfulness you put into this!

@RyanGhd
Copy link

RyanGhd commented Nov 20, 2020

@bgrainger, is there any ETA for this version to be available in nuget?

@bgrainger
Copy link
Member

I can release a non-beta version to NuGet soon (if you can't pull it from GHPR).

@passuied
Copy link

passuied commented Nov 25, 2020

@bgrainger I'm experiencing the same issue but connecting to Aurora MySQL 5.6 which only supports TLS v1.0.
Trying to figure out which cipher suite to include (currently in the openssl.cnf file)
I have tried the cipher suite you mention (TLS_DHE_RSA_WITH_AES_256_GCM_SHA384=DHE-RSA-AES256-GCM-SHA384) but it seems it is specific to TLS 1.2

I'm not sure how to find the correct one (can't find anything useful in AWS doc)... Do you know which one applies here? and how/where to find this info?

Thanks in advance

UPDATE: I have now provisioned a test aurora MySQL 5.7 cluster and validated that the above works properly with 5.7... No luck on 5.6... Is net5.0 no longer compatible with TLS 1.0? Didn't see anything about it... Also I looked at my Aurora 5.6 server Ssl_cipher_list session variable and it includes DHE-RSA-AES256-GCM-SHA384...

UPDATE 2: I have figured it out... It was not really intuitive but I had to raise the Min Tls version to TLSv1.2 in the openssl.cnf file (I think this is so the list of cipher suites can apply). Then I had to explicitly set the TlsVersion to Tlsv1.0 in the Mysql connection string since Aurora MySQL 5.6 supports only 1.0... OUCH!! But it works!

@bgrainger
Copy link
Member

@RyanGhd This is now available on NuGet in 1.2.0.

@RyanGhd
Copy link

RyanGhd commented Nov 27, 2020

@bgrainger , thanks so much! really appreciate it.

@andre197
Copy link

andre197 commented Jun 11, 2021

NuGet version: 1.3.9
.NET version: 5.0
MySql version: 5.6

@bgrainger i'm having the same issue as @passuied. I have used the same solution which he provides but im receiving the following error

MySqlConnector.MySqlException (0x80004005): SSL Authentication Error

---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.

---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.

---> Interop+Crypto+OpenSslCryptographicException: error:141A90B5:SSL routines:ssl_cipher_list_to_bytes:no ciphers available

--- End of inner exception stack trace ---

at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount)

at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)

--- End of inner exception stack trace ---

at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)

at System.Net.Security.SslStream.ProcessAuthentication(Boolean isAsync, Boolean isApm, CancellationToken cancellationToken)

at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)

at MySqlConnector.Core.ServerSession.InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 1359

at MySqlConnector.Core.ServerSession.InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 1384

at MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, Int32 startTickCount, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 470

at MySqlConnector.Core.ConnectionPool.ConnectSessionAsync(String logMessage, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 376

at MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 107

at MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 137

at MySqlConnector.MySqlConnection.CreateSessionAsync(ConnectionPool pool, Int32 startTickCount, Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 863

at MySqlConnector.MySqlConnection.OpenAsync(Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 414

at MySqlConnector.MySqlConnection.Open() in /_/src/MySqlConnector/MySqlConnection.cs:line 380

at ConsoleApp1.Program.Main(String[] args) in C:\Users\Andrey.I.Andreev\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 32

The code i run:

namespace ConsoleApp1
{
    using System;
    using System.Data;
    using System.Threading.Tasks;
    using MySqlConnector;

    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                var csb = new MySqlConnectionStringBuilder(
                    "Server=server;" +
                    "Port=3306;" +
                    "Database=Name;" +
                    "Uid=root;" +
                    "Pwd=password;" +
                    "Connection Timeout=360;" +
                    "default command timeout=340;" +
                    "TlsVersion=\"TLS 1.0\";" +
                    "TlsCipherSuites=TLS_DHE_RSA_WITH_AES_256_GCM_SHA384");

                using (var connection = new MySqlConnection(csb.ConnectionString))
                {
                    try
                    {
                        var command = new MySqlCommand("SELECT 1", connection);
                        command.CommandType = CommandType.Text;

                        connection.Open();
                        command.ExecuteScalar();
                        Console.WriteLine("success");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex);
                    }
                }

                Task.Delay(10_000).GetAwaiter().GetResult();
            }
        }
    }
}

The version of the nuget is 1.3.9 on .net 5.0

This is the dockerfile

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["ConsoleApp1/ConsoleApp1.csproj", "ConsoleApp1/"]
RUN dotnet restore "ConsoleApp1/ConsoleApp1.csproj"
COPY . .
WORKDIR "/src/ConsoleApp1"
RUN dotnet build "ConsoleApp1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ConsoleApp1.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

RUN sed -i "s|CipherString = DEFAULT@SECLEVEL=2|CipherString = ADH-AES128-SHA:ADH-AES128-SHA256:ADH-AES256-SHA256:@SECLEVEL=0|g" /etc/ssl/openssl.cnf

ENTRYPOINT ["dotnet", "ConsoleApp1.dll"]

Code example can be found in this attachment: ConsoleApp1.zip

The solution from @passuied didn't help also the solution from here: #927
didn't help either. So can you advise on what else can i do?

Update: On adding these TLSCipherSuites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 to the end of the connection string i receive this exception:

MySqlConnector.MySqlException (0x80004005): SSL Authentication Error
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_SSL.
 ---> Interop+Crypto+OpenSslCryptographicException: error:141A90B5:SSL routines:ssl_cipher_list_to_bytes:no ciphers available
   --- End of inner exception stack trace ---
   at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount)
   at System.Net.Security.SslStreamPal.HandshakeInternal(SafeFreeCredentials credential, SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Security.SslStream.ProcessAuthentication(Boolean isAsync, Boolean isApm, CancellationToken cancellationToken)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at MySqlConnector.Core.ServerSession.InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 1359
   at MySqlConnector.Core.ServerSession.InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 1384
   at MySqlConnector.Core.ServerSession.ConnectAsync(ConnectionSettings cs, Int32 startTickCount, ILoadBalancer loadBalancer, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ServerSession.cs:line 470
   at MySqlConnector.Core.ConnectionPool.ConnectSessionAsync(String logMessage, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 376
   at MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 107
   at MySqlConnector.Core.ConnectionPool.GetSessionAsync(MySqlConnection connection, Int32 startTickCount, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/ConnectionPool.cs:line 137
   at MySqlConnector.MySqlConnection.CreateSessionAsync(ConnectionPool pool, Int32 startTickCount, Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 863
   at MySqlConnector.MySqlConnection.OpenAsync(Nullable`1 ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlConnection.cs:line 414
   at MySqlConnector.MySqlConnection.Open() in /_/src/MySqlConnector/MySqlConnection.cs:line 380
   at ConsoleApp1.Program.Main(String[] args) in C:\Users\Andrey.I.Andreev\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 32

@bgrainger
Copy link
Member

Are you connecting to Aurora, or to a privately-hosted MySQL Server?

SSL routines:ssl_cipher_list_to_bytes:no ciphers available

AFAICT, the connection is failing because your client can't negotiate a secure connection. Check the MySQL Manual on how to configure this: https://dev.mysql.com/doc/refman/5.6/en/encrypted-connection-protocols-ciphers.html#encrypted-connection-protocol-negotiation

If you can establish a secure connection with some other client (e.g., MySQL Workbench) execute SHOW SESSION STATUS LIKE 'Ssl_cipher'; to see the cipher it's using, then try to use that in your connection string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

5 participants