HAProxy CORS OPTIONS 标头拦截设置

Posted

技术标签:

【中文标题】HAProxy CORS OPTIONS 标头拦截设置【英文标题】:HAProxy CORS OPTIONS header intercept setup 【发布时间】:2015-12-21 08:36:00 【问题描述】:

通过我的 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 

中给出的解决方案:

How to send a response with HAProxy without passing the request to web servers 在您设置客户端请求的所有正确标头时有效,但不是动态的,这不是理想的解决方案。

任何帮助将不胜感激!

【问题讨论】:

我得到 [2016-04-19 12:49:54.625] uaa - 5213 [http-bio-8080-exec-5] .... DEBUG --- CorsFilter: CORS 处理完成对于:URI:/login.do;方案:https;主机:uaa.testinception25.io;端口:443;来源:skyfallui-testinception25.com;方法:POST 状态:403 帮我设置一下这个的haproxy 【参考方案1】:

基于great answer from 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 预检,如果没有充分的理由,请不要限制方法或标头,因为您必须在 haproxy.conf 的 CORS 配置中定义的 ACL 中包含有关方法或标头的任何限制。 Note: Currently Browsers do not support wildcard * for the Access-Control-Allow-Methods header. 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 请求)或带有源值的 Access-Control-Allow-Origin客户端请求的标头将添加到响应中。 cors-origins.lst 的内容示例可以是

example.com
localhost.*
.*\.mydomain\.com:[8080|8443]

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

有关 CORS 的更多详细信息,请参阅 HTTP access control (CORS)。

【讨论】:

这是一个很棒的解决方案!有什么方法可以让它在启用 http2 的情况下工作?【参考方案2】:

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

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

# 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)

【讨论】:

【参考方案3】:

我想在这里提出我自己的答案。我们经历了相当多的痛苦才能达到工作设置。此处对这个问题的其他答案非常有帮助,但并没有为我们提供完整的工作配置。

我们希望允许任何来源。如果您需要列入白名单的来源,请参阅answer from @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。 Browser support for wildcards in theses headers is not quite there yet.

这就是我们的配置所做的:

    使用this cors.lua script 处理预检请求 使用http-response set-header 处理正常请求以添加 Access-Control-Allow-* 标头 调整 tune.maxrewrite 以适应我们的 CORS 标头(大于 1 KB)

1) 和 2) 的步骤在此处的其他答案中进行了说明,但步骤 3) 花了我们很长时间才弄清楚。我在this blog post 中记录了完整的配置和带我们去那里的旅程。该帖子包含指向 github 上的要点的链接。

【讨论】:

btw 很棒的博文!【参考方案4】:

要拦截像我这样的 OPTIONS,我想在响应中添加 Access-Control-Max-Age 标头。我修改了我的haproxy.cfg 并将这一行添加到backend 块中。

backend api
  balance roundrobin
  http-response set-header Access-Control-Max-Age 600 if METH_OPTIONS

HAProxy 版本:2.2.6

参考文档:https://cbonte.github.io/haproxy-dconv/2.2/configuration.html#http-response%20set-header

【讨论】:

以上是关于HAProxy CORS OPTIONS 标头拦截设置的主要内容,如果未能解决你的问题,请参考以下文章