-
Notifications
You must be signed in to change notification settings - Fork 25.1k
YARP migration /2 #34657
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
Merged
Merged
YARP migration /2 #34657
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
26c5805
YARP migration /2
Rick-Anderson b7d7b3b
YARP migration /2
Rick-Anderson 3f1c708
YARP migration /2
Rick-Anderson b04ba90
YARP migration /2
Rick-Anderson 095c73d
YARP migration /2
Rick-Anderson 06684dd
YARP migration /2
Rick-Anderson 9e0f24a
YARP migration /2
Rick-Anderson 25e9b80
YARP migration /2
Rick-Anderson ffb2a7e
YARP migration /2
Rick-Anderson 3d6bda4
YARP migration /2
Rick-Anderson fb8c87a
YARP migration /2
Rick-Anderson be78d8c
YARP migration /2
Rick-Anderson f054deb
YARP migration /2
Rick-Anderson 027b7cd
YARP migration /2
Rick-Anderson 7e7e136
YARP migration /2
Rick-Anderson 7faeea1
YARP migration /2
Rick-Anderson a1be05d
YARP migration /2
Rick-Anderson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # A/B Testing and Rolling Upgrades | ||
|
|
||
| ## Introduction | ||
|
|
||
| A/B testing and rolling upgrades require procedures for dynamically assigning incoming traffic to evaluate changes in the destination application. YARP does not have a built-in model for this, but it does expose some infrastructure useful for building such a system. See [issue #126](https://github.com/microsoft/reverse-proxy/issues/126) for additional details about this scenario. | ||
|
|
||
| ## Example | ||
|
|
||
| ``` | ||
| app.MapReverseProxy(proxyPipeline => | ||
| { | ||
| // Custom cluster selection | ||
| proxyPipeline.Use((context, next) => | ||
| { | ||
| var lookup = context.RequestServices.GetRequiredService<IProxyStateLookup>(); | ||
| if (lookup.TryGetCluster(ChooseCluster(context), out var cluster)) | ||
| { | ||
| context.ReassignProxyRequest(cluster); | ||
| } | ||
|
|
||
| return next(); | ||
| }); | ||
| proxyPipeline.UseSessionAffinity(); | ||
| proxyPipeline.UseLoadBalancing(); | ||
| }); | ||
|
|
||
| string ChooseCluster(HttpContext context) | ||
| { | ||
| // Decide which cluster to use. This could be random, weighted, based on headers, etc. | ||
| return Random.Shared.Next(2) == 1 ? "cluster1" : "cluster2"; | ||
| } | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| This scenario makes use of two APIs, [IProxyStateLookup](xref:Yarp.ReverseProxy.IProxyStateLookup) and [ReassignProxyRequest](xref:Microsoft.AspNetCore.Http.HttpContextFeaturesExtensions.ReassignProxyRequest(Microsoft.AspNetCore.Http.HttpContext,Yarp.ReverseProxy.Model.ClusterState)), called from a custom proxy middleware as shown in the sample above. | ||
|
|
||
| `IProxyStateLookup` is a service available in the Dependency Injection container that can be used to look up or enumerate the current routes and clusters. Note this data may change if the configuration changes. An A/B orchestration algorithm can examine the request, decide which cluster to send it to, and then retrieve that cluster from `IProxyStateLookup.TryGetCluster`. | ||
|
|
||
| Once the cluster is selected, `ReassignProxyRequest` can be called to assign the request to that cluster. This updates the [IReverseProxyFeature](xref:Yarp.ReverseProxy.Model.IReverseProxyFeature) with the new cluster and destination information needed for the rest of the proxy middleware pipeline to handle the request. | ||
|
|
||
| ## Session affinity | ||
|
|
||
| Note that session affinity functionality is split between middleware, which reads it settings from the current cluster, and transforms, which are part of the original route. Clusters used for A/B testing should use the same session affinity configuration to avoid conflicts. | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| # Authentication and Authorization | ||
|
|
||
| ## Introduction | ||
| The reverse proxy can be used to authenticate and authorize requests before they are proxied to the destination servers. This can reduce load on the destination servers, add a layer of protection, and ensure consistent policies are implemented across your applications. | ||
|
|
||
| ## Defaults | ||
|
|
||
| No authentication or authorization is performed on requests unless enabled in the route or application configuration. | ||
|
|
||
| ## Configuration | ||
| Authorization policies can be specified per route via [RouteConfig.AuthorizationPolicy](xref:Yarp.ReverseProxy.Configuration.RouteConfig) and can be bound from the `Routes` sections of the config file. As with other route properties, this can be modified and reloaded without restarting the proxy. Policy names are case insensitive. | ||
|
|
||
| Example: | ||
| ```JSON | ||
| { | ||
| "ReverseProxy": { | ||
| "Routes": { | ||
| "route1" : { | ||
| "ClusterId": "cluster1", | ||
| "AuthorizationPolicy": "customPolicy", | ||
| "Match": { | ||
| "Hosts": [ "localhost" ] | ||
| } | ||
| } | ||
| }, | ||
| "Clusters": { | ||
| "cluster1": { | ||
| "Destinations": { | ||
| "cluster1/destination1": { | ||
| "Address": "https://localhost:10001/" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| [Authorization policies](https://docs.microsoft.com/aspnet/core/security/authorization/policies) are an ASP.NET Core concept that the proxy utilizes. The proxy provides the above configuration to specify a policy per route and the rest is handled by existing ASP.NET Core authentication and authorization components. | ||
|
|
||
| Authorization policies can be configured in the application as follows: | ||
| ``` | ||
| services.AddAuthorization(options => | ||
| { | ||
| options.AddPolicy("customPolicy", policy => | ||
| policy.RequireAuthenticatedUser()); | ||
| }); | ||
| ``` | ||
|
|
||
| In Program.cs add the Authorization and Authentication middleware. | ||
|
|
||
| ``` | ||
| app.UseAuthentication(); | ||
| app.UseAuthorization(); | ||
|
|
||
| app.MapReverseProxy(); | ||
| ``` | ||
|
|
||
| See the [Authentication](https://docs.microsoft.com/aspnet/core/security/authentication/) docs for setting up your preferred kind of authentication. | ||
|
|
||
| ### Special values: | ||
|
|
||
| In addition to custom policy names, there are two special values that can be specified in a route's authorization parameter: `default` and `anonymous`. ASP.NET Core also has a FallbackPolicy setting that applies to routes that do not specify a policy. | ||
|
|
||
| #### DefaultPolicy | ||
|
|
||
| Specifying the value `default` in a route's authorization parameter means that route will use the policy defined in [AuthorizationOptions.DefaultPolicy](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.authorization.authorizationoptions.defaultpolicy?#Microsoft_AspNetCore_Authorization_AuthorizationOptions_DefaultPolicy). That policy is pre-configured to require authenticated users. | ||
|
|
||
| #### Anonymous | ||
|
|
||
| Specifying the value `anonymous` in a route's authorization parameter means that route will not require authorization regardless of any other configuration in the application such as the FallbackPolicy. | ||
|
|
||
| #### FallbackPolicy | ||
|
|
||
| [AuthorizationOptions.FallbackPolicy](https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.authorization.authorizationoptions.fallbackpolicy) is the policy that will be used for any request or route that was not configured with a policy. FallbackPolicy does not have a value by default, any request will be allowed. | ||
|
|
||
| ## Flowing Credentials | ||
|
|
||
| Even after a request has been authorized in the proxy, the destination server may still need to know who the user is (authentication) and what they're allowed to do (authorization). How you flow that information will depend on the type of authentication being used. | ||
|
|
||
| ### Cookie, bearer, API keys | ||
|
|
||
| These authentication types already pass their values in the request headers and these will flow to the destination server by default. That server will still need to verify and interpret those values, causing some double work. | ||
|
|
||
| ### OAuth2, OpenIdConnect, WsFederation | ||
|
|
||
| These protocols are commonly used with remote identity providers. The authentication process can be configured in the proxy application and will result in an authentication cookie. That cookie will flow to the destination server as a normal request header. | ||
|
|
||
| ### Windows, Negotiate, NTLM, Kerberos | ||
|
|
||
| These authentication types are often bound to a specific connection. They are not supported as means of authenticating a user in a destination server behind the YARP proxy (see [#166](https://github.com/microsoft/reverse-proxy/issues/166). They can be used to authenticate an incoming request to the proxy, but that identity information will have to be communicated to the destination server in another form. They can also be used to authenticate the proxy to the destination servers, but only as the proxy's own user, impersonating the client is not supported. | ||
|
|
||
| ### Client Certificates | ||
|
|
||
| Client certificates are a TLS feature and are negotiated as part of a connection. See [these docs](https://docs.microsoft.com/aspnet/core/security/authentication/certauth) for additional information. The certificate can be forwarded to the destination server as an HTTP header using the [ClientCert](transforms.md#clientcert) transform. | ||
|
|
||
| ### Swapping authentication types | ||
|
|
||
| Authentication types like Windows that don't flow naturally to the destination server will need to be converted in the proxy to an alternate form. For example a JWT bearer token can be created with the user information and set on the proxy request. | ||
|
|
||
| These swaps can be performed using [custom request transforms](transforms.md#from-code). Detailed examples can be developed for specific scenarios if there is enough community interest. We need more community feedback on how you want to convert and flow identity information. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| # Configuration Files | ||
|
|
||
| ## Introduction | ||
| The reverse proxy can load configuration for routes and clusters from files using the IConfiguration abstraction from Microsoft.Extensions. The examples given here use JSON, but any IConfiguration source should work. The configuration will also be updated without restarting the proxy if the source file changes. | ||
|
|
||
| ## Loading Configuration | ||
| To load the proxy configuration from IConfiguration add the following code in Program.cs: | ||
| ```c# | ||
| using Microsoft.AspNetCore.Builder; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| // Add the reverse proxy capability to the server | ||
| builder.Services.AddReverseProxy() | ||
| // Initialize the reverse proxy from the "ReverseProxy" section of configuration | ||
| .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy")); | ||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| // Register the reverse proxy routes | ||
| app.MapReverseProxy(); | ||
|
|
||
| app.Run(); | ||
| ``` | ||
| **Note**: For details about middleware ordering see [here](https://docs.microsoft.com/aspnet/core/fundamentals/middleware/#middleware-order). | ||
|
|
||
| Configuration can be modified during the load sequence using [Configuration Filters](config-filters.md). | ||
|
|
||
| ## Multiple Configuration Sources | ||
| As of 1.1, YARP supports loading the proxy configuration from multiple sources. LoadFromConfig may be called multiple times referencing different IConfiguration sections or may be combine with a different config source like InMemory. Routes can reference clusters from other sources. Note merging partial config from different sources for a given route or cluster is not supported. | ||
|
|
||
| ```c# | ||
| services.AddReverseProxy() | ||
| .LoadFromConfig(Configuration.GetSection("ReverseProxy1")) | ||
| .LoadFromConfig(Configuration.GetSection("ReverseProxy2")); | ||
| ``` | ||
| or | ||
| ```c# | ||
|
|
||
| services.AddReverseProxy() | ||
| .LoadFromMemory(routes, clusters) | ||
| .LoadFromConfig(Configuration.GetSection("ReverseProxy")); | ||
| ``` | ||
|
|
||
| ## Configuration contract | ||
| File-based configuration is dynamically mapped to the types in [Yarp.ReverseProxy.Configuration](xref:Yarp.ReverseProxy.Configuration) namespace by an [IProxyConfigProvider](xref:Yarp.ReverseProxy.Configuration.IProxyConfigProvider) implementation converts at application start and each time the configuration changes. | ||
|
|
||
| ## Configuration Structure | ||
| The configuration consists of a named section that you specified above via `Configuration.GetSection("ReverseProxy")`, and contains subsections for routes and clusters. | ||
|
|
||
| Example: | ||
| ```JSON | ||
| { | ||
| "ReverseProxy": { | ||
| "Routes": { | ||
| "route1" : { | ||
| "ClusterId": "cluster1", | ||
| "Match": { | ||
| "Path": "{**catch-all}", | ||
| "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"] | ||
| } | ||
| } | ||
| }, | ||
| "Clusters": { | ||
| "cluster1": { | ||
| "Destinations": { | ||
| "cluster1/destination1": { | ||
| "Address": "https://example.com/" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Routes | ||
|
|
||
| The routes section is an unordered collection of route matches and their associated configuration. A route requires at least the following fields: | ||
| - RouteId - a unique name | ||
| - ClusterId - refers to the name of an entry in the clusters section. | ||
| - Match - contains either a Hosts array or a Path pattern string. Path is an ASP.NET Core route template that can be defined as [explained here](https://docs.microsoft.com/aspnet/core/fundamentals/routing#route-templates). | ||
| Route matching is based on the most specific routes having highest precedence as described [here]( https://docs.microsoft.com/aspnet/core/fundamentals/routing#url-matching). Explicit ordering can be achieved using the `order` field, with lower values taking higher priority. | ||
|
|
||
| [Headers](header-routing.md), [Authorization](authn-authz.md), [CORS](cors.md), and other route based policies can be configured on each route entry. For additional fields see [RouteConfig](xref:Yarp.ReverseProxy.Configuration.RouteConfig). | ||
|
|
||
| The proxy will apply the given matching criteria and policies, and then pass off the request to the specified cluster. | ||
|
|
||
| ### Clusters | ||
| The clusters section is an unordered collection of named clusters. A cluster primarily contains a collection of named destinations and their addresses, any of which is considered capable of handling requests for a given route. The proxy will process the request according to the route and cluster configuration in order to select a destination. | ||
|
|
||
| For additional fields see [ClusterConfig](xref:Yarp.ReverseProxy.Configuration.ClusterConfig). | ||
|
|
||
| ## All config properties | ||
| ```JSON | ||
| { | ||
| // Base URLs the server listens on, must be configured independently of the routes below | ||
| "Urls": "http://localhost:5000;https://localhost:5001", | ||
| "Logging": { | ||
| "LogLevel": { | ||
| "Default": "Information", | ||
| // Uncomment to hide diagnostic messages from runtime and proxy | ||
| // "Microsoft": "Warning", | ||
| // "Yarp" : "Warning", | ||
| "Microsoft.Hosting.Lifetime": "Information" | ||
| } | ||
| }, | ||
| "ReverseProxy": { | ||
| // Routes tell the proxy which requests to forward | ||
| "Routes": { | ||
| "minimumroute" : { | ||
| // Matches anything and routes it to www.example.com | ||
| "ClusterId": "minimumcluster", | ||
| "Match": { | ||
| "Path": "{**catch-all}" | ||
| } | ||
| }, | ||
| "allrouteprops" : { | ||
| // matches /something/* and routes to "allclusterprops" | ||
| "ClusterId": "allclusterprops", // Name of one of the clusters | ||
| "Order" : 100, // Lower numbers have higher precedence | ||
| "MaxRequestBodySize" : 1000000, // In bytes. An optional override of the server's limit (30MB default). Set to -1 to disable. | ||
| "AuthorizationPolicy" : "Anonymous", // Name of the policy or "Default", "Anonymous" | ||
| "CorsPolicy" : "Default", // Name of the CorsPolicy to apply to this route or "Default", "Disable" | ||
| "Match": { | ||
| "Path": "/something/{**remainder}", // The path to match using ASP.NET syntax. | ||
| "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], // The host names to match, unspecified is any | ||
| "Methods" : [ "GET", "PUT" ], // The HTTP methods that match, uspecified is all | ||
| "Headers": [ // The headers to match, unspecified is any | ||
| { | ||
| "Name": "MyCustomHeader", // Name of the header | ||
| "Values": [ "value1", "value2", "another value" ], // Matches are against any of these values | ||
| "Mode": "ExactHeader", // or "HeaderPrefix", "Exists" , "Contains", "NotContains", "NotExists" | ||
| "IsCaseSensitive": true | ||
| } | ||
| ], | ||
| "QueryParameters": [ // The query parameters to match, unspecified is any | ||
| { | ||
| "Name": "MyQueryParameter", // Name of the query parameter | ||
| "Values": [ "value1", "value2", "another value" ], // Matches are against any of these values | ||
| "Mode": "Exact", // or "Prefix", "Exists" , "Contains", "NotContains" | ||
| "IsCaseSensitive": true | ||
| } | ||
| ] | ||
| }, | ||
| "Metadata" : { // List of key value pairs that can be used by custom extensions | ||
| "MyName" : "MyValue" | ||
| }, | ||
| "Transforms" : [ // List of transforms. See the Transforms article for more details | ||
| { | ||
| "RequestHeader": "MyHeader", | ||
| "Set": "MyValue" | ||
| } | ||
| ] | ||
| } | ||
| }, | ||
| // Clusters tell the proxy where and how to forward requests | ||
| "Clusters": { | ||
| "minimumcluster": { | ||
| "Destinations": { | ||
| "example.com": { | ||
| "Address": "http://www.example.com/" | ||
| } | ||
| } | ||
| }, | ||
| "allclusterprops": { | ||
| "Destinations": { | ||
| "first_destination": { | ||
| "Address": "https://contoso.com" | ||
| }, | ||
| "another_destination": { | ||
| "Address": "https://10.20.30.40", | ||
| "Health" : "https://10.20.30.40:12345/test" // override for active health checks | ||
| } | ||
| }, | ||
| "LoadBalancingPolicy" : "PowerOfTwoChoices", // Alternatively "FirstAlphabetical", "Random", "RoundRobin", "LeastRequests" | ||
| "SessionAffinity": { | ||
| "Enabled": true, // Defaults to 'false' | ||
| "Policy": "Cookie", // Default, alternatively "CustomHeader" | ||
| "FailurePolicy": "Redistribute", // default, Alternatively "Return503Error" | ||
| "Settings" : { | ||
| "CustomHeaderName": "MySessionHeaderName" // Defaults to 'X-Yarp-Proxy-Affinity` | ||
| } | ||
| }, | ||
| "HealthCheck": { | ||
| "Active": { // Makes API calls to validate the health. | ||
| "Enabled": "true", | ||
| "Interval": "00:00:10", | ||
| "Timeout": "00:00:10", | ||
| "Policy": "ConsecutiveFailures", | ||
| "Path": "/api/health", // API endpoint to query for health state | ||
| "Query": "?foo=bar" | ||
| }, | ||
| "Passive": { // Disables destinations based on HTTP response codes | ||
| "Enabled": true, // Defaults to false | ||
| "Policy" : "TransportFailureRateHealthPolicy", // Required | ||
| "ReactivationPeriod" : "00:00:10" // 10s | ||
| } | ||
| }, | ||
| "HttpClient" : { // Configuration of HttpClient instance used to contact destinations | ||
| "SSLProtocols" : "Tls13", | ||
| "DangerousAcceptAnyServerCertificate" : false, | ||
| "MaxConnectionsPerServer" : 1024, | ||
| "EnableMultipleHttp2Connections" : true, | ||
| "RequestHeaderEncoding" : "Latin1", // How to interpret non ASCII characters in request header values | ||
| "ResponseHeaderEncoding" : "Latin1" // How to interpret non ASCII characters in response header values | ||
| }, | ||
| "HttpRequest" : { // Options for sending request to destination | ||
| "ActivityTimeout" : "00:02:00", | ||
| "Version" : "2", | ||
| "VersionPolicy" : "RequestVersionOrLower", | ||
| "AllowResponseBuffering" : "false" | ||
| }, | ||
| "Metadata" : { // Custom Key value pairs | ||
| "TransportFailureRateHealthPolicy.RateLimit": "0.5", // Used by Passive health policy | ||
| "MyKey" : "MyValue" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| For more information see [logging configuration](diagnosing-yarp-issues.md#logging) and [HTTP client configuration](http-client-config.md). |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these code blocks have ```csharp so the syntax highlighting is correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, next pass. This pass is to get a clean build. The PR merges into a staging branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adityamandaleeka
Ultimately, we'll move all the code snippets to project files and import the code. That way we can make sure they compile. That allows highlights too.