使用 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 天。