【问题标题】:How to get the real client ip by usig nginx in docker如何在docker中使用nginx获取真实客户端ip
【发布时间】:2020-03-04 09:06:55
【问题描述】:

我在 docker 网络中使用 .NET Core 后端、角度前端和 nginx 作为反向代理。我从本地浏览器或同一局域网中的另一个客户端调用前端。因此没有使用额外的代理。我现在尝试确定客户端的真实 IP 地址。我得到了几个未知的 IP 地址,我无法 ping,但不是客户端之一。

我做错了什么或忘记了什么?

这里是 Startup.cs 的一部分:

virtual public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders =
            ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    });

    ...
}

virtual public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
        ForwardLimit = null, // null = disable check
        RequireHeaderSymmetry = false,
        //KnownProxies = { IPAddress.Parse("<IP Adresse 1>")},
    });

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseAuthentication();

    app.UseMvc();           
}

这里是 nginx.conf:

worker_processes  1;
error_log  /etc/nginx/logs/error.log;

events { 
}

http {

  map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }

  upstream ${SERVER_NAME} {
    server backend_app:4200;
    server 127.0.0.1:4200;
  }

  server {
    listen 443 ssl; 

    server_name ${SERVER_NAME};
    keepalive_timeout   70;

    ssl_certificate /etc/nginx/certs/cert.crt;
    ssl_certificate_key /etc/nginx/certs/cert.key;

    ssl_prefer_server_ciphers on;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    location / {
        proxy_pass http://${SERVER_NAME};
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
  }
}

减少 docker-compose:

version: '3.7'

services:
  frontproxy:
    networks:
      frontproxy_default: {}
      public_net:
        ipv4_address: 192.168.165.137
  application_app:    
    networks:
      frontproxy_default: {}
      test_network: {}   
  application_sql:    
    networks:
      - test_network   
  application_api:    
    container_name: api
    networks:
      - test_network

networks:
  test_network:
    name: test_network
  frontproxy_default:
    name: frontproxy_default
  public_net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.0.0/24
          #gateway: 192.168.165.1

【问题讨论】:

    标签: c# docker nginx .net-core


    【解决方案1】:

    如果您想控制 docker 映像的 IP 地址,最好自己设置整个编排。

    Docker-compose yaml 文件让您可以选择自己设置 IP 地址。

    docker-compose 默认配置网络并自行分配 ip 地址。您可以使用自定义网络覆盖默认网络。

    新的docker-compose.yaml 文件应如下所示:

    在我的例子中,DB 是 mysql 服务器,API 是 .NET Core WebAPI 项目。 WEB1/2 是两个前端服务器,对我来说又是 .NET Core MVC 项目。

    version: '3.3'
    services:
      moviedb:
        image: mysql
        command: --default-authentication-plugin=mysql_native_password
        environment:
          MYSQL_ROOT_PASSWORD: password@123
          MYSQL_DATABASE: moviedb
          MYSQL_USER: movieadmin
          MYSQL_PASSWORD: password@123
        volumes:
          - C:\MvcMovie:/var/lib/mysql
          - ./MvcMovie/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
        networks:
          public_net:
            ipv4_address: ${MVCMOVIE_DB_1_IP}
      movieapi:
        build: 
            context: .
            dockerfile: Dockerfile.api
        depends_on:
          - moviedb
        networks:
          public_net:
            ipv4_address: ${MVCMOVIE_API_1_IP}
      movieweb1:
        build: 
            context: .
            dockerfile: Dockerfile.web
        networks:
          public_net:
            ipv4_address: ${MVCMOVIE_WEB_1_IP}
      movieweb2:
        build: 
            context: .
            dockerfile: Dockerfile.web
        networks:
          public_net:
            ipv4_address: ${MVCMOVIE_WEB_2_IP}
      haproxy:
        image: haproxy
        ports:
          - 8080:80
        expose:
          - 80
        networks:
          public_net:
             ipv4_address: ${HA_PROXY_IP}
        environment:
          - MVCMOVIE_API_1_IP=${MVCMOVIE_API_1_IP}
          - MVCMOVIE_WEB_1_IP=${MVCMOVIE_WEB_1_IP}
          - MVCMOVIE_WEB_2_IP=${MVCMOVIE_WEB_2_IP}
          - MVCMOVIE_EXPOSED_PORT=${MVCMOVIE_EXPOSED_PORT}
        volumes:
          - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    networks:
      public_net:
        driver: bridge
        ipam:
          driver: default
          config:
            - subnet: ${NETWORK_SUBNET}
    

    特别注意缩进。

    在 docker-compose.yaml 旁边添加一个 .env 文件,并使用如下类似的内容。更改名称和端口以满足需要。

    MVCMOVIE_EXPOSED_PORT=80
    
    MVCMOVIE_DB_1_IP=192.168.0.10
    
    MVCMOVIE_API_1_IP=192.168.0.20
    
    MVCMOVIE_WEB_1_IP=192.168.0.30
    MVCMOVIE_WEB_2_IP=192.168.0.31
    
    
    HA_PROXY_IP=192.168.0.100
    
    NETWORK_SUBNET=192.168.0.0/24
    

    话虽如此。您永远不应该针对 IP 地址进行编码。 如果我是你,我会做以下事情之一:

    1. 改用服务名称。
    2. 将 IP 地址设置放在 .env 文件中,并在 ipv4_address: ${server1_ip_address} 或类似的地方使用环境变量。
    3. 考虑使用 Kubernetes。让您的后端服务器位于负载平衡器后面。

    祝你好运。

    【讨论】:

    • 我将您的观点添加到我的 docker-compose 中(请参阅我的帖子)。作为 nginx-proxy 的 IP 地址,我使用了服务器的真实 IP。但是作为 X-Forwarded-For-IP-adress 我现在得到了服务器的网关地址,而不是客户端的 IP。我尝试添加网关,但出现“意外”错误。你有什么想法吗?
    • 快速观察。对于子网 192.168.0.0/24,您用于前端代理的 IP 地址:192.168.165.137 不正确。您只能拥有 192.168.0.0 到 192.168.0.255。问:为什么需要前端服务器的ip地址?
    • 一些建议是将端口添加到您的 yaml 文件中,这样会公开正确的端口。
    • 我使用了 192.168.165.137 因为你写了 ${server1_ip_address} ,这是我们局域网中服务器的 IP 地址。 :-) 还有什么可能是正确的?
    • 在 docker 中,您必须始终注意您在网络的哪一侧。在您的本地系统上有一个单独的网络。添加 docker-compose.yaml 时有一个新网络。本地计算机 LAN 和 docker 内部网络之间的唯一桥梁是 yaml 或 -p 8000:80 中的 ports 命令。如果你想为这些中的每一个启动 shell。你必须为所有容器前端、后端、数据库等公开端口。
    猜你喜欢
    • 2011-09-02
    • 1970-01-01
    • 2020-08-23
    • 2012-07-22
    • 2016-12-05
    • 2020-05-22
    • 1970-01-01
    • 2012-08-04
    • 2020-10-21
    相关资源
    最近更新 更多