MassTransit 3 with RabbitMQ on IIS fails to authenticate - c#

In the spirit of full disclosure, I'm uncertain whether this is a MassTransit/RabbitMQ issue or an IIS authentication/authorization issue, but now that you've been warned, here goes. I have an ASP.NET MVC web application on a web server. It's using the MassTransit.RabbitMQ 3.4 NuGet package to try to establish a connection to an application server with Rabbit 3.6.5 / Erlang 18.0 installed. When the connection is trying to establish, IIS is throwing a 401.2:
HTTP Error 401.2 - Unauthorized
You are not authorized to view this page due to invalid authentication headers.
I've disabled security on the app server, so my connection code from the web app looks like this:
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
if (virtualHost == "/")
{
virtualHost = string.Empty;
}
cfg.Host(new Uri($"rabbitmq://{hostName}/{virtualHost}"), h =>
{
h.Username("qatemp");
h.Password("qatemp");
});
});
I have set up the "qatemp" user on the app server and granted full access to all virtual hosts. Note that I DO have a working C# console app that can successfully connect to the app server via the same code as above, so hence my concern that this is an IIS-specific issue. Can anyone shed any light on this? IIS is set up for windows authentication and that part seems to be working ok...I think.
Thanks,
Andy

Related

Astra Datastax - Asp.net core - Secure Connect Bundle (Zip file) working in local but not when published to azure app service

Am a new starter to Astra Datastax, Apache Cassandra database. We have created a simple asp.net core API to consume the database from Astra DataStax using a secure connect bundle(Zip file with certs and other information). It is working in the local as expected, however, when publishing to azure app service it is not working, but throw's the below error.
An unhandled exception was thrown by the application. Exception: System.AggregateException: One or more errors occurred. (There was an error fetching the metadata information from the Cloud Metadata Service (https://yourdb.db.astra.datastax.com:31977/metadata). Please make sure your cluster is not parked or terminated. See inner exception for more details.) ---> Cassandra.NoHostAvailableException: There was an error fetching the metadata information from the Cloud Metadata Service
we have checked the DB which is working(Not parked or terminated) and no issues when connecting from local.
Here is the code we are using to connect to the DB to establish a session.
private async Task<ISession> ConnectToAstra(string username, string password, string keyspace, string secureConnectBundlePath)
{
var session = await Cluster.Builder()
.WithCloudSecureConnectionBundle(secureConnectBundlePath)
.WithCredentials(username, password)
.WithQueryOptions(new QueryOptions().SetConsistencyLevel(ConsistencyLevel.LocalQuorum))
.Build()
.ConnectAsync(keyspace);
return session;
}
Note: We have validated the path of the bundle after publishing in Azure App service and also tried moving the Secure bundle to wwwroot folder as well, but no luck.
Any help/guidance will help us to move forward.
Cheers!!
The error you posted indicates that your app is not able to reach any of the Astra nodes so it couldn't get the cluster metadata such as schema and topology.
This is most likely a network connectivity issue with your app and your Astra instance. Note that VPC peering is only available on Classic Astra databases and it is not available for dev and test databases.
If you provide more info on what you mean by "no issues when connecting from local", it will give us additional insight and I'll be happy to update my answer. Cheers!

SSL Error "The message received was unexpected or badly formatted" for a .NET application on one specific machine only

I have a .NET Core 3.1 C# application which is calling an API via HTTPS (and presenting its public key as part of getting the token as that certificate is later used to decrypt information sent back separately). On just about all our machines, it is working, but on one Windows 8.1 machine, we get the following series of exceptions when we try to initially connect for an authentication token:
The SSL connection could not be established, see inner exception.
---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
---> System.ComponentModel.Win32Exception (0x80090326): The message received was unexpected or badly formatted.
The exception is thrown from System.Net.Http.HttpClient.FinishSendAsyncBuffered so I suspect it is happening at the HTTPS level and our certificate stuff is not really relevant here anyway.
Our code to get the token looks like this:
The constructor for the auth service:
public XXXXAuthService(IXXDbService dbService, XXXXApiConfig config)
{
_dbService = dbService;
_config = config;
// try forcing TLS1.2 for SSL connection exceptions thrown in some operating environments
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_httpClient = new HttpClient {BaseAddress = new Uri(config.BaseUrl)};
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
Code to get the auth token:
private async Task<string> GetXXXXBearerToken(string userId, DateTime creationTime)
{
var token = await GenerateProviderJwtForXXXX(userId, creationTime);
var kvp = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange"),
new KeyValuePair<string, string>("subject_token", token),
new KeyValuePair<string, string>("subject_token_type", "urn:ietf:params:oauth:token-type:jwt")
};
var data = new FormUrlEncodedContent(kvp);
var publicKey = await GetXXXXPublicKey();
_httpClient.DefaultRequestHeaders.Remove("X-XXXX-Public-Cert");
_httpClient.DefaultRequestHeaders.Add("X-XXXX-Public-Cert", publicKey);
var response = await _httpClient.PostAsync("Identity/token", data);
if (!response.IsSuccessStatusCode)
throw new Exception("XXXX Token Server Error: " + response.ReasonPhrase);
var result = await response.Content.ReadAsStringAsync();
var authResponse = JsonConvert.DeserializeObject<OAuthResponse>(result);
if (!string.IsNullOrEmpty(authResponse.access_token))
return authResponse.access_token;
System.Diagnostics.Trace.WriteLine("Token Exchange Result: " + result);
if (!string.IsNullOrEmpty(authResponse.error))
{
var outcome = new XXX.XXXX.Model.OperationOutcome();
outcome.Issue.Add(new XXX.XXXX.Model.OperationOutcome.IssueComponent()
{
//some code to throw an error is here
}
throw new XXX.XXXX.Rest.XXXXOperationException("Bearer Token Exchange failed", response.StatusCode);
}
Unfortunately none of the existing questions/advice anywhere on Stack Overflow, or the rest of the web, for this particular error seems to have helped. They are primarily about version discrepancies between client and server which seems not to be the case here as I am forcing TLS 1.2 (which is active and enabled on the failing machine).
Interestingly, I can visit the server URL in a browser via HTTPS just fine, which suggests there is something about my code that is the problem rather than the machine, but it works everywhere else.
I have confirmed that:
The certificate I am using to authenticate the connection on the machine is valid and has a chain of trust (though as above I don't think we are getting that far as the TLS connection itself is failing)
The server we are calling supports TLS 1.2 (by forcing it)
I can get to the website for the URL independently via the browser
Is there something I need to do either in the code or on the machine to get this call to work everywhere?
Things I have tried to resolve the issue
Installing all Windows 8.1 updates to present day
Forcing TLS 1.2 in the code (see above code sample)
Limiting VM to TLS 1.2 only
I might be able to at least point you in the right direction…
Same Symptoms
I had a .NET Core 3.1 web app running on IIS (Windows Server 2012 R2) that got the exact same error and stacktrace when it tried to connect to another server using TLS 1.2. I also had the symptom where I could connect with the browser (Chrome), but not with the app. (Would have been interesting to see if Internet Explorer browser worked though.)
Root Cause
The TLS handshake was failing because the two servers were unable to agree on a common cipher suite. (Using Wireshark, I discovered that when my app tried to connect it provided a more limited set of cipher suites than when the Chrome browser made the call.)
Solution
In my case, I used IIS Crypto (a small free tool: https://www.nartac.com/Products/IISCrypto/) to enable additional cipher suites on my web app's server. I downloaded and ran IIS Crypto, checkmarked additional cipher suites on its Cipher Suites tab, and then restarted the machine.
One of the new cipher suites worked with my app and the destination server, so the TLS handshake was successful and the error was resolved.
One quick caveat: Some cipher suites are more secure than others, so you'll want to read up on best practices.
Addendum
If you want to further diagnose the failure, I'd recommend installing Wireshark (another free tool: https://www.wireshark.org/#download) on the machine with your .NET Core app. If a TLS Handshake Failure is the issue, you will see a message like: Alert (Level: Fatal, Description: Handshake Failure)
This primer on wireshark output helped me:
https://blog.catchpoint.com/2017/05/12/dissecting-tls-using-wireshark/
I faced a simular issue, and in order to help others here's what I concluded:
Sucessfully executing this code doesn't mean that your application supports the specified protocol version, and the "SSL Error" can still occur later on when trying to establish a connection:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
In my case I was trying to force Tls13 and found out that my app configuration didn't actually support it:
net core 3.0 running on a Windows Server Datacenter 2019, version 1809
So I had to change my configuration to the following which provides support for the protocol version I needed:
Net framework 5.0 on a Windows Server Datacenter 2022, OS build 20348.288
I was trying to connect to an endpoint that suddenly dropped Tls 1.2 support (not sure why) and from then on only accepted Tls 1.3.

OData, DataServiceClientException: Unauthorized

I'm developing (as a totally no-C#-guy) a web app which communicates with an external data source over OData (server B, some windows 2k12 machine).
The web app runs (is about to run) on an IIS (server A, another windows 2k12 machine), the OData source is a Dynamics NAV 2015 service (the first mentioned windows 2k12 machine, server B).
I'm developing it in VS2013 and if I'm running this locally (meaning: without publishing it, only running it within the local express IIS) it works without any problems at all.
But as soon as I publish it to the target IIS (server A) I'm getting:
An error occurred while processing this request. ---> >System.Data.Services.Client.DataServiceClientException: Unauthorized
at System.Data.Services.Client.QueryResult.ExecuteQuery()
at System.Data.Services.Client.DataServiceRequest.Execute[TElement]>(DataServiceContext context, QueryComponents queryComponents)
--- End of inner exception stack trace ---
This is esentially the piece of code which deals with the OData call:
ODataOrders.NAV odata = new ODataOrders.NAV(new Uri(serviceUri));
System.Net.NetworkCredential nc = new
System.Net.NetworkCredential(_oDataUsername, _oDataUserPassword, _oDataDomain);
odata.Credentials = nc;
DataServiceQuery<ODataOrders.Orders> query =
odata.CreateQuery<ODataOrders.Orders>("Orders");
orderList = query.Execute().ToList(); // "Unauthorized" is being thrown here
It looks like the credentials are being sent/accepted if running from local IIS. And if running from the target (production) server A, the credentials are being somehow lost/overwritten? I really don't know now...
Server A and Server B are in the same domain. My development environment isn't - I can even connect from home through VPN to my work domain and launch my local IIS and still be able to get that request done (the request goes through my local IIS and reaches the Dynamics NAV service so the data is being actually fetched).
Any help would be appreciated...
EDIT
It's Dynamics NAV 2015 CU11.
After some serious hours of reading, programming and trying I've stumbled upon a post here which gave me an idea.
And guess what? It worked - somehow...
Instead of:
System.Net.NetworkCredential(_oDataUsername, _oDataUserPassword, _oDataDomain);
I submitted only:
System.Net.NetworkCredential(_oDataUsername, _oDataUserPassword);
I turns out somehow the domain caused the call not to be authorized (401 error).

Dotnet core web api running inside docker fails to authenticate when consuming external WCF service

I'm building a RESTful API using dotnet core 1.1.2.
A big part of this api requires making requests to an external WCF service. These requests are authenticated using windows-based authentication with username, password and domain.
I'm currently in the process of making the api production ready and I wanted to try dockerizing it.
The problem I'm having is that authentication fails towards this third party WCF service as soon as it's called from within the docker container. Running the API using the dotnet runtime works from both windows and mac and the service gets authenticated as it should.
I consume the WCF service using the Connect wcf service feature of Visual studio 2017 and then modifying the endpoint binding with the correct authentication
mode.
public ServiceSoapClient(EndpointConfiguration endpointConfiguration, string username, string password, string domain) :
base(ServiceSoapClient.GetBindingForEndpoint(endpointConfiguration), ServiceSoapClient.GetEndpointAddress(endpointConfiguration))
{
this.ChannelFactory.Credentials.Windows.ClientCredential.UserName = username;
this.ChannelFactory.Credentials.Windows.ClientCredential.Password = password;
this.ChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
this.Endpoint.Name = endpointConfiguration.ToString();
ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
}
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.ServiceSoap))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true;
result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
result.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
I've tried both Ntml and Windows as ClientCredentialType.
I've verified that the authentication credentials don't get messed up when transferring the api to the docker container by hard coding the credentials inside the app, then running it using the normal dotnet runtime to verify that it works. Finally building the docker image with exactly the same published app and running it again. When exactly the same app is running inside docker it fails to authenticate.
The output from the application is:
The HTTP request is unauthorized with client authentication scheme ‘Negotiate’. The authentication header received from the server was ‘Negotiate, NTLM’.
Which is the same output as if when I'm using incorrect credentials.
I'm wondering if this could be somehow related to how networking works with docker and if the api can't negotiate with the WCF service since it's bridging through the docker host.
If anyone more knowledgeable with docker or WCF consumtion inside dotnet core might have some insight it would be very helpful.
Best regards, Linus.
For anyone experiencing the same issue this is due to the way kerberos is configured on non-windows platforms. This has nothing to do with docker per say but rather running as a linux-based container.
The solution is to either switch your platform to windows or correctly configure kerberos authentication on your platform. This is discussed in more detail in the following github issues:
https://github.com/dotnet/wcf/issues/2641
and
https://github.com/dotnet/corefx/issues/9533

ASP core HttpClient.Get From container to localhost site is failing

Given:
Windows 10 PC With Docker (linux container format)
Local website in IIS accessable on the bare machine (no container) http://localhost/foo
ASP Core DockerContainer which wants to access HTTP GET http://localhost/foo
Problem:
All requests to http://localhost are failing
var client = new HttpClient();
var result = await client.GetAsync("http://localhost/foo");
Then I get
System.Net.Http.HttpRequestException: An error
occurred while sending the request. --->
System.Net.Http.CurlException: Couldn't connect to server
at System.Net.Http.CurlHandler.ThrowIfCURLEError(CURLcode error)
at System.Net.Http.CurlHandler.MultiAge...
Is this expected? I thought access to localhost from inside container should work.
Also other protocols like mongodb are working with localhost. So it seems to be specific to http connections form asp core?
One question before: I suppose that the server you are trying to reach is in the same container, right?
If yes:
You should be able to connect from within the container using localhost.
The problem might be that this container doesn't expose the port 80 internally. Can you check the port internal port with "docker ps"?
If no:
In that case it means that localhost is used to connect to the host. My understanding is that it should not work.

Categories

Resources