HAProxy CORS OPTIONS 标头拦截设置

问题描述 投票:0回答:4

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

给出的解决方案:

如何使用 HAProxy 发送响应而不将请求传递到 Web 服务器当您从客户端请求设置所有正确的标头时可以使用,但不是动态的,这不是理想的解决方案。

如有任何帮助,我们将不胜感激!

ajax cors haproxy preflight
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 预检,如果没有充分的理由,请不要限制方法或标头,因为您必须在
haproxy.conf
中的 CORS 配置中定义的 ACL 中包含有关方法或标头的任何限制。 注意:目前浏览器不支持
*
标头使用通配符
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 预检(对于
HTTP OPTIONS
请求)或带有原始值的
Access-Control-Allow-Origin
客户端请求的标头将添加到响应中。
cors-origins.lst
的内容示例可以是

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

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

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


6
投票

您可以使用Lua,但您需要通过检查

USE_LUA
来确保HAproxy是使用
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 Feldhausanswer,了解有用的正则表达式技巧。我们不使用白名单,而是回显位置标头:

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
浏览器对这些标头中通配符的支持尚未完全实现。

这就是我们的配置的作用:

  1. 使用 this
    cors.lua
    脚本
  2. 处理预检请求
  3. 使用
    http-response set-header
    添加 Access-Control-Allow-* 标头来处理正常请求
  4. 调整
    tune.maxrewrite
    以适合我们的 CORS 标头(>1 KB)

1)和2)的步骤在其他答案中都有解释,但是步骤3)花了我们很长时间才弄清楚。我已经在这篇博文中记录了完整的配置和我们到达那里的旅程。该帖子包含 github 上要点的链接。


0
投票

为了拦截像我这样的选项,我想在响应中添加

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

© www.soinside.com 2019 - 2024. All rights reserved.