Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`X-Forwarded-For` is not used by ASP.Net Core behind reverse proxy

I am trying to get the remote IP address of a request (i.e., the IP of the client who sent the request) in my ASP.Net Core 2.1 MVC Controller (runs in .Net Docker container). Taking into consideration that My ASP.Net Core application is located behind NGINX reverse proxy (runs in NGINX Docker container).

As known, when the reverse proxy redirects the request to my .Net Core application, it will change the source IP of my request (TCP/IP layer), therefore, I configured NGINX to add X-Forwarded-For with the original IP to the request. The request that is redirected from NGINX container to .Net container has X-Forwarded-For in the header:

enter image description here

And of course, I configured .Net Core to know about that:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
    // Rewrite the header when being redirected (this is required because we are behind reverse proxy)
    var forwardedHeadersOptions = new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
        KnownProxies = { IPAddress.Parse("172.20.10.2"), IPAddress.Parse("172.20.10.3"), IPAddress.Parse("172.20.10.4") },
    };

    forwardedHeadersOptions.KnownNetworks.Add(
        new IPNetwork(IPAddress.Parse("172.0.0.0"), 8));
    forwardedHeadersOptions.KnownNetworks.Add(
        new IPNetwork(IPAddress.Parse("127.0.0.1"), 8));

    app.UseForwardedHeaders(forwardedHeadersOptions);
    ...

However, HTTPContext.Connection.RemoteIPAddress still returning 172.20.10.3 (NGINX container IP, not the real remote IP):

logger.LogDebug("Remote IP Address: " + Request.HttpContext.Connection.RemoteIpAddress.ToString());

Remote IP Address: 172.20.10.3

I checked my header in .Net Core, it has X-Forwarded-For with the correct original remote IP address:

logger.LogDebug("X-Forwarded-For Header Feature: " + HttpContext.Request.Headers["X-Forwarded-For"]);

X-Forwarded-For Header Feature: 85.XX.4.121

Does someone have any idea what I am missing? Why RemoteIPAddress still returning the IP of the NGINX docker container instead of the real remote IP address?

Update

My Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseUrls("http://*:5000")
            .UseStartup<Startup>()
            .Build();
}

I tried Also to configure the the ForwarededHeadersOptions by configuring its service like this:

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    //options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("172.0.0.0"), 8));
    options.RequireHeaderSymmetry = false;
    options.ForwardLimit = null;
    options.KnownProxies.Add(IPAddress.Parse("172.20.10.3"));
    //options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("127.0.0.1"), 8));
});

With no success...

UPDATE 2

OK I guess I am on the right way, the IP address returned by RemoteIPAddress was ::ffff:172.20.10.20, not 172.20.10.20! I did not know that they are different. The official documentation helped me to discover that.

like image 408
Mohammed Noureldin Avatar asked Sep 30 '18 11:09

Mohammed Noureldin


People also ask

What is UseForwardedHeaders .NET core?

UseForwardedHeaders(IApplicationBuilder) Applies forwarded headers to their matching fields on the current request. By convention, HTTP proxies forward information from the client in well-known HTTP headers. The ForwardedHeadersMiddleware reads these headers and fills in the associated fields on HttpContext.

What is reverse proxy in .NET core?

A reverse proxy is a special type of proxy server that hides the target server to the client. The client requests a resource to the proxy server which retrieves it from another server and provides it to the client. In this case, the client has no idea that the resource comes from another server.

How do I get X forwarded in C#?

Request. Headers["X-Forwarded-For"]. Split(new char[] { ',' }). FirstOrDefault();


2 Answers

The IP I got from RemoteIPAddress was IPv4 represented as IPv6 (was ::ffff:172.20.10.20, not 172.20.10.20!). I was using only IPv4 part of the IP, therefore the KnownProxies in my case were wrong. I should have entered the full IPv4 address also with the v6 representation part.

The official documentation showed that:

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.1

UPDATE as requested by a comment:

So in my case, the code should look like this:

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.RequireHeaderSymmetry = false;
    options.ForwardLimit = null;
    options.KnownProxies.Add(IPAddress.Parse("::ffff:172.20.10.20")); // <<-- Notice the full format of the IP address.
});
like image 185
Mohammed Noureldin Avatar answered Sep 21 '22 14:09

Mohammed Noureldin


As per the official documentation, if you are using Apache or Nginx integration, following code should be added to the Startup.ConfigureServices method.

// using Microsoft.AspNetCore.HttpOverrides;

    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | 
            ForwardedHeaders.XForwardedProto;
        // Only loopback proxies are allowed by default.
        // Clear that restriction because forwarders are enabled by explicit 
        // configuration.
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });

and then on top of everything, in Configure method use

app.UseForwardedHeaders();

Further suppose in nginx conf file, inside a location, use

proxy_set_header   Host $host;
proxy_set_header   X-Real-IP $remote_addr;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Host $server_name;

Now the first entry in the X-Forwarded-For will be the real client IP.

IMPORTANT: If you want to secure the app and not allow an attacker inject X-Forwarded-For, Please read this answer.

Please see Forward the scheme for Linux and non-IIS reverse proxies, Configure Nginx and Dealing with invalid headers

like image 22
Sayyed Dawood Avatar answered Sep 22 '22 14:09

Sayyed Dawood