The introduction

Recently, I saw that the open source project identityServer4.admin integrates IdentityServer4 with the Admin panel, so I used it directly. When trying to deploy Nginx, I encountered problems such as virtual directory mapping, request headers being too long, and incorrect base path mapping, etc., briefly recorded for future reference.

Nginx configures route forwarding

Identityserver4.admin project structure

IdentityServer4. Admin / ├ ─ ─ Id4. Admin. Api# WebApi project for providing access to Id4 resources├ ─ ─ Id4. AdminA Web management panel for managing Id4 resources├ ─ ─ Id4) STS) Identity# Web project for providing STS services
Copy the code

As three separate projects, it is easy to deploy them separately, but for unified entry management, I prefer to deploy id4.admin and ID4.sts.identity under one domain, and the ID4.admin. API project into the gateway. That is accessed through http://auth.xxx.com Id4. STS. Identity, accessed through http://auth.xxx.com/admin Id4. Admin.

This is the first problem encountered how to use Nginx to achieve single-domain multi-site deployment!

When Kestrel is deployed as an edge Web server, it has an exclusive IP and port. In the absence of a reverse proxy server, Kestrel, used as an edge server, does not support sharing the same IP and port across multiple processes. When Kestrel is configured to listen on a port, Kestrel handles all network traffic on that port and ignores the Host request header specified in the request header, which means that Kestrel is not responsible for request forwarding.

Therefore, for port sharing, we use a reverse proxy to forward requests to Kestrel with unique IP and ports. That’s the picture down here.

According to the Nginx official configuration document, you can configure Location to implement the specified route forwarding.

server {
    listen 80;
    listen [::]:80;
    server_name mysite;
    
    location / {
        proxy_pass http://id4.sts.identity:80;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    location /admin/ {
        proxy_pass http://id4.admin:80/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme; }}Copy the code

Let’s compare two proxy_pass configurations:

  1. location / { proxy_pass id4.sts.identity:80; }
  2. location /admin/ { proxy_pass id4.admin:80**/**; }

The main difference is that proxy_pass http://id4.admin:80/ under the location /admin/ node contains a left slash/at the end. (Without the left slash, all requests are routed to the root node.) Such as a request to http://auth.xxx.com/admin/dashboard, then the nginx according to the above configuration will route requests to http://id4.admin:80/dashboard. The final left slash replaces the routing rule specified by location, which is /admin in this case.

But is that OK? Absolutely no! Execute nginx -s reload and you’ll get a big 404.

Enable the UsePathBase middleware

This is where the UsePathBase middleware comes in, which sets up the base path for site requests. Adding UsePathBase middleware to a Web project is simple: first add a configuration item PATHBASE in appSettings. json and then enable it in Startup Config.


appsettings.json
{
    "PATHBASE":"/admin"} ----- public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } private IConfiguration Configuration { get; } / /... public void Configure(...) {/ /... app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
Copy the code

Enable UseForwardedHeaders middleware

Another issue with reverse proxies is that they can obscure some of the request information:

  1. When an HTTPS request is made through an HTTP proxy, the original transport protocol (HTTPS) is lost and must be forwarded in the request header.
  2. Because the application receives the request from a proxy server, not the actual source of the request, the original client IP address must also be forwarded in the request header.

This is why the above Nginx configuration, by default, contains the following two configurations.

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
Copy the code

Nginx is configured to forward this information by default, so it’s natural to explicitly tell the ASP.NET Core Web application to fetch the actual request information from the request header. Configuration is very simple, need to Ann ` Microsoft. AspNetCore. HttpOverrides NuGet package, and then enable the middleware in the Startup Config.

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } private IConfiguration Configuration { get; } / /... public void Configure(...) {/ /... app.UseForwardedHeaders(new ForwardedHeadersOptions{ ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
Copy the code

It is important to note that any components that depend on transport protocols, such as authentication, link generation, redirection, and geolocation, must be enabled after the request header forwarding middleware. In general, request header forwarding middleware should run ahead of other middleware except diagnostic and error handling middleware.

Once configured, redeploy and you should be up and running for a normal project. But you may also encounter:

Remove Nginx request header forwarding size limit

If the Nginx server is deployed on a Linux server, the default log file is /var/log/nginx/error.log. 17677925 upstream sent too big header while reading response header from upstream. Request header data is too large. Then let’s take a look at how big the forwarded request header will be. It can be seen from the figure below that the largest Cookie carried in the request header is more than 3K.

Add the following configuration to nginx:

proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
Copy the code

Reload Nginx configuration, access successful.

Is All Set? No!

Fix base path errors

When I try to click on the link in the Admin panel, I get an unforgiving 404 because the link address is: http://auth.xxx.com/configruaion/clients, the correct link address should be http://auth.xxx.com/admin/configruaion/clients. Manage Client
There is no help to generate a tag link according to the path specified by UsePathBase. We can only look at the source code to find out. Microsoft AspNetCore. Mvc. TagHelpers/AnchorTagHe… , and ultimately in the splicing Herf attribute is used in var pathBase = ActionContext. HttpContext. Request. PathBase; To concatenate the base path. That is, TagHelper gets the base path from the Http request context. Location /admin/ {proxy_pass http://id4.admin:80/; This route mapping will eventually lose the base path of the original route, which is the /admin/ route part. So, we put the basic path to complement obediently, namely proxy_pass http://id4.admin:80/admin/; The single-domain, multi-site reverse proxy deployment is complete.

The last

Twists and turns, but in the end it lived up to expectations. Finally, the complete Nginx configuration is released for your reference:

server {
    listen 80;
    listen [::]:80;
    server_name mysite;
    
    location / {
        proxy_pass http://id4.sts.identity:80;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    location /admin/ {
        proxy_pass http://id4.admin:80/admin/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; }}Copy the code

References:

  1. Configure ASP.NET Core to work with proxy servers and load balancers
  2. GitHub Issue: Deploy to subdirectory #15464
  3. ASP.Net Core 3 App Running under a Subdirectory on Nginx