负载均衡器——>Varnish——>Nginx

负载均衡器——>Varnish——>Nginx

我已经挠头几个小时了,想看看是否有人可以帮忙。

1)我有一个带有后端6台服务器的负载均衡器。

2)后端服务器是 Nginx,为了获取访问者的真实 IP 地址,我所要做的就是在每个 Nginx 安装中执行以下操作,然后我就能获取每个访问者的真实客户端 IP 地址。

 set_real_ip_from 192.168.255.0/24; <-- to handle the load balancer IP
 real_ip_header X-Forwarded-For;

3)现在我在 127.0.0.1 上运行的每个 Nginx 前面都安装了 Varnish 进行缓存,由于某种原因,现在 Nginx 不再看到来自 LoadBalancer --> Varnish --> Nginx 的真实客户端 IP 地址

打印内容如下:

IP 地址:192.168.255.9 <-- 这应该是真正的客户端 IP 地址,而不是 192.168(假设正在打印负载均衡器 IP 地址)

更详细的主机地址:192.168.255.9

如果您能帮忙的话我将不胜感激。

戴夫

更新:

如果不考虑 Varnish,则有以下 LB --> NGINX,并且在 NGINX 中存在以下内容

set_real_ip_from  192.168.255.0/24;
real_ip_header X-Forwarded-For;

当 NGINX 记录 remote_addr 时,下面的第一个条目打印真实的客户端 IP 地址

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
              '$status $body_bytes_sent "$http_referer" '
              '"$http_user_agent" "$http_x_forwarded_for"';


213.205.234.x - - [05/Sep/2012:09:42:08 -0700] "GET /2011/10/28/chicken-and-apples-in- honey-mustard-sauce/ HTTP/1.1" 200 18283 "-" "Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9100 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30" "213.205.234.x"

使用 Varnish 的公式为 LB --> Varnish --> NGINX

在 NGINX 中,我将 set_real_ip_from 切换为指向 127.0.0.1

set_real_ip_from  127.0.0.1;
real_ip_header X-Forwarded-For;

NGINX 中的 $remote_addr 不会打印真实的客户端 IP 地址:

192.168.255.9 - - [05/Sep/2012:09:46:41 -0700] "GET /2012/09/03/stuffed-baked-potatoes-deconstructed/ HTTP/1.1" 200 18159 "-" "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; ADR6400L Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" "69.255.125.x, 192.168.255.9"

从上面可以看出,打印的 $remote_addr 是负载均衡器的 IP 地址:192.168.255.9,而不是客户端的 remote_addr。虽然“$http_x_forwarded_for”打印的是正确的地址,但我猜是:“69.255.125.x, 192.168.255.9”。我的目标是让 $remote_addr 保留正确的 IP 地址

谢谢戴夫

更新:

这是我从 Varnish 中获取的 default.vcl,注释掉了 Shane 提到的部分,这是 NGINX 访问日志的当前输出

127.0.0.1 - - [05/Sep/2012:11:16:43 -0700] "GET /wp-content/plugins/wp-pagenavi/pagenavi-css.css?ver=2.70 HTTP/1.1" 304 0 "http://mobilefoodblog.com/2011/10/28/chicken-and-apples-in-honey-mustard-sauce/" "Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; SCH-I405 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1" "67.168.209.192, 192.168.255.9"


    # This is a basic VCL configuration file for varnish.  See the vcl(7)
    # man page for details on VCL syntax and semantics.
    # 
    # Default backend definition.  Set this to point to your content
    # server.
    # 

    backend default {
         .host = "localhost";
         .port = "8080";
    }

    sub detect_device {
      # Define the desktop device
      set req.http.X-Device = "desktop";

      if (req.http.User-Agent ~ "iP(hone|od)" || req.http.User-Agent ~ "Android" || req.http.User-Agent ~ "iPad") {
        # Define smartphones and tablets
        set req.http.X-Device = "smart";
      }

      elseif (req.http.User-Agent ~ "SymbianOS" || req.http.User-Agent ~ "^BlackBerry" || req.http.User-Agent ~ "^SonyEricsson" || req.http.User-Agent ~ "^Nokia" || req.http.User-Agent ~ "^SAMSUNG" || req.http.User-Agent ~ "^LG") {
        # Define every other mobile device
        set req.http.X-Device = "other";
      }
    }

    acl purge {
            "localhost";
    }

    sub vcl_recv {
       call detect_device;

         # if (req.restarts == 0) {
         #  if (req.http.x-forwarded-for) {
         #      set req.http.X-Forwarded-For =
         #          req.http.X-Forwarded-For ", " client.ip;
         #  } else {
         #      set req.http.X-Forwarded-For = client.ip;
         #  }
         # }

       if (req.request == "PURGE") {
            if (!client.ip ~ purge) {
                 error 405 "Not allowed.";
            }
            return(lookup);
       }

    if (req.url ~ "^/$") {
          unset req.http.cookie;
        }
    }

    sub vcl_hit {
            if (req.request == "PURGE") {
                    set obj.ttl = 0s;
                    error 200 "Purged.";
            }
    }

    sub vcl_miss {
        if (req.request == "PURGE") {
                    error 404 "Not in cache.";
        }

        if (!(req.url ~ "wp-(login|admin)")) {
                            unset req.http.cookie;
        }

        if (req.url ~ "^/[^?]+.(jpeg|jpg|png|gif|ico|js|css|txt|gz|zip|lzma|bz2|tgz|tbz|html|htm)(\?.|)$") {
               unset req.http.cookie;
               set req.url = regsub(req.url, "\?.$", "");
        }

        if (req.url ~ "^/$") {
               unset req.http.cookie;
        }
    }

    sub vcl_fetch {
            if (req.url ~ "^/$") {
               unset beresp.http.set-cookie;
            }

        if (!(req.url ~ "wp-(login|admin)")) {
               unset beresp.http.set-cookie;
        }
    }

    sub vcl_hash {
         set req.hash += req.url;
         if (req.http.host) {
             set req.hash += req.http.host;
         } else {
             set req.hash += server.ip;
         }

         # And then add the device to the hash (if its a mobile device)
         if (req.http.X-Device ~ "smart" || req.http.X-Device ~ "other") {
           set req.hash += req.http.X-Device; 
         }

         return (hash);
    }

答案1

由于 Varnish 在每个运行 nginx 的服务器上运行,因此从 nginx 的角度来看连接的源是 127.0.0.1,而不再是负载均衡器。

set_real_ip_from 192.168.255.0/24;

X-Forwarded-For这就是问题所在;当连接源自 127.0.0.1(Varnish 进程)时,nginx 不会“信任”标头;它只信任整个 192.168.255.0/24 网络。添加授权以在 Varnish 发送标头时信任该标头。

set_real_ip_from 127.0.0.1;

编辑:

nginx 在解析X-Forwarded-For标头以获取“真实”客户端 IP 时表现不佳;它会查找标头中的最后一个条目,当有多个条目时,该条目永远不会是真实客户端 IP。请参阅这个问题有关此问题的更多信息。

我建议让 Varnish 停止添加自己的X-Forwarded-For标头。您需要删除该vcl_recv函数的这一部分:

if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
        set req.http.X-Forwarded-For =
            req.http.X-Forwarded-For + ", " + client.ip;
    } else {
        set req.http.X-Forwarded-For = client.ip;
    }
}

如果您需要帮助来了解需要进行哪些更改,请提供您当前的 vcl 配置,因为这可能是明确配置的或由默认值附加的(或两者兼而有之)。

编辑2:

将其替换为vcl_recv您 Varnish 配置中的功能;它将您配置的自定义与默认行为相结合,同时删除X-Forwarded-For默认存在的欺骗。

sub vcl_recv {
    call detect_device;
    if (req.request == "PURGE") {
        if (!client.ip ~ purge) {
            error 405 "Not allowed.";
        }
        return(lookup);
    }
    if (req.url ~ "^/$") {
        unset req.http.cookie;
    }
    # Default logic follows; it's normally appended.
    # It'll still be appended, but having the return(lookup)
    # prevents its use. X-Forward-For header behavior removed.
    if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
        /* Non-RFC2616 or CONNECT which is weird. */
        return (pipe);
    }
    if (req.request != "GET" && req.request != "HEAD") {
        /* We only deal with GET and HEAD by default */
        return (pass);
    }
    if (req.http.Authorization || req.http.Cookie) {
        /* Not cacheable by default */
        return (pass);
    }
    return (lookup);
}

答案2

听起来您的负载均衡器没有在任何标头中传递客户端 IP 地址。由于您没有说明您使用什么负载均衡器,因此无法给出具体的解决方案,但通常您需要配置负载均衡器以将客户端 IP 地址放在其中一个标头中(例如X-Forwarded-For)。如果您愿意,您还可以为此指定自定义标头。

如果您的负载均衡器设置了 X-Forwarded-For,那么我会将您的 Varnish 服务器配置为设置除 X-Forwarded-For 之外的标头,以便您可以完全了解客户端的 IP 地址以及哪个服务器处理了请求。

答案3

假设您的负载均衡器X-Forwarded-For同时使用 HTTP 和 HTTPS 的标头,那么...

...在 Varnish 中你应该有这个

sub vcl_recv { 
...
  set req.http.X-Forwarded-For = req.http.X-Forwarded-For;

...在 Nginx 中你应该有这个

server {
...
  set_real_ip_from 192.168.255.0/24;
  real_ip_header X-Forwarded-For;

我会补充您实际上应该只运行 1 个 Varnish 实例,这违背了拥有多个缓存的目的(因为负载平衡器可能会将请求传递给未启动的 Varnish 实例)。您的命中率会更差,更不用说这只是浪费资源。

相关内容