Configure ASP.NET Core Module to host on https instead of http for IdentityServer 4 - iis

The problem I have is when I try to host an implementation of IdentityServer4 on a IIS server that uses SSL. (Full SSL Strict)
When running my application on Kestrel alone with SSL activated it works fine and the IssuerUri and Discovery Endpoints for IdentityServer uses SSL binding. However when I host it behind ASP.NET Core Module it hosts it on http://localhost:{random port}, which in turn generates IssuerUri and Endpoints for Identityserver that are not https.
I have tried the following without success:
Made sure that I have a valid certificate on the IIS website for the
https binding and removed binding on port 80
Tried changing the environmentvariable ASPNETCORE_URLS in web.config
to point to a https address.
Tried rewrite and redirect rules in web.config.
Looked for settings on IISOptions (used by .UseIISIntegration())
in my startup class to bind to a specific url or change protocol.
Tried to find a similar settings like RequireSSL (IdentityServer 3) or
RequireHttpsMetadata in IdentityServer4.
Changed the IssuerUri in IdentityServer Options in startup class
hoping that it might also update the other endpoints.
I have probably missed something very obvious but right now I do not have a clue on what that might be.
Any help from the community would be greatly appreciated :-)
Program.cs code
public static void Main(string[] args)
{
Console.Title = "IdentityServer";
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("kestrelHosting.json", optional: true)
.AddCommandLine(args)
.Build();
var host = new WebHostBuilder()
.UseConfiguration(config)
.UseKestrel(options =>
{
// options.ThreadCount = 4;
options.NoDelay = true;
options.UseHttps("VismaCert.pfx", "Visma123");
//options.UseConnectionLogging();
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}

AspNetCoreModule is an SSL terminator, it will not communicate with Kestrel over HTTPS. What it does do is forward the original scheme via a header so you can use it when generating urls/links. There is a ForwardedHeaders middleware included by default with UseIISIntegration that will take these headers and apply them to the request fields. However, there are situations where the headers cannot be processed by the default settings. There are a bunch of references here: https://github.com/aspnet/Docs/issues/2384

Related

Generic domain part with fixed subdomain using Caddy and auto SSL?

I'd like to setup a Caddy server where the subdomain is static but the domain part is "wildcard", such as "api.*"
From my understanding of Caddy, the wildcard is possible for one part of the full domain (*.domain.com matches bar.domain.com but not foo.bar.domain.com).
Moreover, this configuration would automatically create a SSL certificates (which Caddy does in general, but I'm not sure here) for any new DNS entry that points to my server with a domain starting with "api.*".
The "*" here would be the domain directly, not any subdomain (it would work for api.domain.com, but not for api.foo.domain.com).
Is this something possible using a simple Caddy command (such as api.* { ... }, which I tried without luck), or does it need a more complex implementation?
Thank you for your help!
I found a working solution with the help of the Caddy Community.
Here's the code :
{
on_demand_tls {
ask https://static.site.com/domain/verify
interval 2m
burst 5
}
}
static.site.com {
...
}
:443 {
tls {
on_demand
}
// Your custom config, for instance:
reverse_proxy * ...
}
The nifty part is the tls { on_demand } part for your generic HTTPS, which will create a certificate automatically. But, this can be abused by anyone that points one of their DNS entry to your server.
So to avoid that, the Caddy community highly recommends you to set a on_demand_tls that will query an endpoint, and allow the SSL certificate to be created only if that endpoint returns true.
NOTE: The ask is a GET request that DO NOT FOLLOW redirects! Anything but a 200 status code will be considered a failure, even a 3xx!
The ask url will have the ?domain appended and will allow you to verify that domain against your logic, such as custom value in the domain like "starting by static.*", and verify that the domain exists in your database (for example).
If your URL already contains some query parameter, don't worry, Caddy is clever enough to add them. (https://static.site.com/domain/verify?some=query will become https://static.site.com/domain/verify?some=query&domain={domain}.
Caddy support https for the ask parameter, and that URL can also be external with no problems at all (no need for localhost or local server configuration).
I met the same problem, and after 1 day's stucking, here is my solution:
Assuming the site name is: site.com, and I want caddy handle these domains for me:
a.dot.site.com
b.dot.site.com
c.dot.site.com
a.eth.site.com
b.eth.site.com
c.eth.site.com
1.make sure you set SSL access available. e.g. via cloudflare:
2.set the A address pointing to your Caddy server's IP.
2.Caddy file should looks like:
# the key is: you have to list all the patterns for your multiple subdomains
*.site.com *.eth.site.com *.dot.site.com {
reverse_proxy 127.0.0.1:4567
log {
output file /var/log/access-wildcard-site.com.log
}
tls {
dns cloudflare <your cloud flare api key>
}
}

Configuring Azure ServiceFabric for development lifecycle - how to parameterize host name?

What's a good way to manage deploying code changes to Dev, Test, and Prod environments in Azure? The Azure / Service Fabric site provides an article for specifying port numbers using parameters under How-to guides - Manage application lifecycle (https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-how-to-specify-port-number-using-parameters), but I'm not sure how one manages host names - is there a host name related property that can be included in Publish Profile .xml file (e.g., Cloud.xml)?
Background: I'm migrating from a self hosted on premise WCF application running as a Windows Service and using WebHttpBinding with http and https endpoints (uses T4 config file templates to determine hostname and port number depending on the environment). I'm migrating this to an Azure ServiceFabric WcfCommunicationListener application (similar to the sample found here: https://github.com/loekd/ServiceFabric.WcfCalc)....
internal sealed class ServiceFabricWcfService : StatelessService
{
public ServiceFabricWcfService(StatelessServiceContext context) : base(context)
protected override IEnumerable<ServiceInstanceListener>
CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(CreateRestListener);
}
private ICommunicationListener CreateRestListener(StatelessServiceContext context)
{
var host = context.NodeContext.IPAddressOrFQDN;
var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
var port = endpointConfig.Port;
var scheme = endpointConfig.Protocol.ToString();
var uri = string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/webhost/", scheme, host, port);
var listener = new WcfCommunicationListener<IJsonService>(context, new JsonServicePerCall(), new WebHttpBinding(WebHttpSecurityMode.None), new EndpointAddress(uri));
var ep = listener.ServiceHost.Description.Endpoints.Last();
ep.Behaviors.Add(new WebHttpBehavior());
return listener;
}
}
As you can see, the host name is obtained from the StatelessServiceContext's NodeContext - is there a good way to set this up to target different host names for each environment? My clients need to be able to make http/https calls based on host name to determine which environment they connect to. Thanks!
I don't think that you can do that, since in the provided example host variable represents exact node on which service is running. You can reach it using cluster name if you open appropriate port, e.g. http://mycluster.eastus.cloudapp.azure.com:19081/MyApp/MyService

Is editing configuration cause iis recycle?

I saw that in asp.net older version, editing configuration cause recycle.
(so we implemented our own library for dynamic config to avoid recycling).
Now I'm writing new asp.net core app and I have not seen anywhere that there is recycle, if using built-in configuration (like:
.AddJsonFile("appsettings.json", optional: true, **reloadOnChange: true**)
So, can I use it without expecting any down time?
AFAIK, editing configuration does not cause IIS recycle. One of the reason maybe is that IIS is acting now merely as a reverse proxy and the application itself runs as a separate process using the Kestrel HTTP server (if you use Kestrel).
If you need to catch moment, when configuration has been changed, you can use Configuration Reload Token (Microsoft.Extensions.Configuration.ConfigurationReloadToken):
var config = builder.Build();
var token = config.GetReloadToken();
token.RegisterChangeCallback(_ =>
{
Console.WriteLine("Changed");
}, null);
But note, the token only fires once, so need have code in the callback to a change token, if needed.

How to deploy Asp.Net Core apps on Azure Service Fabric using subpaths sharing same port on the cluster

The Service Fabric samples like wordcount the web app listen on a port in a subpath like this:
http://localhost:8081/wordcount
The code for this configuration is: (See the file on GitHub https://github.com/Azure-Samples/service-fabric-dotnet-getting-started/blob/master/Services/WordCount/WordCount.WebService/WordCountWebService.cs)
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(initParams => new OwinCommunicationListener("wordcount", new Startup(), initParams))
};
}
With this configuration we can deploy other web apps on the same cluster using the same port (8081)
http://localhost:8081/wordcount
http://localhost:8081/app1
http://localhost:8081/app2
And so on.
But the Asp.Net Core project template is different and I don't know how to add the subpath on listener configuration.
The code below is what we have in the project template (Program.cs class WebHostingService):
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] { new ServiceInstanceListener(_ => this) };
}
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}";
_webHost = new WebHostBuilder().UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
The semantic is a bit different, but all ends up in the same point.
The problems is that even I add the subpath at the end of serverUrl it does't work and the web apps always responds on the root http://localhost:8081/
See how I've tried in the code snippet below:
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}/app1";
How to achieve the same result as "classic" web app using asp.net core?
The goal is to publish on azure on port 80 to let users with a better experience like:
http://mywebsite.com/app1
http://mywebsite.com/app2
Thank you a lot!
As #Vaclav said is necessary to change UseKestrel by UseWebListener.
But the problem is that WebListener binding to the address is different.
Look this thread to more details https://github.com/aspnet/Hosting/issues/749
Is necessary to use + instead of localhost or other machine names on the serverUrl.
So, change de template code from:
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://{FabricRuntime.GetNodeContext().IPAddressOrFQDN}:{endpoint.Port}/service1";
_webHost = new WebHostBuilder().UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
To
Task<string> ICommunicationListener.OpenAsync(CancellationToken cancellationToken)
{
var endpoint = FabricRuntime.GetActivationContext().GetEndpoint(_endpointName);
string serverUrl = $"{endpoint.Protocol}://+:{endpoint.Port}/service1";
_webHost = new WebHostBuilder().UseWebListener()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseUrls(serverUrl)
.Build();
_webHost.Start();
return Task.FromResult(serverUrl);
}
And it workd very well.
Kestrel doesn't support URL prefixes or port sharing between multiple applications. You have to use WebListener instead:
using Microsoft.AspNetCore.Hosting
...
_webHost = new WebHostBuilder().UseWebListener()
I've not done this yet, but is this GitHub repository useful?
https://github.com/weidazhao/Hosting
About The Sample
This sample demonstrates:
1.How ASP.NET Core can be used in a communication listener of stateless/stateful services. Today the scenario we've enabled is to host ASP.NET Core web application as a stateless service with Service Fabric. We wanted to light up the scenarios that people also can use ASP.NET Core as communication listeners in stateless services and stateful services, similar to what the OwinCommunicationListener does.
2.How to build an API gateway service to forward requests to multiple microservices behind it with the reusable and modular component. Service Fabric is a great platform for building microservices. The gateway middleware (Microsoft.ServiceFabric.AspNetCore.Gateway) is an attempt to provide a building block for people to easily implement the API gateway pattern of microservices on Service Fabric. There are a couple good articles elaborating the API gateway pattern, such as http://microservices.io/patterns/apigateway.html, http://www.infoq.com/articles/microservices-intro, etc. For more information about microservices, check out https://azure.microsoft.com/en-us/blog/microservices-an-application-revolution-powered-by-the-cloud/, http://martinfowler.com/articles/microservices.html.
#Nick Randell
With the sample approach is possible to run several Services on the same port using their names like:
http://localhost:20000/service1 <--- Svc in Application1
http://localhost:20000/service2 <--- Svc in Application1
This is possible because is there a Gateway service that maps the addresses service1 and service2 in the URI to the correct services.
But I couldn't find a way to have 2 different Applications running on the same port.
Is it possible?
http://localhost:20000/service1 <--- Svc in Application1
http://localhost:20000/service2 <--- Svc in Application2

Calling Bindings.Remove does not remove SSL cert from HTTP.sys

The shortest version of my question is that calling ServerManager.Binding.Remove seems to remove a binding from IIS, but still leave it in HTTP.sys or wherever SSL bindings are set and breaks layers of my code further down.
I'm running an Azure Cloud Service that needs to use SNI to support multiple hostnames using SSL. Effectively what I'm doing is in OnStart removing the default binding using ServerManager.Binding.Remove(binding) and adding my own bindings using ServerManager.Binding.Add(binding). So for example:
ServerManager serverManager = new ServerManager();
Site site = serverManager.Sites[0];
// Add my site bindings.
foreach (string host in listOfHostsToBind)
{
X509Certificate2 cert = LookupCertificate(host.sslThumbprint);
var binding = site.Bindings.Add(":443:" + host, cert.GetCertHash(), "My");
binding.SetAttributeValue("sslFlags", 1); //Set SNI flag
}
// Remove the default binding
var bindingsToRemove = new List<Binding>();
foreach (Binding binding in site.Bindings)
{
if (binding.Protocol == "https" && Convert.ToInt64(binding.Attributes["sslFlags"].Value) != 1)
{
bindingsToRemove.Add(binding);
}
}
foreach (Binding binding in bindingsToRemove)
{
site.Bindings.Remove(binding);
serverManager.CommitChanges();
}
serverManager.CommitChanges();
What ends up happening is that the default IP:Port binding is removed from the list of IIS bindings, but it still shows up in the list of SSL bindings when I call netsh http show sslcert.
So, for example, here's the output from calling Get-WebBinding in Powershell. Notice that the default IP:Port binding is not there:
protocol bindingInformation sslFlags
-------- ------------------ --------
http 10.20.30.40:80: 0
https :443:myfirstaddedhost.com 1
https :443:mysecondaddedhost.com 1
Looks good, but it still doesn't work, because if I run netsh http show sslcert I get the following:
IP:port : 10.20.30.40:443
Certificate Hash : xxx
Application ID : {00000000-0000-0000-0000-000000000000}
Certificate Store Name : MY
...
Hostname:port : myfirstaddedhost.com:443
Certificate Hash : xxx
Application ID : {4dc3e181-e14b-4a21-b022-59fc669b0914}
Certificate Store Name : My
...
Hostname:port : mysecondaddedhost.com:443
Certificate Hash : xxx
Application ID : {4dc3e181-e14b-4a21-b022-59fc669b0914}
Certificate Store Name : My
...
Why would the SSL Cert binding still be there if I successfully removed the binding from IIS using ServerManager?
Turns out that configuring the role for Remote Desktop from the Azure Portal was adding the binding. More specifically, updating the Certificate configuration for the role (which happens as part of RDP config) is causing it. This meant that it worked until I went in via RDP to check whether it was working at which point it would start to fail. Of course, genius that I am I was trying to be methodical and do things in the same order every time, which meant I was configuring remote desktop before actually attempting a request, so from my perspective it looked like it was failing from the beginning. It was only when I tried things in the opposite (running requests before configuring RDP) that it started to work.
You can use netsh http delete sslcert to delete the binding and it does not affect your ability to log in via RDP to that instance.
When you configure RDP it calls the RoleEnvironment.Changing and RoleEnvironment.Changed events, but unfortunately when those events are called the binding has not been created yet, so there's not an obvious place where you could use netsh http delete sslcert to delete the binding in code.
I don't know that this is an "answer" exactly. It means I still have an issue where configuring an Azure Instance for RDP or changing the cert configuration breaks my SNI bindings. For my organization this is OK because there are only a couple people with enough permissions to configure RDP and they can be trained to explicitly delete the new binding if they need to use RDP. I'll follow up here if I figure out a way to prevent this altogether.

Resources