Docker Swarm Nginx 获取客户端RealIP

在使用 Docker Swarm 部署应用时,通常会使用 Overlay 网络 来实现跨主机的容器通信。然而,这种网络模式下,Nginx 作为反向代理时,只能获取到来自内部网络的 IP 地址(如 10.0.0.2),而无法获取到客户端的真实公网 IP。

1
2
3
4
10.0.0.2 - - [06/Jan/2025:11:30:56 +0800] "GET /health-status HTTP/1.1" 200 196 "-" "-"
10.0.0.2 - - [06/Jan/2025:11:31:06 +0800] "GET /health-status HTTP/1.1" 200 196 "-" "-"
10.0.0.2 - - [06/Jan/2025:11:31:16 +0800] "GET /health-status HTTP/1.1" 200 197 "-" "-"
10.0.0.2 - - [06/Jan/2025:11:31:26 +0800] "GET /health-status HTTP/1.1" 200 196 "-" "-"

要解决这一问题,核心思路是 绕过 Docker Swarm 的 Overlay 网络 NAT 转换,使 Nginx 能够直接获取到客户端的真实 IP。具体方法是将 Nginx 服务配置为 host 模式,即将宿主机的网络栈直接分配给容器。Nginx 容器直接监听宿主机的网络接口,能够接收到来自客户端的真实 IP。

Nginx

修改docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
proxy:
image: nginx
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host

设置proxy-set-header

1
2
3
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;

应用配置

在 ASP.NET Core 应用中,需要配置 Forwarded Headers 中间件,以解析并使用 X-Forwarded-For 头部中的真实 IP。

Program.cs 中,添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 配置 Forwarded Headers
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

// 设置信任的代理 IP 范围
options.KnownNetworks.Clear(); // 清除默认配置
options.KnownProxies.Clear();

// 添加 Nginx 所在的 Overlay 网络 IP 范围
// options.KnownNetworks.Add(new IPNetwork(System.Net.IPAddress.Parse("10.0.0.0"), 8));
// options.KnownNetworks.Add(new IPNetwork(System.Net.IPAddress.Parse("172.16.0.0"), 12));
});

// 使用 Forwarded Headers 中间件,必须放在最前面
app.UseForwardedHeaders();
// 其他中间件
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto: 配置应用解析 X-Forwarded-ForX-Forwarded-Proto 头部。

KnownNetworksKnownProxies: 明确指定信任的网络和代理,确保只有来自这些范围的头部信息会被解析

检查Nginx 日志

其中192.168.1.12为客户端IP

1
2
3
192.168.1.12 - - [06/Jan/2025:11:20:33 +0800]
192.168.1.12 - - [06/Jan/2025:11:20:33 +0800]
192.168.1.12 - - [06/Jan/2025:11:20:33 +0800]

image-20250106114630079

验证应用端获取的 IP

通过控制器或日志记录查看 HttpContext.Connection.RemoteIpAddress 是否为真实客户端 IP

1
2
3
4
5
6
7
[HttpGet("client-ip")]
public IActionResult GetClientIp()
{
var forwardedFor = HttpContext.Request.Headers["X-Forwarded-For"];
var clientIp = HttpContext.Connection.RemoteIpAddress?.ToString();
return Ok(new { ClientIp = clientIp, forwardedFor= forwardedFor });
}

访问httP://yourip:port/client-ip

1
2
3
4
{
"clientIp": "192.168.1.12",
"forwardedFor": []
}