How to resolve ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY for self hosted signalR? - ssl

I'm trying to host a SignalR hub in a .NET Core 3.1 Windows Service, and when my client begins negotiation it fails with the response net::ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY
My SSL certificate is successfully loaded, and it checks out as valid in browser on port 443, but when browsing to my alternate port (randomly selected 12457) the browser does not consider it valid
If I switch down to HTTP1, I get a 405 I suspect from incompatibility with the client (microsoft/angular).
Here's how I'm configuring with my SSL certificate
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls(configuration.GetValue<string>("ListenerEndpoint"));
webBuilder.UseKestrel(options =>
{
options.Listen(IPAddress.Any, 12457, listenOptions =>
{
listenOptions.UseHttps(options =>
{
var certificateStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine, OpenFlags.ReadOnly);
var certificates = certificateStore.Certificates.Find(X509FindType.FindByThumbprint, "<thumbprint>", true);
var certificate = certificates[0];
options.ServerCertificate = certificate;
});
});
});
});
I've followed the netsh command to expose the cert on this port per: https://weblog.west-wind.com/posts/2013/Sep/23/Hosting-SignalR-under-SSLhttps#:~:text=Even%20if%20your%20self-hosted%20SignalR%20application%20doesn%27t%20explicitly,that%20will%20reject%20mixed%20content%20on%20SSL%20pages. without a positive effect

Related

SignalR .Net client cannot connect with https

I am using SignalR Core 2.4.1.0.
This is an Owin project, self-hosted.
My configuration:
public void Configuration(IAppBuilder app)
{
app.Use(async (ctx, next) =>
{
Console.WriteLine($"Incoming: {ctx.Request.Path}");
await next();
});
app.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration();
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(10);
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
I am able to connect to https://localhost:9999/signalr/hubs from a web browser fine.
I am also able to connect to SignalR when not using https. (after removing the urlacl )
I also have tried adding a middleware before the SignalR to see the incoming request.
With http the middleware shows the Request and path.
With https the middleware shows the Request from the web browser but never shows any request from the client.
The client just changes states from connecting to disconnected with not exceptions.
My client for testing is .Net console application:
var hub = new HubConnection("https://localhost:9999");
var hubProxy = hub.CreateHubProxy("MyHUB");
hub.Error += (e) =>
{
};
hub.StateChanged += (s) =>
{
Console.WriteLine(s.NewState.ToString());
};
hub.Start();
Console.ReadLine();
I've used SignalR before but this is my first time trying to implement ssl.
In summary, .Net client will connect via http but not https.
Browser can connect to the JS library over https but I haven't tried using the JS library yet.
T.I.A.

Swashbuckle using port 80 for https when run behind reverse proxy

I have a .net core api documented with swagger/swashbuckle.
When running the swagger ui on localhost on url https://localhost:44390/ the "Try it out" works fine.
We have the same solution in an App service in Azure with an Azure Front Door acting as reverse proxy. Front Door only accepts https traffic and only forwards https traffic. Front door domain is widget.example.com and App service is widget-test-app.azurewebsites.net. When running the swagger ui in Azure using the url https://widget.example.com/api/index.html there are two differences compared to running in localhost:
The swagger ui is showing a Servers -heading and a dropdown
The swagger ui is showing the server url as https://widget.example.com:80
I added an endpoint in the api with the following code
return $"Host {HttpContext.Request.Host.Host} Port {HttpContext.Request.Host.Port} Https {HttpContext.Request.IsHttps}";
When requesting https://widget.example.com/api/v1/test/url it returns
Host widget-test-app.azurewebsites.net Port Https True
This is completely ok since Front door is changing the host header. Port is empty, though.
Summary: Swagger ui is showing the correct domain in the Servers -dropdown but the port number is wrong. How can I get it to either omit the port number if it's 80 or 443, or add it correctly?
Update: The problem is in the swagger.json file which behind the reverse proxy includes a servers element
"servers": [{
"url": "https://widget.example.com:80"
}]
Startup.ConfigureServices
services.AddApiVersioning(options => {
options.Conventions.Add(new VersionByNamespaceConvention());
});
services.AddVersionedApiExplorer(o => {
o.GroupNameFormat = "'v'VVV";
o.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Widget backend v1", Version = "v1"
});
c.SwaggerDoc("v2", new OpenApiInfo {
Title = "Widget backend v2", Version = "v2"
});
c.EnableAnnotations();
c.AddEnumsWithValuesFixFilters();
var xmlFile = $ "{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
Startup.Configure
app.UseSwagger(options => {
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
});
app.UseSwaggerUI(options => {
foreach(var description in provider.ApiVersionDescriptions) {
options.SwaggerEndpoint($ "/api/swagger/{description.GroupName}/swagger.json", "widget backend " + description.GroupName);
}
options.RoutePrefix = "api";
});
To fix this I cleared the Servers -list. Here is my code:
app.UseSwagger(options =>
{
options.RouteTemplate = "/api/swagger/{documentname}/swagger.json";
options.PreSerializeFilters.Add((swagger, httpReq) =>
{
//Clear servers -element in swagger.json because it got the wrong port when hosted behind reverse proxy
swagger.Servers.Clear();
});
});
The solution (ok, a - mine - solution :)) is to configure forward headers in Startup.
services.Configure<ForwardHeadersOptions>(options =>
{
options.ForwardHeaders = ForwardHeaders.All; // For, Proto and Host
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Doing this, any URL generation in the app (behind reverse proxy) should respect the port-forwarding value. According to documentation known networks should be specified (taken from docs):
Only allow trusted proxies and networks to forward headers. Otherwise, IP spoofing attacks are possible.
See ASP.NET documentation for more details.

Enabling TLS1.2 or TLS1.3 in c# asp.net core 3.1

I am building a web app using asp.net core 3.1.
I want to enable TLS1.2 (or TLS1.3 if it works and is backward compatible.)
I have a web site running under IIS Express that is failing the SSL certificate.
The console shows the following error:
I followed some instructions and I thought I could solve the problem by executing the following code in CreateHostBuilder in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(s => {
s.ConfigureHttpsDefaults(k =>
{
k.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
});
}).UseStartup<Startup>();
});
I have run the application and it is still failing with the same error.
I am also running an implementation of IdentityServer4 on my local machine. That does not seem to have the same problem.
The identityserver4 site is secure.
How do I force my site to use TLS1.2 (or later)?
You can set the supported TSL protocols here:
webBuilder.UseKestrel((context, serverOptions) =>
{
serverOptions.AddServerHeader = false;
serverOptions.Listen(IPAddress.Any, 80);
serverOptions.Listen(IPAddress.Any, 443,
options =>
{
var cert = ...Load TLS certificate;
options.UseHttps(serverCertificate: cert, configureOptions: httpsOptions =>
{
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
});
});
});
See also
https://github.com/dotnet/aspnetcore/issues/22563
https://karthiktechblog.com/aspnetcore/how-to-use-tls-1-2-in-asp-net-core-2-0-and-above

API using SignalR with IdentityServer4 hangs on 3+ connections

My scenario is I have an API that implements SignalR and IdentityServer4. My client is a Vuejs SPA served from a .net-core app. After the vue client has the access_token I initiate the signalr connection and save it inside vuex store.
This is how I setup my connection:
var connection = new signalR.HubConnectionBuilder()
.withUrl(http://example.com + '/notifyHub',
{
accessTokenFactory: () => { return token }
}
).build();
connection.start()
.then(() => {
window.console.log("signalR connection successful")
})
.catch(function (err) {
return window.console.error(err.toString());
});
This is my API configuration of SignalR with IS4 Authentication
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = $"{_config["Server"]}";
options.RequireHttpsMetadata = _env.IsProduction() ? true : false;
options.ApiName = "api";
options.ApiSecret = "secret";
options.TokenRetriever = new Func<HttpRequest, string>(req =>
{
var fromHeader = TokenRetrieval.FromAuthorizationHeader();
var fromQuery = TokenRetrieval.FromQueryString();
return fromHeader(req) ?? fromQuery(req);
});
});
services.AddSignalR();
//sub claim is used from the token for individual users
services.AddSingleton<IUserIdProvider, UserProvider>();
And application
app.UseWebSockets();
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<NotificationHub>("/notifyHub");
});
The client always establishes a successful connection, and all the live notifications work as expected.
Now to test the functionality I launch 2 clients, 1 from Chrome and 1 from Firefox, while the two clients are connected everything works.
As soon as try to connect a 3rd client (using Chrome Incognito or IE Edge), the signalr connection is successful but the API hangs for all 3 clients. If I refresh any of the clients the other 2 will resume working, if close one of the clients the other two resume working fine.
There is no valuable info in the logs on the client or the api side to indicate to what is happening.
My question is what can cause this issue and how to do I investigate?
Is this an issue of launching multiple clients from the same host, and will this occurs in production will it just block the single host launching the clients or all of the hosts?
EDIT
I changed from localhost to local IP 192...* to try connect from my mobile browser see if that changes anything, same thing happened, after 3rd client connects the API hangs, If I don't connect to the signalR hub by omitting connection.start() I can launch as many clients as I can without breaking.
I am hosting on local IIS at the moment and not IIS express.

Is there Any Alternative For X509Certificate2UI in ASP.net Core?

X509Certificate2Collection sel = X509Certificate2UI.SelectFromCollection(
Filteredcollection,
"Certificates",
"Select a Certificate to sign",
X509SelectionFlag.SingleSelection
);
X509Certificate2UI is giving an error at this point in asp.net core
No. X509CertificateUI is meant to pop up a selection UI on the Windows Desktop. For the web the selection process is enforced by the browser, not by any code on the server.
You must configure your web site to only accept certificates, how you do that depends on the hosting software.
For Kestrel it's in code;
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
{
ServerCertificate = /* Your HTTPS Certificate */,
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
ClientCertificateValidation = /* Validator */
});
});
})
.Build();
For IIS it's through the UI,
Select your Site in the Connections tab.
Double click the SSL Settings in the Features View window.
Check the Require SSL Check Box and select the Require radio button under Client Certificates.