在使用 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
| builder.Services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear();
});
app.UseForwardedHeaders();
app.UseRouting(); app.UseAuthentication(); app.UseAuthorization();
app.MapControllers();
app.Run();
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
: 配置应用解析 X-Forwarded-For
和 X-Forwarded-Proto
头部。
KnownNetworks
和 KnownProxies
: 明确指定信任的网络和代理,确保只有来自这些范围的头部信息会被解析
检查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": [] }
|