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 标头拦截设置的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CORS 预检 OPTIONS 请求中发送自定义标头?

API Gateway v2 未从 OPTIONS 调用返回 CORS 标头

AWS Api Gateway 仅在 OPTIONS 调用时不响应 CORS 标头

DART HttpRequest 不为 CORS OPTIONS 请求提供授权标头

Amazon S3 CORS 标头仅在 OPTIONS(预检)期间显示,而不在 GET 请求期间显示

在 .NET Core Web API 上为 CORS 启用 OPTIONS 标头