Лак автоматическое добавление IP балансировщика нагрузки в заголовок X-Forwarded-For

мой поток запросов выглядит следующим образом;

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

когда новый запрос поступает в HAProxy, IP-адрес клиента добавляется в X-Forwarded-For заголовок (что хорошо!). Тем не менее, похоже, что лак добавляет 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. С момента последнего IP в X-Forwarded-For заголовок-это HAProxy, проверка ACL не выполняется для всех IPs. Вот соответствующий раздел моей конфигурации:

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

}

любые идеи, как я могу полагаться исключительно на X-Forwarded-For заголовок от HAProxy, без подделки лака?

Примечание стороны, кажется, что лак делает именно это (хотя это не в конфигурации 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;
    }
}

3 ответов


я также столкнулся с этой проблемой сегодня.

на default.vcl в varnish 4.0 был переименован в builtin.vcl и не содержит set req.http.X-Forwarded-For часть, которую вы упомянули - ссылке. Тем не менее он явно добавляет в разделенный запятыми список промежуточный IP - адрес прокси в соответствии со спецификациями протокола -ссылка на Википедию.

одним из решений было бы использовать X-Real-IP заголовок вместо этого, перезаписывая этот заголовок все время в HAProxy с реальным ip-адрес клиента и использование этого для VCL ACL.

другое решение, как (ошибочно) упоминается в лаковом форуме, будет 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 ответ

вы также можете проверить, почему в вашем VCL_call RECV вы ReqUnset X-Forwarded-For перед матчем ACL.


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 выполняется впереди vcl_recv логика. Какое значение вы видите для X-Forwarded-For внутри vcl_deliver если вы определяете свой собственный?

одна вещь, которую вы могли бы сделать, это разобрать первый IP-адрес, как это, где необходимо:

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

исходный код Vanish переместился в GitHub, поэтому для справки, начиная с версии 4.0, логика X-Forwarded-For была перемещена из builtin.vcl (ранее default.vcl), а исходную логику можно найти в bin / varnishd / cache / cache_req_fsm.c.