我可以使用 HAProxy 的新“捕获”功能将远程地址保存在 TCP 前端,并将其用作 HTTP 后端中的“X-Forwarded-For”标头吗?

我可以使用 HAProxy 的新“捕获”功能将远程地址保存在 TCP 前端,并将其用作 HTTP 后端中的“X-Forwarded-For”标头吗?

使用 HAProxy 1.6 和聪明的黑客,我现在有一个 HAProxy tcp 模式前端,它可以检测浏览器是否支持 SNI,并根据检测结果路由到强加密 SSL 终端后端或较弱的后端。这确保了 SSL 实验室的 A+ 评级,同时仍允许除 IE6 之外的所有浏览器使用 SSL。

这是我的配置。它包含一些模板变量,这些变量应该是不言自明的,但不在与我的问题相关的区域:

frontend https_incoming
 bind 0.0.0.0:443
 mode tcp
 option tcplog
 tcp-request inspect-delay 5s
 tcp-request content accept if { req.ssl_hello_type 1 }
 use_backend https_strong if { req.ssl_sni -m end .transloadit.com }
 default_backend https_weak

backend https_strong
 mode tcp
 option tcplog
 server https_strong 127.0.0.1:1665

frontend https_strong
 bind 127.0.0.1:1665 ssl crt ${DM_ROOT_DIR}/envs/ssl/haproxy-dh2048.pem no-sslv3 no-tls-tickets ciphers ${strongCiphers}
 mode http
 option httplog
 option httpclose
 option forwardfor if-none except 127.0.0.1
 http-response add-header Strict-Transport-Security max-age=31536000
 reqadd X-Forwarded-Proto:\ https
 reqadd FRONT_END_HTTPS:\ on
 use_backend http_incoming

backend https_weak
 mode tcp
 option tcplog
 server https_weak 127.0.0.1:1667

frontend https_weak
 bind 127.0.0.1:1667 ssl crt ${DM_ROOT_DIR}/envs/ssl/haproxy.pem no-sslv3 ciphers ${weakCiphers}
 mode http
 option httplog
 option httpclose
 option forwardfor if-none except 127.0.0.1
 http-response add-header Strict-Transport-Security max-age=31536000
 reqadd X-Forwarded-Proto:\ https
 reqadd FRONT_END_HTTPS:\ on
 use_backend http_incoming

问题:https_incoming前端知道客户端 IP,但由于它处于 中mode tcp,因此无法将此信息保存在mode http X-Forwarded-For标头中。option forwardfor在 TCP 模式下无效。

关于 serverfault 的另一个问题 我已经发现我可以使用:

  • 低压开关柜
  • PROXY 协议

因此,X-Forwarded-For据我了解,就 LVS 而言,标头不再需要:数据包被欺骗,因此源变为客户端 IP;而就 PROXY 而言,数据包被封装以携带客户端 IP。

这两种方式似乎都可以。但是 LVS 对我们来说似乎是一项大手术,可能会产生副作用,而 PROXY 的缺点是代理/应用程序上游/下游可能尚未完全兼容。

我真的很希望能有更轻便的东西,就在那时,我发现了新的HAProxy 1.6 的“捕获”功能正如它提到的:

您可以声明捕获槽,在其中存储数据并在会话期间随时使用它。

它继续显示以下示例:

defaults 
 mode http

frontend f_myapp
 bind :9001
 declare capture request len 32 # id=0 to store Host header
 declare capture request len 64 # id=1 to store User-Agent header
 http-request capture req.hdr(Host) id 0
 http-request capture req.hdr(User-Agent) id 1
 default_backend b_myapp

backend b_myapp
 http-response set-header Your-Host %[capture.req.hdr(0)]
 http-response set-header Your-User-Agent %[capture.req.hdr(1)]
 server s1 10.0.0.3:4444 check

在我看来,信息存储在前端,然后在后端使用,所以也许我可以以 TCP 模式获取客户端 IP,保存它,然后在稍后使用它,可能像这样:

http-response set-header X-Forwarded-For %[capture.req.hdr(0)]

我看过捕获文档并且似乎捕获更多是针对 http 模式标头的,但我还看到了邮件列表对话成功演示了如何使用tcp-request capture

我尝试了几件事,其中包括:

tcp-request capture req.hdr(RemoteAddr) id 0
# or
tcp-request content capture req.hdr(RemoteHost) id 0

但正如你所看到的,我不知道语法应该是什么,也不知道这些信息在哪个键下可用,我也无法在(我认为)相关文件

问题:是否可以在 TCP 模式下捕获客户端 IP,然后X-Forwarded-For在 HTTP 模式下将此信息写入标头?如果可以,那么语法是什么?

答案1

回答我自己的问题,这似乎是不可能的,因为流量在这里“离开”HAProxy:

       TCP                             HTTP
frontend->backend (->leaving->) frontend->backend

因此上下文丢失,捕获无法保留。相反,正如“PiBa-NL”昨天在 Freenode 上的 #haproxy IRC 上所建议的那样:

[5:29pm] PiBa-NL: kvz, use proxy-protocol between back and front
[5:54pm] kvz: PiBa-NL: Thanks, does this mean my app also needs to understand 
         the proxy protocol, or will it be 'stripped' once it reaches the 
         backend. I don't think my node.js server could handle it without  
         significant changes to its stack
[6:07pm] kvz: Or let me rephrase: could I enable the proxy protocol on the first 
         frontend, then 'unwrap' it in the second frontend, taking the client ip 
         and putting it into the http header - so that my app would not have to 
         be proxy protocol compatible, and it would just be means to carry the 
         client ip from first frontend to the second?
[6:49pm] PiBa-NL: kvz, the last part you can still use the x-forwarded-for header
[6:50pm] PiBa-NL: but between haproxy backend and frontend you would use the 
         proxyprotocol to make the second frontent 'know' the original client ip
[6:50pm] PiBa-NL: server https_strong 127.0.0.1:1665 send-proxy
[6:50pm] PiBa-NL: bind 127.0.0.1:1665 ssl crt .... accept-proxy
[6:52pm] PiBa-NL: the second frontend can then still use the  
         'option forwardfor', and it should insert the wanted header
[6:53pm] PiBa-NL: so basically 'yes' 

这意味着 PROXY 协议仅用于将两个前端粘合在一起,封装客户机 IP,但第二个前端将其解开并X-Forwarded-For通过 将其保存在标头中option forwardfor,以便其后端可以向我的应用服务器发送无 PROXY 协议的请求,这意味着我不必担心上下游的兼容性问题。

答案2

HAProxy 应该已经添加了 X-Forwarded-For 标头。如果其中任何一个不是标准的,您可能需要添加协议和/或端口。

我通常使用一个回显请求标头的页面来测试这种行为。这样可以轻松查看哪些标头可用以及它们的内容是什么。

X-Forward-For 包含地址列表并不罕见。这表明请求已通过多个代理,或者有人在伪造标头。最右边的地址将是处理请求的最后一个代理 (ha-proxy) 添加的地址。

某些 Web 服务器可以配置为从标头而不是连接记录 IP 地址。这对于您的访问日志非常有用,并且在某些情况下,您可能希望根据传入连接的 IP 地址生成标头。

无需使用两个不同的堆栈,就可以获得 A+ 评级,同时支持除 IE 6 之外的所有列出的浏览器。

  • 对于 Java 6,至少禁用 DHE-RSA-AES128-SHA 和 AES128-SHA。所有密码可能都没有前向保密性。
  • 对于 WinXP/IE8 和 Java6,启用 DES-CBC3-SHA 和/或 EDH-DSS-DES-CBC3-SHA。这些应该是最不受欢迎的密码,因为它们不支持前向保密。

WinXP/IE8 和 Java 6 不支持使用安全协议的前向保密,因此测试不会因失败而惩罚您。如果您强制执行服务器排序,则所有其他浏览器都将使用前向保密。(如果您不这样做,Win Phones 会失败。)

A+ 评级需要设置严格传输安全,时间段至少为 180 天。

相关内容