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

Unable to connect to SQL Server with encryption enabled with 4.0 #1402

Closed
jmezach opened this issue Nov 23, 2021 · 68 comments
Closed

Unable to connect to SQL Server with encryption enabled with 4.0 #1402

jmezach opened this issue Nov 23, 2021 · 68 comments

Comments

@jmezach
Copy link

jmezach commented Nov 23, 2021

Describe the bug

We've recently upgraded one of our applications from .NET 5 to 6 and also updated Microsoft.Data.SqlClient from 3.0.1 to 4.0.0. After this upgrade we are unable to connect to our SQL Server running on Windows from a Linux container running on Kubernetes based on the dotnet/aspnet:6.0-alpine image. Connecting to the SQL Server gives us the following exception:

Microsoft.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught)
 ---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
   at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
   at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at Microsoft.Data.SqlClient.SNI.SNITCPHandle.EnableSsl(UInt32 options)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry, SqlConnectionOverrides overrides)
   at Microsoft.Data.SqlClient.SqlConnection.InternalOpenAsync(CancellationToken cancellationToken)
--- End of stack trace from previous location ---
   at HealthChecks.SqlServer.SqlServerHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken)
ClientConnectionId:32fb614f-d95f-4cb9-9c4b-d54ce172d57d
Error Number:-2146893019,State:0,Class:20

Going back to .NET 5 and Microsoft.Data.SqlClient 3.0.1 fixes the issue. Even after disabling encryption on the server we still receive the same error. Only after explicitly setting Encrypt=False in the connection string the error goes away.

To reproduce

The issue shows up as soon as we try to make a connection to the SQL Server from the container, so it should be fairly simple to reproduce.

Expected behavior

I would expect the connection to be established since the root CA's certificate has been added to the trusted root within the container.

Further technical details

Microsoft.Data.SqlClient version: 4.0
.NET target: .NET 6
SQL Server version: SQL Server 2019
Operating system: Docker container for the app, Windows for the SQL Server

Additional context
The SQL Server is using a certificate that is signed by an Active Directory certificate authority. At first we thought the problem lied in the way we injected the public key certificate of that CA into the container (by mounting it into /etc/ssl/certs), but even after building a new image with the CA certificate build into it (and running update-ca-certificates) we are still seeing the same issue.

@JRahnama
Copy link
Contributor

@jmezach the default value for connection string property Encrypt has set to true, which previously was false. You can read more here.

@alansingfield
Copy link

It may be a reasonable idea to encourage the use of this setting, but the "discovery pathway" is terrible! The error message should state upfront that Encrypt=False will fix the problem.

@jmezach
Copy link
Author

jmezach commented Nov 24, 2021

Yes, I noticed that Encrypt=True is now the default, which makes sense. Interestingly we are forcing encryption on the server, so it should have used encryption when we were running .NET 5 and Microsoft.Data.SqlClient 3.0 even though we didn't have Encrypt=True in the connection string right?

@jmezach
Copy link
Author

jmezach commented Nov 24, 2021

I just tried doing the same on my MacBook and I get the exact same error message, but it works just fine on Windows. So it seems that the behaviour is different on Windows than it is on Mac and Linux.

@jmezach
Copy link
Author

jmezach commented Nov 24, 2021

Downgrading Microsoft.Data.SqlClient to 3.0.1 but staying on .NET 6 solves the issue on my Mac, so it looks like there's definitely something going on with 4.0.

@roji
Copy link
Member

roji commented Nov 24, 2021

It may be a reasonable idea to encourage the use of this setting, but the "discovery pathway" is terrible! The error message should state upfront that Encrypt=False will fix the problem.

I think the idea here is to encourage users to actually enable encryption at the server, rather than disable encryption on the client.

@JRahnama
Copy link
Contributor

@alansingfield we do not recommend setting that value to false on production applications. Our recommended solution is to use encryption. For testing and non production applications you can set the value to false and also can leave that value set to true and add another connection string property TrustServerCertificate and set it to true. In this case at least you can keep your connection encrypted.

@Zetanova
Copy link

Does the SqlClient support a CustomCertifacteValidator like HttpClient or GrpcClient ?
The hostname of a mssql server can vary especial for internal and external access.
A validation of the certificate thump like in ssh would be nice
something like TrustServerCertificateThump=b7d6c2997179bfbe172ea76ef51f05b7cbdeacd4; would be optimal

@JRahnama
Copy link
Contributor

@Zetanova SqlClient driver does not interfere with validation. It just passes that to SslStream class. Related code for Managed SNI could be seen here and native code here.

@Zetanova
Copy link

@JRahnama Thx for answer. I could only find this static validator :

internal static bool ValidateSslServerCertificate(string targetServerName, X509Certificate cert, SslPolicyErrors policyErrors)

It does even try to allow hostnames with different lengths

The problem is that it is all internal and static and nothing to cache the validator external without recompiling the project
and using with EF

@jmezach
Copy link
Author

jmezach commented Nov 25, 2021

I feel like this issue is going a bit of topic. What we're seeing is a regression issue between 3.0.1 and 4.0 where the older version is capable of establishing a connection to our SQL Server with an encrypted connection, while the newer version does not.

@Zetanova
Copy link

I had the same issue with the same error and to add the TrustServerCertificate=true; into the connection string did resolve it.

@jmezach
Copy link
Author

jmezach commented Nov 25, 2021

I get that, but that setting essentially says trust whatever certificate is being presented by the server, which isn't very secure imho.

@Zetanova
Copy link

@jmezach From your Additional context I can assume that you made a mistake with the directory and method

You need to copy your ca-crt into a different directory (build time, do not mount) first and then run update-ca-certificates
alternatively you can append your ca-crt to the file /etc/ssl/certs/ca-certificates.crt and do not run update-ca-certificates after it.

Option A:

FROM dotnet/aspnet:6.0-alpine

COPY ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt
RUN update-ca-certificates

Option B:

FROM dotnet/aspnet:6.0-alpine

COPY ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt
RUN cat /usr/local/share/ca-certificates/ca-cert.crt >> /etc/ssl/certs/ca-certificates.crt

@jmezach
Copy link
Author

jmezach commented Nov 25, 2021

@Zetanova We tried option A, but unfortunately we still ran into the same issue.

And again, we've downgraded our application to use version 3.0.1 of Microsoft.Data.SqlClient and we have confirmed that all connections coming from our container(s) are using an encrypted connection using the following query:

SELECT session_id, connect_time, net_transport, encrypt_option, auth_scheme, client_net_address 
FROM sys.dm_exec_connections

Additionally HTTPS connections are working correctly from the container to other machines in our network that have certificates signed with the same CA root.

Thus so far the only combination that doesn't seem to work is one where we're using version 4.0 of Microsoft.Data.SqlClient.

@labsilva
Copy link

I'm facing the same situation.
Only happens when running on Linux.
Adding TrustServerCertificate=true or reverting to 3.0.1 seems to fix it, although adding TrustServerCertificate is not acceptable in my use case.

@CoolDadTx
Copy link

I'm facing this issue as well but my code works just fine in a regular .NET 5 console app. However a unit test (running against .NET 5) with the exact same block of code fails with this error.

I'm sorry but turning on a feature that could break existing, working code is just bad form, irrelevant of opinions on best practice. Why didn't the team provide an opt-in feature until this breaking change was better announced? This entire library has been replete with issues from the getgo (locked files, extra dependencies, etc) and I really struggle trying to justify why we should be using it over the already working, no issues System.Data.SqlClient. Changes like this really damage the trust we should have in MS libraries.

@JRahnama
Copy link
Contributor

JRahnama commented Dec 1, 2021

@CoolDadTx , we hear your comment and appreciate that you brought it up here. Your comment might be true in your specific use case, but we have so many other users to use the driver in different use cases. There are two issues being addressed here:

Default Encrypt to True. This is for security. Similar to http/https, if the client starts with allowing non-encrypted connections, it will always be susceptible to MITM attacks. Even if the server is configured to require encryption, there can be a MITM altering the server's response to say it doesn't require encryption. The MITM can then proxy the connection. client <-plain text-> MITM <-encrypted-> server = the connection is compromised.

Security has been encouraging us for years to change the default behavior of client drivers to be secure by default and we have resisted, knowing that it is a breaking change for most users. It's easy enough for devs to add Encrypt = false to all their connection strings, if they need to. We just want to make sure they understand the choice they are making and they are making it deliberately. With cloud computing becoming more and more common, it's not safe to leave the default value of Encrypt equal to false.

The less-breaking, but still important, fix here is to ensure connections fail if the client does not have any encryption libraries available and either Encrypt = true or the server requires encryption. SqlClient + native SNI is the only MS driver we've found that will successfully connect in that scenario.

plus, System.Data.SqlClient is in servicing mode and is not updating or adding new features on regular bases unless there is a critical bug fix. Microsoft.Data.SqlClient is the only ADO.NET active library.

@CoolDadTx
Copy link

@JRahnama,

It's easy enough for devs to add Encrypt = false to all their connection strings,

Unless, like me, you manage over 80 different applications across multiple servers. In which case it'll take a reasonable amount of time to update them assuming the developer updating the library even knows to do so.

@jmezach
Copy link
Author

jmezach commented Dec 2, 2021

I still feel like we are not addressing the actual issue here that version 4.0 is unable to connect, while 3.0.1 connects just fine in an otherwise same setup. Unfortunately downgrading introduces its own set of problems for us as well which makes this particularly problematic for us.

@atombwp
Copy link

atombwp commented Dec 2, 2021

Default Encrypt to True. This is for security. Similar to http/https, if the client starts with allowing non-encrypted connections, it will always be susceptible to MITM attacks. Even if the server is configured to require encryption, there can be a MITM altering the server's response to say it doesn't require encryption. The MITM can then proxy the connection. client <-plain text-> MITM

The difference is. With https you can install a self signed cert and the connection will be established, with SqlClient 4.0 that doesnt work (?). With https there even isnt need for TrustServerCertificate or Encrypt params in header.

@JRahnama
Copy link
Contributor

JRahnama commented Dec 2, 2021

@jmezach we are trying to repro the issue. Can you walk us through the issue? On a container (Linux) when on MDS v4 and net5 or 6 and you can see the issue. Is that correct? ( MDS 4.0 and net 5 is more of our interest to see if that is related to net 6 or driver)

@jmezach
Copy link
Author

jmezach commented Dec 2, 2021

From what I've seen so far I don't think .NET 5 or 6 has any bearing on the issue, it is just the version MDS. I've written a small .NET 6 console app with the following code:

using Microsoft.Data.SqlClient;

Console.WriteLine("Connecting...");

string connectionString = "Data Source=ZEUSSQLAPOLLO1.zeus.corp;Initial Catalog=ConfigurationDatabase;Encrypt=true;User Id=someusername;Password=somepassword";

using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();

Console.WriteLine("Connected!");

Running this on my MacBook Pro with a <PackageReference> for Microsoft.Data.SqlClient version 3.0.1 I get the following output:

 ~/Projects/demos/SqlTls  dotnet run
Connecting...
Connected!

When I change the version number of the package reference to 4.0.0 and run it again I get the following output:

 ~/Projects/demos/SqlTls  dotnet run
Connecting...
Unhandled exception. Microsoft.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught)
 ---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
   at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
   at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at Microsoft.Data.SqlClient.SNI.SNITCPHandle.EnableSsl(UInt32 options)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean& marsCapable, Boolean& fedAuthRequired)
   at Microsoft.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover, SqlAuthenticationMethod authType)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool)
   at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in /Users/jonathanm/Projects/demos/SqlTls/Program.cs:line 9
   at Program.<Main>(String[] args)
ClientConnectionId:53d43214-a8ac-4216-9c11-2c95d5954f6d
Error Number:-2146893019,State:0,Class:20

If I build a container using the following Dockerfile and build it using docker build -t sqltls:local .:

FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine

WORKDIR /app

COPY Program.cs /app
COPY SqlTls.csproj /app

COPY root.pem /etc/ssl/certs

RUN dotnet build

ENTRYPOINT [ "dotnet", "run" ]

And then run that with the package reference at 3.0.1 I get the following output:

 ~/Projects/demos/SqlTls  docker run -it sqltls:local
Connecting...
Connected!

Again, updating the package reference to 4.0.0 I get the following output:

 ~/Projects/demos/SqlTls  docker run -it sqltls:local   
Connecting...
Unhandled exception. Microsoft.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the pre-login handshake. (provider: TCP Provider, error: 35 - An internal exception was caught)
 ---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
   at System.Net.Security.SslStream.SendAuthResetSignal(ProtocolToken message, ExceptionDispatchInfo exception)
   at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](TIOAdapter adapter, Boolean receiveFirst, Byte[] reAuthenticationData, Boolean isApm)
   at System.Net.Security.SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at Microsoft.Data.SqlClient.SNI.SNITCPHandle.EnableSsl(UInt32 options)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean& marsCapable, Boolean& fedAuthRequired)
   at Microsoft.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean withFailover, SqlAuthenticationMethod authType)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool)
   at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at Microsoft.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in /app/Program.cs:line 9
   at Program.<Main>(String[] args)
ClientConnectionId:8e9eecb2-e979-4f40-9e84-95a492788fd5
Error Number:-2146893019,State:0,Class:20

Doing a dotnet publish -r win-x64 and copying the resulting output to a Windows machine and running it there works just fine, regardless of whether we're using 3.0.1 or 4.0.0.

@JRahnama
Copy link
Contributor

JRahnama commented Dec 2, 2021

@jmezach thanks for prompt and detailed response. I will test it shortly and get back to you soon.

@Zetanova
Copy link

Zetanova commented Dec 2, 2021

@jmezach pls try and replace in your docker file:

COPY root.pem /etc/ssl/certs

with following

COPY root.pem /usr/local/share/ca-certificates/ca-root.crt
RUN update-ca-certificates

You wrote that you tested it, but I still believe that it can fix the issue
YOu can test it with

docker run -rm yourimage:latest cat /etc/ssl/certs/ca-certificates.crt

Your root certificate should then be in the output

@jmezach
Copy link
Author

jmezach commented Dec 2, 2021

@Zetanova Just tried that, but I'm still receiving the same error message. And again it works fine with 3.0.1.

@jmezach
Copy link
Author

jmezach commented Dec 9, 2021

Any updates on this issue? We've added TrustServerCertificate=True to our connection string for now as a workaround, but that basically disables the security check which is obviously not what we want.

@Ilchert
Copy link

Ilchert commented Dec 23, 2021

We have the similar issue, EWT says 'SNICommon.ValidateSslServerCertificate | ERR | targetServerName servername.com, SslPolicyError RemoteCertificateChainErrors, SSL Policy invalidated certificate.'

But servername is matched with certificate and root cert placed into right place.

@DataJuggler
Copy link

I'm facing this issue as well but my code works just fine in a regular .NET 5 console app. However a unit test (running against .NET 5) with the exact same block of code fails with this error.

I'm sorry but turning on a feature that could break existing, working code is just bad form, irrelevant of opinions on best practice. Why didn't the team provide an opt-in feature until this breaking change was better announced? This entire library has been replete with issues from the getgo (locked files, extra dependencies, etc) and I really struggle trying to justify why we should be using it over the already working, no issues System.Data.SqlClient. Changes like this really damage the trust we should have in MS libraries.

I agree I do not like this breaking change. Can someone explain what Encrypt = True actually does? Does this mean I have to encrypt my connectionstrings when I store in System Environment Variables? If yes, how do I encrypt them, what password code or algorithm.

I don't get what this solves? The people I see that set Encrypt=true, I can still read the connection string in plain text.

Thank you, Microsoft, you just broke another opensource project. I have spent the last 7 weeks upgrading from 5.0 to 6.0, and open source doesn't pay very well.

@premradhakrishnan
Copy link

Just came across this issue and commenting as its not closed yet.

If the default encrypt setting becomes true, then theoretically we have 2 options.
Option 1 (recommended) : Assign a valid certificate to the SQL Server Instance, a certificate that will be trusted by all clients.
Option 2 : Add trustservercertificate=true in the connection string

Option 2 is not recommended as it is not secure because it leaves the connection vulnerable to MITM attacks.

Therefore, the only viable and recommended option is to have a valid and trusted certificate assigned to the SQL Server instance. The errors listed e.g. The remote certificate was rejected by the provided RemoteCertificateValidationCallback. and The remote certificate is invalid according to the validation procedure are due to the server certificate not being trusted by the client machine. The only way to fix these errors is to update the certificate to have all the required parameters and then reassign it. (Or follow Option 2 and add trustservercertificate=true in the connection string which will then ignore the validation checks on the certificate and trust whatever certificate is presented. But then this is obviously insecure and not recommended as mentioned earlier.)

This article lists all the parameters you need for the certificate.

The options available for a trusted certificate are many depending on your environment. Based on the server name you use to connect to the SQL Server, you could get a DNS certificate from GoDaddy or Digicert and use this. If your server and client are hosted within a trusted AD environment, your IS team might already have trusted root certficates enabled and then you can just assign this to SQL Server. In dev you can always create a self-signed certificate and import this into your trusted store.

Hope the above helps at least a few people if not all.

Thanks

@Toqe
Copy link

Toqe commented Jun 9, 2022

@premradhakrishnan thanks for the summary, indeed most people searching for this error message may have an issue with their certificates and have to fix this as you described.

But let me add that this exact issue is caused by a code change in version 4.0 of Microsoft.Data.SqlClient, which to my understanding lets the authentication of the connection to SQL Server fail, if the CRL (certificate revocation list) can't be provided by the environment at a certain point during authenticaion. It seems like PR #1559 will fix this issue with the upcoming 5.0 version (please correct me where I'm wrong, I'm not involved in SqlClient, just observing this issue).

@JRahnama
Copy link
Contributor

@Toqe PR #1559 will address the CRL issue. When the preview package is released we recommend users to test with the package to see if the issue is solved.

@MonocleKelso
Copy link

@JRahnama we had the identical experience as @jmezach. We connect to AWS RDS and MDS v4 works fine after you install the certificates for the region your DB is in but only in Windows. When running in Docker Linux we get the same exception.

I tried to use v5 preview3 which also works in Windows but in Docker you get the following exception:

System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.SqlServer.Server, Version=5.0.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5'. The system cannot find the file specified.

@ErikEJ
Copy link
Contributor

ErikEJ commented Jun 23, 2022

@MonocleKelso try preview 2 instead.

@MonocleKelso
Copy link

@ErikEJ preview 2 works. Thanks!

@JRahnama
Copy link
Contributor

I would like to ask all users who encountered this issue to test with the latest release of M.D.SqlClient to see if the problem is solved.
@jmezach

@jmezach
Copy link
Author

jmezach commented Nov 14, 2022

We are using version 5.0.1 now which seems to work just fine on our development and test environments, but doesn't yet work in production. For production we are connecting to a SQL Server cluster through a listener. The listener has a certificate which has the common name of one of the servers of the cluster, but with subject alternative names of the listener. So for example:

Common Name=SQL-CL01-N1.domain.corp
Subject Alternative Names=SQL-CL01-N1, SQL-CL01-L, SQL-CL01-L.domain.corp

Our connection string then uses:

Data Source=SQL-CL01-L;Initial Catalog=...

But with that setup we are still getting the same error.

@ErikEJ
Copy link
Contributor

ErikEJ commented Nov 14, 2022

Have you tried:

Data Source=SQL-CL01-N1.domain.corp;Initial Catalog=...

@jmezach
Copy link
Author

jmezach commented Nov 14, 2022

We did, unfortunately there is a firewall blocking that traffic currently so we'll have to involve another team to open that up. We are expecting that to work though, as that is exactly the same as all other environments. However, we were expecting the subject alternative names to work as well?

@jsmarsch
Copy link

jsmarsch commented Nov 15, 2022

If this is going to be the new default, could we make it easier to find documentation on how to configure it? I just migrated to EF 7, and so far the only way I can successfully connect to a db is by setting Encrypt=False. But without it set to false, I just get exceptions trying to connect. I'm assuming that I need to install a cert on my SQL server, and then also somewhere in my client app, but not finding any docs online on how to proceed. (note: this is just running on my development machine -- win 10/.Net 7/SQL server Developer Edition

@Hastaroth1
Copy link

I'm having this issue when upgrading a .Net 6 AspNetCore application to .Net 7.

The application works fine with

<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.2">

This fails when upgrading everything to use Version="7.0.0" unless I add TrustServerCertificate=true; at the end of my connection string.

@lcheunglci
Copy link
Contributor

lcheunglci commented Nov 17, 2022

I'm having this issue when upgrading a .Net 6 AspNetCore application to .Net 7.

The application works fine with

<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.2">

This fails when upgrading everything to use Version="7.0.0" unless I add TrustServerCertificate=true; at the end of my connection string.

I believe when you upgraded from EF Core 6 to EF Core 7, there was a breaking change (from Microsoft.Data.SqlClient 2.1.4 to 5.0.1) mentioned in the release notes re: the default value of Encrypt where it use to be set to false, but was changed to true. You can find the details in another closed issue found here .

@scholtz
Copy link

scholtz commented Dec 28, 2022

We are having the same issue when upgrading the app from .net3.1 to .net6

Encrypt=False solved the issue.. i assume something is not correctly setup for encryption

I am sure that the servers communicate through ssl as we use integrated security and we solved the ca issue with .net3

For the record we used mcr.microsoft.com/dotnet/core/sdk:3.1-buster and nowe we use mcr.microsoft.com/dotnet/aspnet:6.0-focal as base images to deploy apps

@ErikEJ
Copy link
Contributor

ErikEJ commented Dec 28, 2022

@scholtz Encrypt was always false before, did you have it set to true previously?

@scholtz
Copy link

scholtz commented Dec 28, 2022

No, we had it without the value inserted.. With integrated security I assume it would not work as well without setting up the CA correctly. We also use connection between services encrypted with https, so i am sure that CA certificate within docker container works correctly. Perhaps the CA does not have correct CRL or something, and .net6 has more strict verification.. However the connection between services still works, so it should be something mssql related.

@ErikEJ
Copy link
Contributor

ErikEJ commented Dec 28, 2022

@scholtz the default value was false before, now the default value is true.

@cheenamalhotra
Copy link
Member

cheenamalhotra commented Dec 28, 2022

.NET 5+ now respects Open SSL ciphers, could you try recommendations from here?

Default Cipher suites for TLS on linux

For additional reference: SqlClient Troubleshooting Guide

If you didn't set Encrypt=True before SqlClient 4.0, your app was not encrypting server connection, and now encryption is set to True by default.

Recommended action is to set "Trust Server Certificate = True" instead of setting "Encrypt = False" until proper server certificates are installed on Client. And making sure TLS 1.2 is supported by server/client for secure communication.

If your server doesn't support TLS 1.2, we recommend upgrading or alternatively you may configure OpenSSL on Linux platforms as needed. You may need to enable Cipher suites for old SQL servers.

@diogosilvafullstack
Copy link

In the appsettings,json file I put :

},
"AllowedHosts": "*",
"ConnectionStrings": {
"MVCDemoConnectionsString": "server=LAPTOP-VLGA59J2\SQLEXPRESS;database=PacientesDb;Trusted_connection=true; TrustServerCertificate=true"
}

after placing the the sentence "TrustServerCertificate=true" the connection was made.

rpsft added a commit to rpsft/etlbox that referenced this issue May 5, 2023
Added NEtDateTimeKind to address TZ/noTZ ambiguity
Address https://www.npgsql.org/doc/release-notes/6.0.html#major-changes-to-timestamp-mapping incopatibility
Address dotnet/SqlClient#1402 new SQL Server encryption defaults
@sencagri
Copy link

Hello, how can I disable this thing if I don't have access to connection string. It is generated hard coded in the program and I'm stuck now.

@ErikEJ
Copy link
Contributor

ErikEJ commented Dec 9, 2023

@jmezach Could you test this with the latest preview?

@David-Engel
Copy link
Contributor

Closing as by design.

@David-Engel David-Engel closed this as not planned Won't fix, can't repro, duplicate, stale Jun 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests