12

Problem description:
When I access Asp.net MVC Core 3.1 webpage that is supposed to return my remote client's ip address I instead get internal docker IP of Nginx container. I'm not sure if the problem is my code in asp.net core, the Nginx reverse proxy configuration not forwarding my client ip to Kestrel container, or something else.

Output:

::ffff:172.23.0.3

Desired Output (example using random ip):

231.43.6.124

Setup:

  • Remote VPS using Ubuntu and running Docker (i.e. not running locally)
  • Kestrel docker container that has no public access
  • Nginx Docker container that has public access on port 80 and forwards requests to backend kestrel server.

nginx.conf

http {
        upstream backendservers {
                server kestrel:80;
        }
        server {
                listen 80;
                location / {
                        proxy_pass http://backendservers/;
                        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 $host:443;
                        proxy_set_header X-Forwarded-Server $host;
                        proxy_set_header X-Forwarded-Port 443;
                        proxy_set_header X-Forwarded-Proto https;
                }
        }
}

events {

}

asp.net core code

// controller
public IActionResult GetIP()
{
   var ip = HttpContext.Connection.RemoteIpAddress.ToString();
   ViewBag.ip = ip;
   return View();
}

// view
@{
    ViewData["Title"] = "Get IP";
}

<div class="text-center">
   <p>@ViewBag.ip</p>
</div>

Docker startup:

docker network create --driver=bridge stacknet
docker run -d --name=kestrel --restart=always -h kestrel.local --network=stacknet mykestrelimage
docker run -d --name=nginx --restart=always  -p 80:80 -h nginx.local --network=stacknet mynginximage

UPDATE 1: As a test I opened the Kestrel 80 port. I can get client IP when I hit Kestrel directly I just can't get it when it's coming via Nginx reverse proxy.

UPDATE 2: Added some lines to ngix.conf as per suggestion of one of replies below but didn't seem to make a difference.

Anon Ymous
  • 139
  • 1
  • 2
  • 10
  • Are you implementing the available middleware for header forwarding? https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1 – Adam Jun 30 '20 at 20:01
  • No. I tried following "Forwarded Headers Middleware order" section of your link but keep getting error when I compile. (The name 'ForwardedHeaders' does not exist in the current context ) Is there some using statement I'm missing? – Anon Ymous Jul 01 '20 at 00:40
  • You’d need to add a using for `Microsoft.AspNetCore.HttpOverrides` – Adam Jul 01 '20 at 01:46
  • Can you elaborate with some sample code? Still get build errors. btw - as a test I openned up kestrel port 80 and the code does work if I'm not using nginx as a reverse proxy. – Anon Ymous Jul 01 '20 at 15:21
  • As you asked nginx to forward the IP as `X-Real-IP` header, read it directly in your code. – Lex Li Jul 18 '20 at 20:07
  • @Lex Li Whats the difference with what I'm doing with HttpContext.Connection.RemoteIpAddress? Can you provide some sample code? – Anon Ymous Jul 21 '20 at 12:59

2 Answers2

11

I had similar issue this is what i tried and it fixed my issue..

  1. go to /etc/nginx/sites-available/ you will found default file where you had configured your servers like this.
server{
  server_name example.com;
  location / {
    proxy_pass http://localhost:4000;
    proxy_set_header X-Real-IP $remote_addr; <---Add this line
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; <---this line too
  }

2.there will be one more file named proxy_params in /nginx folder make sure you have below lines in this file.

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  1. Most important in your back-end code get the IP like this.
const ipAddress = req.headers['x-forwarded-for'] as string;

You can also check the request ip through this command.

$ sudo tail -f /var/log/nginx/access.log
Subham kuswa
  • 356
  • 4
  • 12
  • So well written, so helpful. Thank you so much. – Prince Owen Dec 06 '20 at 16:34
  • 1
    In case someone has a similar case like mine. I had the same issue with a java backend application, which was using Javalin to expose a rest API. The solution for me was the step 3, thanks a lot for the detailed explanation. – Konstantinos Raptis Feb 07 '21 at 00:20
3

You may need to forward the original IP. Here is NGinx's doc on the subject: https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/

And an example of the older x-forwarded-for from: https://plone.lucidsolutions.co.nz/web/reverseproxyandcache/setting-nginx-http-x-forward-headers-for-reverse-proxy

location / {
  proxy_pass              http://upstream/;
  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   $host:443;
  proxy_set_header        X-Forwarded-Server $host;
  proxy_set_header        X-Forwarded-Port   443;
  proxy_set_header        X-Forwarded-Proto  https;
}
BookOfGreg
  • 3,550
  • 2
  • 42
  • 56
  • 1
    Thanks for the reply. I tried updating nginx.conf with your recommended settings (substituting upstream for my own) but it still giving me ngiix internal docker ip. – Anon Ymous Jul 01 '20 at 18:06