Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce the concept of frontend to decouple routing logic #134

Closed
guibirow opened this issue May 2, 2020 · 5 comments
Closed

Introduce the concept of frontend to decouple routing logic #134

guibirow opened this issue May 2, 2020 · 5 comments
Labels
Type: Idea This issue is a high-level idea for discussion.

Comments

@guibirow
Copy link

guibirow commented May 2, 2020

What should we add or change to make your life better?

Introduce the Frontend concept which act like a server to decouple routing logic, allowing us to:

  • Setup proxy listening ports per hosts
  • Setup SSL certificates per host
  • Simplify routing setup and group routes valid for specific host.

Why is this important to you?

Current approach suggest the use of routes to match hosts from requests to backends, I see quite a few limitations with this approach.

1 - Performance
Each request need to validate the hostname against each route defined. If we have 5 routes, each request would validate the hostname 5 times.

Adding the frontend(server in nginx), we could validate the host once per request.

2 - Configuration complexity
Adding the host to rules is very bloated and requires the hostname on every route.

Separating it into frontend we could group routes only valid for that specific host

Alternatively, we could separate frontend from routing, so we could share same routing rules with multiple frontend.

3 - SNI
SNI happens before the request is proceessed, wouldn't be feasible to setup SNI on each route, so a separate configuration would be required, frontend seems the most appropriate.

4 - Pipelines
Setup different pipelines/middleware based on frontend hostname, not on routes.
Example: a multi-tenant application having separate domains but routing to same backend, we could setup different request throttling per customer.

Also, using frontend with pipelines before the routing, we could move authentication middleware on top of request chain.

5 - proxy startup setup
Current configuration we have only one server.

Using frontend we could setup each frontend to act as a server and define ports and protocols we intend to listen for each domain. This way we could setup the entire proxy without requiring any line of code.

In summary,
I am not suggesting removing host validation from routes, in some cases we could have a route valid to only a specific domain.

The goal is to make host validation in the route optional and decouple frontend - pipelines - routes - backend

This relates to following issues:

@guibirow guibirow added the Type: Idea This issue is a high-level idea for discussion. label May 2, 2020
@Tratcher
Copy link
Member

Tratcher commented May 2, 2020

Can we invert this to discuss problems before solutions? This is an interesting solution, but I think many of these points can be addressed in other ways. We may want to break this up into individual issues to address.

Each request need to validate the hostname against each route defined. If we have 5 routes, each request would validate the hostname 5 times.

That assumes the route evaluation ordering implementation is to first check the host and then the path. I think it works the other way, checking path and then disambiguating on host. @rynowak?

Adding the host to rules is very bloated and requires the hostname on every route.

True. Some grouping would make sense to address this. That said, I wonder about the relationship between your hosts and routes. Given these four combinations:
A) Few hosts, few routes
B) Many hosts, few routes
C) Few hosts, many routes
D) Many hosts, many routes
It seems like this host redundancy issue would primarily affect case D (and maybe C?), but I expect most of our usage to fall into A-C. If that's true then this is not a high priority to address. Do you have any metrics from existing deployments about the number of hosts, routes, and avg. routes per host? @samsp-msft this would be interesting data to collect from partners.

SNI happens before the request is proceessed, wouldn't be feasible to setup SNI on each route, so a separate configuration would be required, frontend seems the most appropriate.

SNI is configured in the server on sockets / IPs, it is not associated with routing in any way. SNI is also configured differently in each server so we wouldn't want to tie the proxy config to it.

Setup different pipelines/middleware based on frontend hostname, not on routes.

We decided to not even do custom pipelines for routes, but rather to have middleware inspect the route data at runtime and enable/disable themselves (which also works for hosts). The main trouble is that the proxy routes can be reloaded during the lifetime of the app, but pipelines are too complicated to define in config or rebuild every time the proxy config changes.

Current configuration we have only one server.

That's all AspNetCore supports. Restructuring that would require fundamental changes to the underlying framework. And again here, each underling server has a different configuration system (IIS vs HttpSys vs Kestrel) so combining server config and proxy config isn't viable.

This way we could setup the entire proxy without requiring any line of code.

Code is already optional. If there's any basic setting you can't access from config let us know.

@guibirow
Copy link
Author

guibirow commented May 2, 2020

Can we invert this to discuss problems before solutions?

Sure, I just raised this as a "solution" to a problem that all other proxies have already been through, just bringing it to life because this project might get there at some point.

e.g:

  • Haproxy -> frontend
  • nginx -> server
  • envoy -> listener
  • traeffic -> entrypoint
  • azure APIM -> frontend

Each request need to validate the hostname against each route defined. If we have 5 routes, each request would validate the hostname 5 times.

That assumes the route evaluation ordering implementation is to first check the host and then the path. I think it works the other way, checking path and then disambiguating on host. @ rynowak?

I think it is the same problem, just the other way around, now you validate all the route path first, then the hosts.

Validating the host first would allow you to only validate a subset of routes.

Regarding the number of host vs routes performance, this is probably a more in depth analysis, because each user will have a different scenario.

SNI is configured in the server on sockets / IPs

Current configuration we have only one server.

That's all AspNetCore supports.

I am not exactly sure how you guys are planning SNI and HTTPs,
But the idea I was suggesting was an internal (proxy) concept that will either share the webhost using SNI or
assuming you could have multiple webhosts bound to different NICs IPs.

Without trying to solutionize, but to explain with example, something like:

app.MapHost("domain.com", context => {...hostpipeline..})
app.MapHost("sub.domain.com", context => {...hostpipeline..})

@Tratcher
Copy link
Member

Tratcher commented May 2, 2020

Those frontends are conceptually similar the AspNetCore Server layer which is already pretty well defined.

SNI is already supported by the servers, but we're tracking a few different enhancements over at #86.

@guibirow
Copy link
Author

guibirow commented May 4, 2020

No, they are not, the comparable to the aspnet server would be:

nginx -> http(that represents the http server)
Traeffic -> net/http from go
envoy -> Connection mannager with http filter

In simple terms, what most of these solutions does, is define a logical server (frontend) for individual configuration and aggregate them into a single TCP listener to share ports and do the forwarding according to each server setup.

@Tratcher
Copy link
Member

Summary: There are several distinct issues here that we will plan to address individually.

  • Since we're building this on top of the existing AspNetCore framework there are some constraints around the basic architecture such as how external port/host/sni bindings work. We'll make improvements to AspNetCore as needed, but won't be able to redesign large portions of the underling framework to accommodate this project.
  • We plan to address the routing performance concerns directly in routing. See MapHost endpoint routing dotnet/aspnetcore#19354
  • The config redundancy question makes sense if you have many routes with similar values. This doesn't seem to be specific to the Host property though, any of the route properties could be repeated. Filed Allow grouping and inheriting route config #164 for route grouping. This is primarily a config problem and doesn't impact the internal structure.
  • SNI is a server concern and we support multiple servers, we will not be coupling that to proxy config. SNI features are tracked over at SNI cert selection support #86.
  • We do not recommend having multiple pipelines based on reloadable data like hosts or routes, but rather to have one pipeline that reacts to the settings on a route/request.

Let me know if I missed anything, but I think we can close this and continue discussion on the linked issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Idea This issue is a high-level idea for discussion.
Projects
None yet
Development

No branches or pull requests

2 participants