Joshua Rogers
Joshua Rogers

Reputation: 3548

Can't forward real client IP Address in a Blazor docker compose configuration with Nginx

I'm having some trouble to set up a Blazor server app to receive the real IP Address in a docker compose configuration with nginx in a reverse proxy configuration. The blazor app is running in an specific ipam network configuration. The Blazor app seems to be receiving nginx private IP Address inside the docker configuration, instead of the external client IP Address.

I have tried nearly everything I found in Google:

  1. added the options.ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor; flags and app.UseForwardedHeaders(); in the Blazor Program.cs file
  2. Added the previous ipam ip addresses to Blazor's KnownProxies and `KnownNetworks
  3. Added the real_ip_header and set_real_ip_from in the nginx.config as suggested by many answers I found on Google
  4. Added proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 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; to the nginx.config file to forward the Client IP Address

But the Blazor server, which is listening in port 5077 always retrieves the proxy address, 127.28.1.1 which corresponds with the Docker ipam network, instead of the Client Ip Address, which in my case should be 127.0.0.1 as I'm running docker in my local machine.

enter image description here

I will attach the configuration I have as well as a .zip with all the reprocucible project in case:

Program.cs:

using Microsoft.AspNetCore.HttpOverrides;
using NginxBlazorTest.Data;
using System.Net;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();


builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    //options.ForwardedHeaders = ForwardedHeaders.All;

    options.ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor;
    //options.ForwardLimit = null;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();

    options.KnownProxies.Add(IPAddress.Parse("::ffff:172.28.1.0"));
    options.KnownProxies.Add(IPAddress.Parse("::ffff:172.28.1.1"));
    options.KnownProxies.Add(IPAddress.Parse("::ffff:172.28.1.2"));
    options.KnownProxies.Add(IPAddress.Parse("::ffff:172.28.1.3"));
    options.KnownProxies.Add(IPAddress.Parse("172.28.1.0"));
    options.KnownProxies.Add(IPAddress.Parse("172.28.1.1"));
    options.KnownProxies.Add(IPAddress.Parse("172.28.1.2"));
    options.KnownProxies.Add(IPAddress.Parse("172.28.1.3"));
    

    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("::ffff:172.28.0.0"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("::ffff:172.28.0.1"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("::ffff:172.28.0.2"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("::ffff:172.28.0.3"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("172.28.0.0"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("172.28.0.1"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("172.28.0.2"), 8));
    options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("172.28.0.3"), 8));    
});

var app = builder.Build();
app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();

DockerFile:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 5077

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Debug
WORKDIR /src
COPY ["NginxBlazorTest/NginxBlazorTest.csproj", "NginxBlazorTest/"]
RUN dotnet restore "./NginxBlazorTest/./NginxBlazorTest.csproj"
COPY . .
WORKDIR "/src/NginxBlazorTest"
RUN dotnet build "./NginxBlazorTest.csproj" -c $BUILD_CONFIGURATION -o /app/build

FROM build AS publish
ARG BUILD_CONFIGURATION=Debug
RUN dotnet publish "./NginxBlazorTest.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "NginxBlazorTest.dll"]

docker-compose.yml:

version: '3.4'

services:
  nginxblazortest:
    container_name: nginxblazortest
    image: ${DOCKER_REGISTRY-}nginxblazortest
    build:
      context: .
      dockerfile: NginxBlazorTest/Dockerfile
    networks:
      - frontend_network 
    environment:
      - ASPNETCORE_ENVIRONMENT=Development      
      - URLS=http://+:5077;   
      - HTTP_PORT:5077;
 
  nginx:
      container_name: nginx
      image: nginx:alpine     
      networks:
      - frontend_network 
      ports:
      - 80:80
      - 8080:8080
      - 443:443
      volumes:
      - ./Nginx/nginx.conf:/etc/nginx/nginx.conf:ro  
      - ./Nginx/proxy_params:/etc/nginx/proxy_params:ro  
      depends_on:
      - nginxblazortest      
networks:
  frontend_network: 
     ipam:
      driver: host
      config:
        - subnet: 172.28.1.0/16
          ip_range: 172.28.1.0/24   

volumes:
  conf:
    driver: local      
  vhost:
    driver: local      
  certs:
    driver: local
  html:
    driver: local

nginx.conf:

user nginx;

events 
{
    worker_connections 4096;
}

http 
{ 
    proxy_set_header Host $host;
    proxy_pass_request_headers on;

    upstream nginxblazortest 
    {
        server nginxblazortest:5077;        
    }

    server {
        listen 80;
        listen [::]:80;

        server_name localhost 127.0.0.1;

        set_real_ip_from  172.28.1.0;
        set_real_ip_from  172.28.1.1;
        set_real_ip_from  172.28.1.2;
        set_real_ip_from  172.28.1.3;
        set_real_ip_from  172.28.1.0/16;
        set_real_ip_from  172.28.1.0/24;
        set_real_ip_from  127.0.0.1;
        set_real_ip_from  ::1;    
        set_real_ip_from  ::ffff:172.28.1.0;
        set_real_ip_from  ::ffff:172.28.1.1;
        set_real_ip_from  ::ffff:172.28.1.2;
        set_real_ip_from  ::ffff:172.28.1.3;
        set_real_ip_from  ::ffff:172.28.1.0/16;
        set_real_ip_from  ::ffff:172.28.1.0/24;
        set_real_ip_from  ::ffff:127.0.0.1;
        real_ip_header    X-Forwarded-For;
        real_ip_recursive on;

        location / {      
            include proxy_params;
            proxy_pass http://nginxblazortest/;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
            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;
        }
    }
}

Any help would be greatly appreciated, thanks in advance!

Update 1

I added some middleware in the Blazor app as suggested in the comments to capture the request headers as fast as they arrive without further manipulation, and the IPs are wrong, they still reflect the nGinx container IP instead of the Client IP's localhost address

enter image description here

Upvotes: 2

Views: 846

Answers (1)

Jalpa Panchal
Jalpa Panchal

Reputation: 12769

In your docker-compose.yml, you've used ipam with a custom subnet and IP range.If you don't have a specific need for this, consider removing the custom IPAM configuration and let Docker handle the networking. only add the IP of your NGINX container to the KnownProxies. In the nginx.conf, you have multiple redundant lines like proxy_set_header X-Real-IP $remote_addr;.

http {
       ...
       upstream nginxblazortest {
           server nginxblazortest:5077;        
       }

       server {
           ...
           set_real_ip_from  172.28.1.0/24;
           real_ip_header    X-Forwarded-For;
           real_ip_recursive on;

           location / {      
               include proxy_params;
               proxy_pass http://nginxblazortest/;
               proxy_set_header X-Real-IP $remote_addr;
               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
           }
       }
   }

Remove all known proxies and networks except the Docker subnet.

builder.Services.Configure<ForwardedHeadersOptions>(options =>
   {
       options.ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor;
       options.KnownNetworks.Clear();
       options.KnownProxies.Clear();

       options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("172.28.1.0"), 24)); 
   });

Make sure that no other middleware or service is adding to the X-Forwarded-For header after it gets to Blazor. try deploying the project to some different server and make a test.

Upvotes: 2

Related Questions