Make Http.sys work with Windows Authentication - c#

I try to make Window authentication work with Kestrel by following the links:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys?view=aspnetcore-3.0#how-to-use-httpsys
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio#httpsys
Here is the code.
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.AllowSynchronousIO = true;
options.Authentication.Schemes = AuthenticationSchemes.None;
options.Authentication.AllowAnonymous = true;
options.MaxConnections = null;
options.MaxRequestBodySize = 30000000;
options.UrlPrefixes.Add("https://localhost:8080");
});
webBuilder.UseStartup<Startup>()
.UseHttpSys(options =>
{
options.Authentication.Schemes =
AuthenticationSchemes.NTLM |
AuthenticationSchemes.Negotiate;
options.Authentication.AllowAnonymous = false;
});
});
However, browse https://localhost:8080 shows the following error message (Edge)?
Can’t connect securely to this page
This might be because the site uses outdated or unsafe TLS security settings. If this keeps happening, try contacting the website’s owner.

It's because you didn't have development certification installed on your machine.
try this:
dotnet dev-certs https --trust

Related

Detected a TLS handshake to an endpoint that does not have TLS enabled

I have an ASP.NET Core Web API running in a Linux Docker container orchestrated using Docker compose. When I try to access the Swagger endpoint of the Web API on HTTPS, the browser cannot reach the endpoint, and I see the following error in the Web API logs.
Detected a TLS handshake to an endpoint that does not have TLS enabled
After reading this topic, I "fixed" the issue by adding the following to my configuration. However, I am not sure why this is needed. It seems overkill and has too much complexity to enable HTTPS, so I am wondering if I am missing something.
builder.WebHost.ConfigureKestrel(options =>
{
options.Listen(
address: IPAddress.Any,
port: 443,
configure: listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps(new TlsHandshakeCallbackOptions
{
OnConnection = context =>
{
if (string.Equals(context.ClientHelloInfo.ServerName, "localhost", StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.ClientHelloInfo.ServerName, "host.docker.internal", StringComparison.OrdinalIgnoreCase))
{
// Different TLS requirements for this host
context.AllowDelayedClientCertificateNegotation = true;
return new ValueTask<SslServerAuthenticationOptions>(
new SslServerAuthenticationOptions
{
ServerCertificate = new X509Certificate2("/root/.aspnet/https/webapi.pfx", "password")
});
}
return new ValueTask<SslServerAuthenticationOptions>(
new SslServerAuthenticationOptions
{
ServerCertificate = new X509Certificate2("/root/.aspnet/https/webapi.pfx", "password")
});
}
});
});
});

Kestrel isn't "using" pfx file for https

My local development is wanting to install a localhost ssl certificate even though I have specified a pfx I want it to use.
core API .NET 5.0
My understanding was that kestrel was used by default so this is what I have done.
In my hosts file I setup a FQDN.
127.0.0.1 myapi.mysite.com
In my program.cs I added ConfigureKestrel
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.ConfigureKestrel(options =>
{
options.Listen(IPAddress.Loopback, 44394, listenOptions =>
{
var serverCertificate = LoadCertificate();
listenOptions.UseHttps(serverCertificate); // <- Configures SSL
});
});
});
private static X509Certificate2 LoadCertificate()
{
var assembly = typeof(Startup).GetTypeInfo().Assembly;
var embeddedFileProvider = new EmbeddedFileProvider(assembly, "My.API");
var certificateFileInfo = embeddedFileProvider.GetFileInfo("wildcard_mydomain_com.pfx");
using (var certificateStream = certificateFileInfo.CreateReadStream())
{
byte[] certificatePayload;
using (var memoryStream = new MemoryStream())
{
certificateStream.CopyTo(memoryStream);
certificatePayload = memoryStream.ToArray();
}
return new X509Certificate2(certificatePayload, "password");
}
}
I do not get any errors but I don't get the custom domain certificate presented to the browser either. The browser tell me I am unsecure and wants me to accept the localhost cert....which I don't want to accept. I want my custom domain cert to be used.
Am I not understanding how the certs work in a browser?
Doesn't the web server present the cert to the browser and the browser checks to see if it is issued by a legit CA?
Am I loading my custom cert but not "attaching" it to the development server to use?

how to use ssl certificates with gRPC and ASP Net Core 3.0?

I am rtying to configure the service to use a SSL certificate. I have read this post:
How to enable server side SSL for gRPC?
I guess this is the main code:
var cacert = File.ReadAllText(#"ca.crt");
var servercert = File.ReadAllText(#"server.crt");
var serverkey = File.ReadAllText(#"server.key");
var keypair = new KeyCertificatePair(servercert, serverkey);
var sslCredentials = new SslServerCredentials(new List<KeyCertificatePair>() { keypair }, cacert, false);
var server = new Server
{
Services = { GrpcTest.BindService(new GrpcTestImpl(writeToDisk)) },
Ports = { new ServerPort("0.0.0.0", 555, sslCredentials) }
};
server.Start();
The problem is that in my case, I don't start the service in this way, I am using kestrel, and the code is this:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
System.Net.IPAddress miAddress = System.Net.IPAddress.Parse("x.x.x.x");
//options.Listen(miAddress, 5001, o => o.Protocols = HttpProtocols.Http2);
options.Listen(miAddress, 5001, l =>
{
l.Protocols = HttpProtocols.Http2;
l.UseHttps();
});
});
webBuilder.UseStartup<Startup>();
});
In this case, I don't have access to SslCredentials, so I can't create a new one.
How could I configure my ssl certificate using kestrel?
Thanks.
The post you linked to is for Grpc.Core, the grpc-dotnet implementation is configured differently.
This documentation and example should help:
https://github.com/grpc/grpc-dotnet/blob/dd72d6a38ab2984fd224aa8ed53686dc0153b9da/testassets/InteropTestsWebsite/Program.cs#L55
https://learn.microsoft.com/en-us/aspnet/core/grpc/authn-and-authz?view=aspnetcore-3.1
(in another words, you can configure the certificates on the server side exactly the same way as you would for any other HTTP/2 server - there's nothing grpc specific in configuring the secure connections in ASP.NET Core).
It looks like you mistake authentication by certificates for SSL-data-encryption. In case you just want to encrypt the data channel, good practice is to use Kestrel:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(builder =>
{
builder.ConfigureKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5005, configure => { configure.UseHttps(); configure.Protocols = HttpProtocols.Http2; });
});
});
The call to UseHttps() uses the internal ASP.NET Core’s trusted development certificate.
If you want to provide one yourself, use i.e. (or the other overloads):
public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate)

How to enable certificate authentication in App-Service with .NET Core 3.0?

I am implementing client authentication with certificates to access my API. I followed the documentation from Microsoft https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-3.0
My problem is, that I never receive an client cert. I tested it locally and on Azure as well.
I have tried several variations, but with same result.
Excerpst from my Startup class:
// Register Certificate Authentication
//services.AddCertificateForwarding(options => options.CertificateHeader = "X-ARR-ClientCert");
services.AddCertificateForwarding(options =>
{
options.CertificateHeader = "X-ARR-ClientCert";
options.HeaderConverter = (headerValue) =>
{
X509Certificate2 clientCertificate = null;
if (!string.IsNullOrWhiteSpace(headerValue))
{
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(headerValue);
clientCertificate = new X509Certificate2(bytes);
}
return clientCertificate;
};
});
services.AddSingleton<CertificateValidationService>();
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService =
context.HttpContext.RequestServices.GetService<CertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Fail("invalid cert");
return Task.CompletedTask;
}
};
});
and
app.UseCertificateForwarding();
app.UseAuthentication();
in Program.cs I added this config:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(opt =>
{
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});
});
Diagnostic
If you enable trace logs in console - you could see additional info why this certificate forwarding middleware won't work.
One important thing about CertificateForwarding middleware is it only works with HTTPS schema. So you should use one of two options here:
Use HTTPS on application level (Enable HTTPS in Kestrel)
This way is a plenty easy so it should not be a problem for you to enable it. Just follow official instructions.
Use schema forwarding
If you use proxy server, like NGINX before application, that works using HTTPS and proxying requests to your application through HTTP - you could face issue here that middeware methods not being called.
To fix that, there is one thing you should add is to proxy schema through NGINX this way:
# nginx.conf
proxy_set_header X-Forwarded-Proto $scheme;
// Configure startup
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedProto;
});
// and use it
app.UseForwardedHeaders(); // Place it before UseCertificateForwarding()
This way, when your request goes to NGINX through HTTPS nginx will proxy it to your application through HTTP but with X-Forwarded-Proto header that contains HTTPS schema. So your dotnet application will launch CertificateForwarding middleware correctly.
Remove the header converter when calling AddCertificateForwarding

Why do I get ERR_EMPTY_RESPONSE running nopCommerce 4 in Visual Studio with SSL?

When I try to run nopCommerce 4.0 locally in VisualStudio with SSL, I get the error ERR_EMPTY_RESPONSE. Running without HTTPS works fine. I am testing integration with an external login and I need to run HTTPS locally to avoid mixed content problems.
I know there are several SO posts about this, but they all lead me to the same place. My latest attempt is based on this article.
In my Startup.cs, I have:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc(
options =>
{
options.SslPort = 55391;
options.Filters.Add(new RequireHttpsAttribute());
}
);
services.AddAntiforgery(
options =>
{
options.Cookie.Name = "_af";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.HeaderName = "X-XSRF-TOKEN";
}
);
return services.ConfigureApplicationServices(Configuration);
}
In certificate.json, I have:
{
"certificateSettings": {
"filename": "localhost.pfx",
"password": "secretpassword"
}
}
In Program.cs, I have:
public static void Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.AddJsonFile("certificate.json", optional: true, reloadOnChange: true)
.AddJsonFile($"certificate.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.Build();
var certificateSettings = config.GetSection("certificateSettings");
string certificateFileName = certificateSettings.GetValue<string>("filename");
string certificatePassword = certificateSettings.GetValue<string>("password");
var certificate = new X509Certificate2(certificateFileName, certificatePassword);
var host = WebHost.CreateDefaultBuilder(args)
.UseKestrel(options =>
{
options.AddServerHeader = false;
options.Listen(IPAddress.Loopback, 55391, listenOptions =>
{
listenOptions.UseHttps(certificate);
});
options.Listen(IPAddress.Loopback, 55390);
})
.UseStartup<Startup>()
.CaptureStartupErrors(true)
.Build();
host.Run();
}
I have ASPNETCORE_HTTPS_PORT set to 55391 in my Nop.Web project settings.
When I run this, I get the error ERR_EMPTY_RESPONSE.
If I remove all of the HTTPS options and just go with the standard setup, everything works fine.
Edit: I have to run this project with the Nop.Web profile, so I can't run it with IIS Express and just get the SSL settings in the project properties.
Edit #2: I noticed that when I get this error, the browser is being redirected from https://localhost:55391 to http://localhost:55391 (no s). I'm not sure why.

Categories

Resources