web-dev-qa-db-ja.com

X-Forwarded-ForヘッダーにロードバランサーIPを自動的に追加するワニス

私のリクエストフローは次のとおりです。

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

新しいリクエストがHAProxyに届くと、クライアントのIPアドレスがX-Forwarded-Forヘッダーに追加されます(これは良いことです!)。ただし、VarnishはHAProxyIPも追加しているようです。リクエストが私の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アドレスが必要な理由は、PURGE/BANのACLルールと照合できるようにするためです。 X-Forwarded-Forヘッダーの最後のIPはHAProxyのIPであるため、ACLチェックはすべてのIPで失敗します。これが私の設定の関連セクションです:

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

}

Varnishが改ざんすることなく、HAProxyのX-Forwarded-Forヘッダーのみに依存する方法はありますか?

ちなみに、Varnishはまさにこれを行っているようです(ただし、これはIS 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;
    }
}
12
Benjamin Smith

私も今日この問題にぶつかりました。

ワニス4.0の_default.vcl_は_builtin.vcl_で名前が変更され、あなたが言及した_set req.http.X-Forwarded-For_の部分は含まれていません link 。それにもかかわらず、彼はプロトコル仕様に従って中間プロキシIPアドレスをコンマ区切りのリストに明確に追加します Wikipediaリンク

1つの解決策は、代わりに_X-Real-IP_ヘッダーを使用し、HAProxyでこのヘッダーを常に実際のクライアントIPで上書きし、これをvclACLに使用することです。

(間違って)言及されている別の解決策 ワニスフォーラムで は、左端のIPアドレスを取得するregsub(req.http.X-Forwarded-For, "[, ].*$", "")です。ただし、このヘッダーは簡単にスプーフィングされる可能性があるため、このメソッドはNOT SECUREです。

私の提案は、既知の部分を抽出し、次のようにヘッダーから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."));
}
_

これに関する唯一の問題は、接続に3つ以上のホップがある場合です。また、プロキシを使用してインターネットに接続します。信頼できるホップを定義できるため、これに対する適切なソリューションはnginxによって提供され、実際のクライアントIPまで再帰的に無視されます。

_set_real_ip_from 192.168.1.101;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
_

これについての詳細は、これで確認できます serverfault thread answer

ACLが一致する前に、_VCL_call RECV_で_ReqUnset X-Forwarded-For_を実行する理由を確認することもできます。

9
Razvan Grigore

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ロジックよりも先に実行されているように見えることです。独自に定義した場合、X-Forwarded-For内のvcl_deliverにどのような値が表示されますか?

できることの1つは、必要に応じて次のように最初のIPアドレスを解析することです。

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

VanishソースコードはGitHubに移動したため、参考までに、バージョン4.0以降、X-Forwarded-Forロジックはbuiltin.vcl(以前のdefault.vcl)から移動され、ソースロジックは次のようになります。 bin/varnishd/cache/cache_req_fsm.c にあります。

2
jdeathe