Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net core X-Forwarded-Proto header doesn't pass to Nginx properly

Sorry for the edit history but this issue was really unclear to me and it was difficult to locate the exact problem.

I have a .Net-Core web application that runs behind a Nginx and the X-Forwarded-Proto always passes http instead of https.

Startup.cs

 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });

            services.AddMvc();

        }

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //first middlewear
            app.UseForwardedHeaders();
             //and the rest
         }

Nginx conf

server {
    listen        80;
    server_name   example.com;
    location / {
        proxy_pass         http://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Nginx.conf

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    #cloudflare real ip
    #https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-#12345681
    set_real_ip_from 173.245.48.0/20;
    real_ip_header    X-Forwarded-For;
    real_ip_recursive on;

    log_format  main  '"$scheme" $remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

access.log record

"http" 185.108.83.156 - - [03/Oct/2019:19:59:33 +0300] "GET /auth/signin/Facebook?returnUrl=%2F HTTP/1.1" 302 0 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36" "156"

as you can see the $scheme that I log is always HTTP.

A solution that solves the issue is to enforce Scheme to be HTTPS like so:

app.Use((context, next) =>
        {
            context.Request.Scheme = "https";
            return next();
        });

from https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.2#forwarded-headers-middleware-options

But with this solution I don't pass the headers and loses some information.

So does anyone have any solution for this case?

like image 972
Offir Avatar asked Sep 26 '19 20:09

Offir


People also ask

How do I enable X-forwarded-for in nginx?

Nginx x-forwarded-for IP Address If suppose we are using an nginx, then we will need to modify it in order to make an XFF ip address field. For our nginx server to use the real IP address instead of the proxy address, we will need to enable the module of ngx http realip module.

What is ForwardedHeadersOptions?

ForwardedHeadersOptions control the behavior of the Forwarded Headers Middleware. The following example changes the default values: Limits the number of entries in the forwarded headers to 2 . Adds a known proxy address of 127.0.

What does X forwarded proto do?

The X-Forwarded-Proto request header helps you identify the protocol (HTTP or HTTPS) that a client used to connect to your load balancer.


1 Answers

Your Startup.cs file is fine but you didn't configure ssl for Kesterl.

You need to configure a X.509 ssl certificate for your .net core application

From https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-2.2#when-to-use-kestrel-with-a-reverse-proxy-1

Only the reverse proxy server requires an X.509 certificate, and that server can communicate with the app's servers on the internal network using plain HTTP

So in your case you have 7 main steps to do:
1) Get a SSL certificate from CloudFlare ORIGIN CA
2) Install the ssl certificate in your server
3) Create a PFX file so your Kesterl Server can read it.
4) Configure Kesterl https end points.
5) Modify your Nginx server to listen to port 443 and then redirect to Kestrel listening ports
6) Modify service files accordingly your configuration
7) Change Cloudflare to run Strict mode

Step 1 - Get ssl certificate from cloudflare:
Go to your dashboard at Cloudflare and choose SSL/TLS --> Origin Server -->Create Certificate. Follow the instructions and you will have 2 files at the end of the process: example.com.key example.com.pem

Step 2 - Place the files in your server at etc/ssl folder

Step 3 - Create PFX file so Kesterl can use the certificate:
run this at your server:

openssl pkcs12 -export -out example.pfx -inkey example.com.key -in example.com.pem

This will generate the example.pfx, place it in the same folder.

Step 4 - Configure Kesterl https end points.
From step 3 you should have got a password that you need to use at this step.
At your appsetting.json place this lines:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Kestrel": {
    "Endpoints": {
      "HTTPS": {
        "Url": "https://localhost:5001",
        "Certificate": {
          "Path": "/etc/ssl/example.pfx",
          "Password": "**********"
        }
      }
    }
  }

Step 5 - Configure your Nginx to listen on port 443 like so:

server {
    #listen        80;
    listen                 *:443 ssl;
    ssl_certificate        /etc/ssl/example.pem;
    ssl_certificate_key    /etc/ssl/example.key;
    server_name            example.com
    location / {
        proxy_pass         https://localhost:5001/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Step 6 - configure your service file like so:

  [Unit]
    Description=example

    [Service]
    WorkingDirectory=/var/www/example
    ExecStart=/usr/bin/dotnet /var/www/example/exampleSite.dll
    Restart=always
    # Restart service after 10 seconds if the dotnet service crashes:
    RestartSec=10
    KillSignal=SIGINT
    SyslogIdentifier=example
    User=www-data
    Environment=ASPNETCORE_ENVIRONMENT=Staging
    Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
    Environment=ASPNETCORE_HTTPS_PORT=5001
    Environment=ASPNETCORE_URLS=https://localhost:5001

    [Install]
    WantedBy=multi-user.target

Step 7 - Change Cloudflare at SSL/TLS to use Strict mode

Restart your application + Nginx and your server should now work like this:

Request --> Cloudflare(HTTPS) --> Nginx(HTTPS)-->Example.com

like image 145
Alon Shmiel Avatar answered Sep 17 '22 18:09

Alon Shmiel