私のリクエストフローは次のとおりです。
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アドレスが必要な理由は、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;
}
}
私も今日この問題にぶつかりました。
ワニス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
_を実行する理由を確認することもできます。
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");
VanishソースコードはGitHubに移動したため、参考までに、バージョン4.0以降、X-Forwarded-Forロジックはbuiltin.vcl
(以前のdefault.vcl
)から移動され、ソースロジックは次のようになります。 bin/varnishd/cache/cache_req_fsm.c にあります。