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-Headers
和Access-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 请求提供授权标头