【问题标题】:Varnish automagically adding load balancer IP to X-Forwarded-For headerVarnish 自动将负载均衡器 IP 添加到 X-Forwarded-For 标头
【发布时间】:2014-10-22 21:40:28
【问题描述】:

我的请求流程如下;

HAProxy --> Varnish (4.0.1) --> Apache web backends

当 HAProxy 收到新请求时,客户端的 IP 地址将被添加到 X-Forwarded-For 标头中(这很好!)。然而,看起来 Varnish 也在添加HAProxy IP。当请求到达我的vcl_recv 例程时,X-Forwarded-For 标头是:

X-Forwarded-For: end-user-ip, haproxy-ip

您可以在varnishlog 输出中看到:

*   << Request  >> 8
-   Begin          req 7 rxreq
-   Timestamp      Start: 1409262358.542659 0.000000 0.000000
-   Timestamp      Req: 1409262358.542659 0.000000 0.000000
-   ReqStart       192.168.1.103 48193
-   ReqMethod      PURGE
-   ReqURL         /some/path
-   ReqProtocol    HTTP/1.1
-   ReqHeader      Authorization: Basic xxx
-   ReqHeader      User-Agent: curl/7.30.0
-   ReqHeader      Host: example.com
-   ReqHeader      Accept: */*
-   ReqHeader      X-Forwarded-For: 1.2.3.4
-   ReqHeader      Connection: close
-   ReqUnset       X-Forwarded-For: 1.2.3.4
-   ReqHeader      X-Forwarded-For: 1.2.3.4, 192.168.1.101
-   VCL_call       RECV
-   ReqUnset       X-Forwarded-For: 1.2.3.4, 192.168.1.101
-   VCL_acl        NO_MATCH purge_acl
-   Debug          "VCL_error(403, Not allowed.)"
-   VCL_return     synth

我需要准确的客户端 IP 地址的原因是,我可以根据 ACL 规则检查它是否符合 PURGE/BAN。由于X-Forwarded-For 标头中的最后一个 IP 是 HAProxy 的,因此所有 IP 的 ACL 检查都失败。这是我的配置的相关部分:

acl purge_acl {
    "1.2.3.4";
}

sub vcl_recv {

    set req.backend_hint = load_balancer.backend();

    if (req.method == "PURGE") {
        if (!std.ip(req.http.X-forwarded-for, "0.0.0.0") ~ purge_acl) {
            return(synth(403, "Not allowed."));
        }
        ban("obj.http.x-url ~ " + req.url);
        return(synth(200, "Ban added"));
    }

}

有什么想法可以让我完全依赖 HAProxy 的 X-Forwarded-For 标头,而不用 Varnish 篡改它吗?

附带说明,Varnish 似乎正是这样做的(尽管这不在 mv VCL 配置中)

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;
    }
}

【问题讨论】:

    标签: varnish varnish-vcl


    【解决方案1】:

    Varnish 将其默认逻辑附加到您定义的任何函数,例如 vcl_recv,而不是纯粹覆盖它。默认vcl_recv 逻辑包含:

    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_recv 中的 Varnish 默认逻辑似乎在您的vcl_recv 逻辑之前执行。如果您定义自己的值,您认为vcl_deliver 中的X-Forwarded-For 有什么价值?

    您可以做的一件事是在必要时像这样解析出第一个 IP 地址:

    set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For, "^([^,]+),?.*$", "\1");
    

    【讨论】:

    【解决方案2】:

    我今天也遇到了这个问题。

    varnish 4.0 中的 default.vclbuiltin.vcl 中重命名,并且不包含您提到的 set req.http.X-Forwarded-For 部分 - link。尽管如此,他还是根据协议规范清楚地将中间代理 IP 地址附加到逗号分隔列表中 - Wikipedia link

    一种解决方案是改用 X-Real-IP 标头,在 HAProxy 中始终使用真实客户端 IP 覆盖此标头,并将其用于 vcl ACL。

    另一个(错误地)提到in the varnish forum 的解决方案是regsub(req.http.X-Forwarded-For, "[, ].*$", ""),它采用最左边的IP 地址。但是,此方法不安全,因为此标头很容易被欺骗。

    我的建议是提取已知部分,像这样从标头中清漆 IP:

    if (!std.ip(regsub(req.http.X-Forwarded-For, ", 192\.168\.1\.101$", ""), "0.0.0.0") ~ purge_acl) {
        return(synth(403, "Not allowed."));
    }
    

    唯一的问题是连接中是否有超过 2 个跃点,例如。您还使用代理连接到互联网。 nginx 提供了一个很好的解决方案,因为您可以定义受信任的跃点,并且它们会被递归地忽略,直到真正的客户端 ip。

    set_real_ip_from 192.168.1.101;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;
    

    您可以在此serverfault thread answer 中查看更多详细信息

    您可能还想检查为什么在您的 VCL_call RECV 中您在 ACL 匹配之前执行 ReqUnset X-Forwarded-For

    【讨论】:

      【解决方案3】:

      Vanish 源代码已移至 GitHub,因此,作为参考,自 4.0 版以来,X-Forwarded-For 逻辑已从 builtin.vcl(以前为 default.vcl)中移出,可以找到源逻辑在bin/varnishd/cache/cache_req_fsm.c

      【讨论】:

        猜你喜欢
        • 2016-01-25
        • 1970-01-01
        • 2013-10-03
        • 2020-07-04
        • 1970-01-01
        • 2021-03-07
        • 1970-01-01
        • 2017-05-30
        • 1970-01-01
        相关资源
        最近更新 更多