Service Fabric reverse proxy
In my previous article Hosting a web application in Service Fabric, we deployed an OWIN hosted website as a Guest Executable and accessed the application using the built-in
Service Fabric Reverse Proxy. This works, and it gives you an out of the box solution to reach into your cluster from outside of it :
Unfortunately, the built-in Reverse Proxy does not give you any control over which application will be exposed, nor does it allow you to rewrite urls. It means that if you enable the reverse proxy and expose it to the outside world, every single endpoints in your cluster will be suddenly exposed.
The documentation makes very this clear :
Of course, we do not want this. So how do we expose our http endpoints selectively?
In this article, here is what we will do :
- Use a sample project with a single default aspnet web application deployed in Service Fabric described below
- Configure and deploy our own gateway
- Handle the proxy url forwarded headers in our application
- Configure the Azure Load Balancer to point at our new gateway
Our sample project
You can grab the exact solution and code I used for this blog post in this gihub repo : https://github.com/serbrech/deploy-own-gateway-SF
My sample project just contains a default asp.net service. This will represent the service you want to expose.
We configure it to run 3 instances. In the Application Parameters, I set the *_InstanceCount Parameter value to 3:
To let Service Fabric select a port, we will remove the
Port attribute from the service manifest endpoint :
At this point, if your Reverse proxy is enable, you can reach your website through the built-in Reverse Proxy at
Deploying on a local development cluster in my case, that is
As warned earlier, that’s not what you want to expose to the outside world.
Deploy your own gateway
It sounds like a lot of work, but bear with me, it’s actually very simple. We need the same sort of behaviour as the reverse proxy, only with a little bit more control.
Fortunately, we live in the beautiful world of open source and this problem has already been solved. The package
C3.ServiceFabric.HttpServiceGateway is exactly what we need and it is pretty well done. It is an ASP.NET Core Middleware that routes incoming requests to internal services. You can browse the code and read the extensive documentation on github : https://github.com/c3-ls/ServiceFabric-Http
Here is how to set this up in your cluster :
We are going to add a new empty aspnet core service to our Application.
Choose the empty template, this is only going to run our middleware.
This will create a project with a
WebListenerCommunicationListener configured with a
WebHost and a
Startup class that we will look into in a minute.
Let’s configure our Endpoint. You will need to know where is our gateway, so we give it a specific port. In the ServiceManifest, set the port of the Endpoint :
The gateway is the entry point to the system and it must stay available. We deploy it on every node.
Gateway_InstanceCount parameter that was added in the ApplicationParameters. Make sure that your production deployment has a value of
Note that if you deploy to your local cluster, you will not be able to have more than one instance of the service running on the same port. For local dev cluster, either leave this to 1, or let the port be dynamic.
We now add the nuget package C3.ServiceFabric.HttpServiceGateway to the gateway.
Startup.cs file, we need to enable it :
And we configure the middleware :
Let’s walk through it.
RunHttpServiceGateway method maps the path
/MyAwesomeWebsite to the root of the
WebService under the
Now, whenever you hit the gateway on port
1664, at the path
/MyAwesomeWebsite, the gateway will use the Naming Service to locate
fabric:/MyApp/WebService in the cluster and your request will be forwarded to one of the instances.
Application Urls, Reverse Proxy and X-Forwarded-* headers
When you put your application behind a proxy, the request url might not match the resource url. This means your application will not be able to construct the relative urls.
For example when I navigate to
/MyAwesomeWebsite, my website thinks I am on
/, because that is where the request was forwarded to by the gateway.
If I have a relative a link on the page to
/about the browser will try to reach
http://<gateway>/about instead of
Fortunately, there are commonly used headers to cicumvent this issue:
X-Forwarded-*. When the proxy forwards a request, it copies the original information necessary to rebuild the urls in these special headers.
And because it’s a common use case, there is a middleware for it : UseForwardedHeaders.
It is not very well documented, but it is described in the documentation when hosting asp.net behind nginx, and it can be used withany proxy, as long as the headers are passed along.
C3.ServiceFabric.HttpServiceGateway is sending these headers. Unfortunately, the
ForwardedHeaders middleware does not handle
X-Forwarded-PathBase so we need to handle this one ourselves. It’s not too hard though. Here is what is needed in the WebService
Startup.cs to handle the proxy headers :
Configure the Azure Load Balancer
Of course, nobody wants to access your website on port
1664. Next, we need set up the load balancer to point at our newly deployed gateway.
On a fresh cluster deployed on azure (in one powershell line!),
there is an existing
AppPortLBRule1 for port 80. Edit it, and change the backend port to point at your gateway port :
1664 in my case.
Note that the Load Balancer defines a
Health Probe. If the health probe does not succeed, the load balancer will assume that the backend pool is not healthy, and it will not redirect your request. Here is a probe that will succeed.
I can now reach the website on
http://<FQDN>/MyAwesomeWebsite and all relative links and resources work!
I hope this was useful. Do not hesitate to reach out in the comments below, or tweet at me: @serbrech