Kestrel and https very slow - asp.net-core

I am using Rider from JetBrains and it made me realize that configuring https directly through Kestrel makes the requests to API very slow. I have the same behaviour if I just run
dotnet MyApi.dll
When I am using Visual Studio and running the API on IIS Express I don't have any performance issues.
This is how I configured Kestrel to listen on https
X509Certificate2 cert = null;
using (var store = new X509Store(StoreName.My))
{
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindBySubjectName, "localhost", false);
cert = certs[0];
}
WebHost.CreateDefaultBuilder(args).UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 44317, listenOptions =>
{
listenOptions.UseHttps(cert);
});
})
.UseStartup<Startup>()
.Build();
I tried to remove the https and just use http, and it is working as fast as with IIS Express. So it tells me that there is something wrong with the way I am configuring https.
Is there any configuration I am missing that is making the Kestrel slower when https is activated or is it simply better to run it on IIS Express ?

Related

How to resolve ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY for self hosted signalR?

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

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

Having certificate issues when calling Web API from Web App

I am developing a web api and a web app locally. I am having trouble calling the web api from the web app.
When I call it I keep getting the error: "The remote certificate is invalid according to the validation procedure."
Both apps are built with ASP.Net Core and are running on kestrel. The webapp is callable as https://mylibrary.com:5003 and the Web API is callable as https://api.mylibrary.com:5001.
How can I get them working together with valid certificates?
Edit: Come to realise that the issue is that the apps are using localhost certs by default. I want to be able to use my own self signed cert.
If someone can point me to somewhere that explains how to set up two apps to use a self-signed certificate in .net core web projects please do :)
If you need to work around the cert validation using HttpClient, you could do it by creating a HttpClientHandler and passing it to HttpClient as per Rohit Jangid's answer to The SSL connection could not be established
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
// Pass the handler to httpclient(from you are calling api)
HttpClient client = new HttpClient(clientHandler)
Avoid accidentally circumventing certificate validation in production by checking if it is in development environment:
HttpClient httpClient = new HttpClient();
if (env.IsDevelopment())
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, ssl) => { return true; };
httpClient = new HttpClient(clientHandler);
}
Inject information about webhostenvironment by injecting it in the handler/action:
public async Task OnGet([FromServices] IWebHostEnvironment env)
Please try to use RestSharp library to make the webapi request and set the cert validation to true. see here
or you can install the dotnet dev certs by executing dotnet dev-certs https --trust in a command promt or powershell

Expose my asp.net core web app over the internet

I want to expose my in-development asp.net core web app so that I can demo it etc. I use ngrok to do this, but this doesn't seem to work with asp.net core. I get a 502 bad gateway error
I'm tunnelling with this command:
./ngrok tcp 5001
My web host builder code in program.cs sets up kestrel as follows:
.UseKestrel(options =>
{
options.AddServerHeader = false;
options.Listen(IPAddress.Loopback, 5000);
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("localhost.pfx", "SOMETHING");
});
})
So I can hit my web app with localhost or my local IP address, but via ngrok it fails.
Does anyone know why this is?
For Ngrok, you can try this command:
./ngrok http https://127.0.0.1:5001
You can also try a shorthand of ./ngrok http 5001, but sometimes that doesn't work for me.