fixer-m / snowflake-db-net-client

Snowflake .NET Client
Apache License 2.0
51 stars 14 forks source link

"SSL connection could not be established" error #7

Closed mattthr closed 3 years ago

mattthr commented 3 years ago

Hi,

I believe I had the same issue as this poster: https://github.com/fixer-m/snowflake-db-net-client/issues/2

When I try to connect to Snowflake, I see the error:

System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

I tried the fix suggested in that thread which they never came back to you about: ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.UseNagleAlgorithm = false;
ServicePointManager.CheckCertificateRevocationList = true;

But I'm still getting the error as soon as I try to issue a query to Snowflake. Is there something else I can try, or some addition debugging info you'd like me to provide?

fixer-m commented 3 years ago

@mattthr, Hi!

What region in Snowflake do you use? Do you use any proxy in your connection? How your URL looks like when you are logged-in into Snowflake web interface? Does it ends with *.snowflakecomputing.com ?

Can you please try to run some queries with official .NET connector? Will it produce the same connection exception? Some users have this issue with official connector: https://github.com/snowflakedb/snowflake-connector-net/issues/6 , but I don't see clear answer how to resolve it.

mattthr commented 3 years ago

Thanks for coming back to me.

I can connect normally and run successful queries with the standard Snowflake .Net connector.

Our snowflake instance ends with west-europe.azure.snowflakecomputing.com

The connection I'm using (with region) is: var snowflakeClient = new SnowflakeClient("MY_USER", "myPa$$word", "my_account", "west-europe");

Leaving off the "west-europe" region and submitting just the three parameters yields a 403 Foribidden response from the Snowflake server.

We do use a proxy server as we're all working from home at the moment, but I've verified I get the same error both with and without the proxy turned on.

rafael-queiroz-cko commented 3 years ago

I have the same issue.

fixer-m commented 3 years ago

@rafael-queiroz-cko, Hi! Thanks for reporting!

@mattthr, @rafael-queiroz-cko, After some research I think the root cause of this issue is in SSL settings applied to HTTP connection. These settings/options are:

SecurityProtocol = SecurityProtocolType.Tls12;
UseNagleAlgorithm = false;
CheckCertificateRevocationList = true;

Unfortunately I can't reproduce this issue: for me it does work regardless of these settings. However I think they might be required for some regions (or cloud provider) in SF, but I don't have account in every region to check this. What's bad - these options have different default and allowed values in different Windows versions and .NET versions.

Anyway I have prepared special version of Snowflake.Client which have option to pass your own HttpClient instance, so you can configure its behavior. I've created separate branch for it: ssl-issue. So if you can test it and help me with debugging this - that would be nice.

Here is the basic idea how you can test it:

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true
};
var httpClient = new HttpClient(handler);

var snowflakeClient = new SnowflakeClient("user", "password", "account", "region");
snowflakeClient.SetHttpClient(httpClient);

var sessionInitialized = await snowflakeClient.InitNewSessionAsync(); // <= does it work?

What info also could help:

  1. Your .NET and Windows versions
  2. Your SF region and cloud provider
  3. Does official library (Snowflake.Data) works for you?
  4. Have you tried solution with ServicePointManager (see first post here)? Did it work?
rafael-queiroz-cko commented 3 years ago

1- Lambda function in AWS, it works fine in my end. But I'm connected to a VPN in my home, perhaps it is a mismatch of configurations in AWS 2- AWS, eu-west-1 3- the official library works locally but I can't publish it to AWS because it has issues with log4net not being able to run in linux 4 - didn't work

rafael-queiroz-cko commented 3 years ago

@fixer-m there is a problem with certificates. Using this might solve the problem:

handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;

After I did this my lambda stopped complaining about certificates and started complaining about reaching the host and trying to authenticate (403) which is expected, perhaps the official SDK (that crap one) does this underneath the carpet xD

This will skip certificate validation. Can you publish a nuget version with this new branch that you created? With the ability to send custom HTTP Handlers / Clients. I can check in my environment to see if it is working as expected but I cannot publish the whole code there as it is a AWS Lambda. But it might be good to understand what the underlying is. Let me know if you are willing to do this.

Thing is, why does it work in my local environment without the hack, but doesn't work in AWS Lambda?

rafael-queiroz-cko commented 3 years ago

Also, check this forked code: https://github.com/rdagumampan/snowflake-connector-net

This is forked from the original, it doesn't give me problems with certs but gave me other problems because it was forcing to log some weird stuff in a File. Which my lambda complained .

fixer-m commented 3 years ago

@rafael-queiroz-cko, thanks! I have just published new version of package (0.3.3) with SetHttpClient() method. Can you please try to use it in AWS environment?

Yes, trick with a ServerCertificateCustomValidationCallback should work: it just disables cert validation. However I can't use it as a solution in my library for obvious reasons, so I really want to figure out proper solution.

In official library they use HttpClient, but before its creation they have these lines:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.UseNagleAlgorithm = false;
ServicePointManager.CheckCertificateRevocationList = true;

However - as I recently discovered - these lines have effect only in .NET Framework. To set these parameters in .NET Core you have to use HttpClientHandler with according options, as I suggested earlier:

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true
};
var httpClient = new HttpClient(handler);

Another issue with ServicePointManager is that it sets options globally, affecting all HttpClients that you have in your application, which is really bad for library code. That's why initially I haven't reproduced this code in my library.

rafael-queiroz-cko commented 3 years ago

Ok thanks! I got something really specific and perhaps it should be enough to make it work with the right certs. The scenarios are as following:

In my HOME PC: 1 - VPN disconnected + HTTPClientHandler (with attributes) = SSL error 2 - VPN connected + HTTPCLientHandler = 🎉

AWS Lambda: 1 - No VPN conn + HTTPClientHandler = SSL error (Same as home pc) 2 - I can't run scenario 2 without making a PR to my code base (I will do now and ask someone to approve it tmr)

I will let you know

mattthr commented 3 years ago

Thanks for the new branch. I've given it a go and I'm still getting the same error. The code I'm using with the branch is:

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true
};
var httpClient = new HttpClient(handler);

var snowflakeClient = new SnowflakeClient("MY_USER", "pa$$word", "myAccount", "west-europe");
snowflakeClient.SetHttpClient(httpClient);

var sessionInitialized = await snowflakeClient.InitNewSessionAsync();

To answer your other questions:

Your .NET and Windows versions - data access is .net standard 2.0, web API layer is .net core 3.1 , windows is 10.0.18363 Your SF region and cloud provider - west-europe and Azure Does official library (Snowflake.Data) works for you? - Yes Have you tried solution with ServicePointManager (see first post here)? Did it work? - Yes, and no

fixer-m commented 3 years ago

@mattthr, Ok, what if you add "empty" ServerCertificateCustomValidationCallback to handler? Like this:

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true, 
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true // i.e. bypass cert validation
};

var httpClient = new HttpClient(handler);
var snowflakeClient = new SnowflakeClient("MY_USER", "pa$$word", "myAccount", "west-europe");
snowflakeClient.SetHttpClient(httpClient);

var sessionInitialized = await snowflakeClient.InitNewSessionAsync();

This will bypass SSL cert validation, so I would not recommend to use it in production, but this should work. If it actually does, then my next step would be to set another delegate in ServerCertificateCustomValidationCallback to log all errors - it may contain much more detailed error messages.

One more question: does your account name contains underscore ("_")? If so, try to replace it with dash ("-") when creating SnowflakeClient. It can be a cause of the issue: https://github.com/snowflakedb/snowflake-connector-net/issues/160#issuecomment-692883663

fixer-m commented 3 years ago

@mattthr, @rafael-queiroz-cko, One more finding. It appears that SF hostname can include cloud provider name, like this:

account-name.us-east-1.aws.snowflakecomputing.com

In SnowflakeClient host name - if not passed explicitly - is built like this: $"{Account}.{Region}.snowflakecomputing.com" I.e. it doesn't include cloud provider name. And incorrect hostname will lead to SSL cert error. To overcome this you can specify your region with your cloud provider like "us-east.aws".

Or you can explicitly specify your full hostname like this:

var authInfo = new AuthInfo("user", "pass", "account", "region");
var urlInfo = new UrlInfo() { Host = "your SF full hostname"};
var client = new SnowflakeClient(authInfo, null, urlInfo);

I will think how to fix this.

rafael-queiroz-cko commented 3 years ago

@fixer-m this is how I'm doing, I have the connection string in my secrets manager (AWS)

var builder = new DbConnectionStringBuilder
{
    ConnectionString = secrets.ConnectionString
};

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true,
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};

var httpClient = new HttpClient(handler);

var authInfo = new AuthInfo
{
    Account = builder["account"].ToString(),
    User = builder["user"].ToString(),
    Password = builder["password"].ToString(),
    Region = builder["region"].ToString()
};

var snowflakeClientSettings = new SnowflakeClientSettings(authInfo)
{
    UrlInfo =
    {
        Host = $"{builder["host"]}"
    }
};

var snowflakeClient = new SnowflakeClient(snowflakeClientSettings);
snowflakeClient.SetHttpClient(httpClient);

var result = await snowflakeClient.QueryRawResponseAsync(
    MissingEventsQuery.Query, new { ReportDate = positionDate });
fixer-m commented 3 years ago

@rafael-queiroz-cko, so does it work?

rafael-queiroz-cko commented 3 years ago

@rafael-queiroz-cko, so does it work?

it does.. but I'm using ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true

fixer-m commented 3 years ago

@rafael-queiroz-cko, got it. Well, at least we have working workaround. If you want to go further and try to understand why this error happens, you can change callback delegate to something that prints out error messages, maybe they will have some insights.

rafael-queiroz-cko commented 3 years ago

@rafael-queiroz-cko, got it. Well, at least we have working workaround. If you want to go further and try to understand why this error happens, you can change callback delegate to something that prints out error messages, maybe they will have some insights.

any thoughts on how to do that?

fixer-m commented 3 years ago

@mattthr, @rafael-queiroz-cko, I've just published new package version 0.3.5.

New version includes:

@rafael-queiroz-cko, please, try new version - it might work for you without any "hacks". If it's not - then you can set delegate callback to get more detailed error messages. This can be done like this:

handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
    Console.WriteLine(cert);
    Console.WriteLine(errors); 
    return errors == SslPolicyErrors.None;
};
rafael-queiroz-cko commented 3 years ago

Thanks mate, I will check this out on Monday

Obter o Outlook para Androidhttps://aka.ms/AAb9ysg


From: Ilya Bystrov @.> Sent: Friday, April 2, 2021 10:07:01 PM To: fixer-m/snowflake-db-net-client @.> Cc: Rafael Queiroz @.>; Mention @.> Subject: Re: [fixer-m/snowflake-db-net-client] "SSL connection could not be established" error (#7)

@mattthrhttps://github.com/mattthr, @rafael-queiroz-ckohttps://github.com/rafael-queiroz-cko, I've just published new package version 0.3.5.

New version includes:

@rafael-queiroz-ckohttps://github.com/rafael-queiroz-cko, please, try new version - it might work for you without any "hacks". If it's not - then you can set delegate callback to get more detailed error messages. This can be done like this:

handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => { Console.WriteLine(cert); Console.WriteLine(errors); return errors == SslPolicyErrors.None; };

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/fixer-m/snowflake-db-net-client/issues/7#issuecomment-812715944, or unsubscribehttps://github.com/notifications/unsubscribe-auth/APL2YN3OBMVZZZLNCGZ4RELTGYWXLANCNFSM4ZGK5IWQ.

--


This message is intended solely for the addressee and may contain confidential information. If you have received this message in error, please send it back to us, immediately and permanently delete it, and do not use, copy or disclose the information contained in this message or in any attachment. Any unauthorized use is strictly prohibited.


rafael-queiroz-cko commented 3 years ago

@mattthr, @rafael-queiroz-cko, I've just published new package version 0.3.5.

New version includes:

* **Cloud tag auto-detection.** Now it automatically recognizes cloud tag (`aws`,`azure`, `gcp`) by region and builds URL with it. Before this piece was missing, i.e. Snowflake URL was improperly build for a ~half of all of the regions. And wrong URL leads to SSL cert issue. @mattthr, this is exact your case, so I think new version should work for you without any special adjustments.

* **Account name auto-fix**. Now it replaces underscores in account name with dashes (SF supports this). Underscores in URL lead to SSL cert issue.

* **Fix for explicitly specified Snowflake URL.** Now explicitly specified URL (via `UrlInfo`) has higher priority than automatically built  one. I though it was already the case, but it appeared that it wasn't.

@rafael-queiroz-cko, please, try new version - it might work for you without any "hacks". If it's not - then you can set delegate callback to get more detailed error messages. This can be done like this:

handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
{
    Console.WriteLine(cert);
    Console.WriteLine(errors); 
    return errors == SslPolicyErrors.None;
};

Awesome findings @fixer-m . This has fixed the issue for me. I removed the custom HttpClient and left only the SnowflakeClientSettings creation passing it to the constructor of: SnowflakeClient. I removed all that bullshit around SSL + Certs delegation, good job!

mattthr commented 3 years ago

Hey. Sorry for the radio silence, was moving house.

I can also confirm this fixes my issue too. Thanks very much: your client is so much more streamlined than the clunky official snowflake one.

PiyushJoshi-GDM commented 3 years ago

@fixer-m I have also faced the similar issue and I have tried this code after you guys, but I'm getting an exception: Name and Service not known any help from you guys will make my day.

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = true,
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true // i.e. bypass cert validation
};
var httpClient = new HttpClient(handler);
var authInfo = new AuthInfo("user", "password", "xyz", "region");
var urlInfo = new UrlInfo() { Host = "hostname" };
var snowflakeClient = new SnowflakeClient(authInfo, null, urlInfo);
snowflakeClient.SetHttpClient(httpClient);
//exception occurs while executing this line of code -- below
var sessionInitialized = await snowflakeClient.InitNewSessionAsync();

Note: When I tried same credentials with snowflake connector for python it works but not in the case of .Net.

I'm using AWS lambda function to access snwoflake with this code and my runtime is .net core 3.1

fixer-m commented 3 years ago

@PiyushJoshi-GDM, Hi! Thanks for reporting!

PiyushJoshi-GDM commented 3 years ago

Hello @fixer-m , I'm using Snowflake.Client 0.3.5, My snowflake host look like this: test_test.us-east-1.testlink.snowflakecomputing.com I have checked my account name and region parameters and the snowflake is able to connect when I'm using snowflake connector in python with same values. but not sure why it is not able to connect with Snowflake nugets and Snowflake client nugets.

fixer-m commented 3 years ago

@PiyushJoshi-GDM, What is a testlink in your SF hostname? This is not valid cloud provider, see this table: https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#locator-formats-by-cloud-platform-and-region If your region is us-east-1, then you don't need to specify cloud provider at all, so I guess your SF hostname should look like this: test_test.us-east-1.snowflakecomputing.com

PiyushJoshi-GDM commented 3 years ago

@PiyushJoshi-GDM, What is a testlink in your SF hostname? This is not valid cloud provider, see this table: https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#locator-formats-by-cloud-platform-and-region If your region is us-east-1, then you don't need to specify cloud provider at all, so I guess your SF hostname should look like this: test_test.us-east-1.snowflakecomputing.com

Hello @fixer-m testlink is some private link which I cannot reveal here sorry, also regarding the SF hostname it is correct because same hostname is working fine in python with snowflake's provided connector.

PiyushJoshi-GDM commented 3 years ago

@fixer-m Now with the help of snowflake client it gives me this error: Exception=Athentication failed. Message: IP 3.209.181.87 is not allowed to access Snowflake. Contact your local security administrator. stack= at Snowflake.Client.SnowflakeClient.AuthenticateAsync(AuthInfo authInfo, SessionInfo sessionInfo). This error I'm getting on AWS lambda but same code is able to connect and working fine in local machine.

fixer-m commented 2 years ago

@PiyushJoshi-GDM, Hi! I've just noticed that I never really responded to your last messages here - sorry.

It looks like that you are using some SF feature (like PrivateLink) that requires to use special account identifier. In case of PrivateLink, it should contain privatelink part, here is SF docs about this. So full hostname should look like xy12345.us-west-2.privatelink.snowflakecomputing.com.

If that's the case, this can be easily achieved by passing full URL in client settings like this:

var urlInfo = new UrlInfo("xy12345.us-west-2.privatelink.snowflakecomputing.com");
var settings = new SnowflakeClientSettings(new AuthInfo("user", "pw", "account"), null, urlInfo);
var snowflakeClient = new SnowflakeClient(settings);

Your last error message indicates that your IP is not allowed to make requests in SF, I believe this is not related to the client itself. If you still have connection issue - feel free to create new issue, since this one is closed.