HAProxy 跨域资源共享(CORS)OPTIONS 头截取设置

15

在我的NGinx设置中,我能够拦截来自ajax预检的OPTIONS请求,并使用正确的CORS标头和200响应进行响应,以便请求可以继续前进。我正在尝试将我的前端代理整合到HAProxy中,但在使这个部分工作时遇到了一些问题。

我的具体问题是,虽然我能够在有服务器能够适当地响应OPTIONS请求时添加正确的CORS选项,但一些后端服务器无法处理/响应发出预检请求时的405错误。我的haproxy.cfg包括以下行用于添加标头:

capture request header origin len 128
http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Credentials:\ true if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Headers:\ Origin,\ X-Requested-With,\ Content-Type,\ Origin,\ User-Agent,\ If-Modified-Since,\ Cache-Control,\ Accept if { capture.req.hdr(0) -m found }
rspadd Access-Control-Allow-Methods:\ GET,\ POST,\ PUT,\ DELETE,\ OPTIONS if { capture.req.hdr(0) -m found }
rspadd Access-Control-Max-Age:\ 1728000 if { capture.req.hdr(0) -m found }

在此解决方案中:

如何在不将请求传递到Web服务器的情况下使用HAProxy发送响应能够在从客户端请求设置了所有正确的标头时工作,但并不是动态的,这不是理想的解决方案。

任何帮助将不胜感激!


我收到了以下信息[2016-04-19 12:49:54.625] uaa - 5213 [http-bio-8080-exec-5] .... DEBUG --- CorsFilter: CORS processing completed for: URI: /login.do; Scheme: https; Host: uaa.testinception25.io; Port: 443; Origin: https://skyfallui-testinception25.com; Method: POST Status:403。请帮我设置相应的haproxy配置。 - Vikram Ranabhatt
4个回答

8

根据anine.io的优秀答案,我提出了以下解决方案,它允许定义允许来源列表,并为所有HTTP请求添加缺失的Acccess-Control-Allow-Origin头。 anine.io的答案只显示了CORS预检,但没有考虑正常请求。

haproxy.cfg中,在全局部分加载cors.lua文件(必要时调整路径)

global
    lua-load /usr/local/etc/haproxy/cors.lua

将CORS配置添加到您的前端定义中。
frontend http-in
    # CORS configuration
    # capture origin HTTP header
    capture request header origin len 128
    # add Access-Control-Allow-Origin HTTP header to response if origin matches the list of allowed URLs
    http-response add-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if !METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }
    # if a preflight request is made, use CORS preflight backend
    http-request use-service lua.cors-response if METH_OPTIONS { capture.req.hdr(0) -m reg -f /usr/local/etc/haproxy/cors-origins.lst }

创建一个名为 cors.lua 的文件,并将其存储在上面指定的路径下。该文件包含CORS预检请求,如果没有充分的理由,请不要限制方法或标头,因为您必须在CORS配置中定义的ACL中包含任何关于方法或标头的限制。haproxy.conf注意:目前浏览器不支持通配符*用于Access-Control-Allow-Methods标头。 cors.lua文件应包含以下内容。
core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "*")
    applet:add_header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)

创建一个名为 cors-origins.lst 的文件,并将其存储在上面在CORS配置中指定的路径下。该文件应包含正则表达式(或简单字符串)。如果客户端发送Origin标头,则它将与这些正则表达式进行验证,仅当它们匹配时,才会返回cors.lua中的CORS Preflight(对于HTTP OPTIONS请求),或者将带有客户端请求的Origin标头值的Access-Control-Allow-Origin添加到响应中。 cors-origins.lst 内容的示例可能是:
example.com
localhost.*
.*\.mydomain\.com:[8080|8443]

使用 http://test-cors.org/ 测试配置。对于 GET 请求,不应该有CORS Preflight。 对于除GET之外的请求,客户端应首先执行CORS Preflight请求(例如,HTTP OPTIONS调用)以检查是否允许所需的方法,标头和授权。

有关CORS的详细信息,请参见HTTP访问控制(CORS)


这是一个很棒的解决方案!有什么方法可以让它在启用http2时工作吗? - Jonathan S. Fisher

6

您可以使用Lua,但需要确保HAproxy是使用USE_LUA构建的,可以通过检查haproxy -vv来确认。

这是一个示例配置,我自己没有尝试过,但它会让您了解您可以做什么:

# haproxy.cfg

global
    lua-load cors.lua

frontend foo
    ...
    http-request use-service lua.cors-response if METH_OPTIONS { req.hdr(origin) -m found } { ... }

# cors.lua
core.register_service("cors-response", "http", function(applet)
    applet:set_status(200)
    applet:add_header("Content-Length", "0")
    applet:add_header("Access-Control-Allow-Origin", applet.headers["origin"][0])
    applet:add_header("Access-Control-Allow-Credentials", "true")
    applet:add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Origin, User-Agent, If-Modified-Since, Cache-Control, Accept")
    applet:add_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
    applet:add_header("Access-Control-Max-Age", "1728000")
    applet:start_response()
end)

4

我希望在这里提供自己的答案。我们经历了很多痛苦才得到一个可工作的设置。这个问题的其他答案非常有用,但没有为我们提供完全可工作的配置。

我们想要允许任何来源。如果您需要白名单来源,请参阅@Florian Feldhaus的答案以获取有用的正则表达式技巧。我们不使用白名单,而是回显位置标头:

http-request set-header Access-Control-Allow-Origin %[capture.req.hdr(0)] if { capture.req.hdr(0) -m found }

我们还需要明确设置Access-Control-Allow-HeadersAccess-Control-Expose-Headers这些标题中通配符的浏览器支持还不够。

因此,这是我们的配置所做的事情:

  1. 使用此cors.lua脚本处理预检请求
  2. 使用http-response set-header处理普通请求以添加Access-Control-Allow-*标题
  3. 调整tune.maxrewrite以适应我们的CORS标题(> 1 KB)

步骤1和2在其他答案中已经解释,但步骤3花费了我们很长时间才解决。我在这篇博客文章中记录了完整的配置和我们走过的路程。该文章包含指向github上gist的链接。


1
顺便说一句,博客文章写得很棒! - the dude

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接