如何在 HAProxy 中验证 HMAC

How to verify HMAC in HAProxy

是否可以在 HAProxy 中检查 HMAC 有效性?理想情况下,如果 HMAC 有效,我想设置一个 acl,以便我可以在规则中使用它。

我们的 Ubuntu 18.04 构建服务器 (运行 Jenkins) 位于防火墙后面,仅将特定 IP 范围列入白名单。

我们有一个 HAProxy (1.8) 实例接收所有入站请求并路由到适当的后端服务。

问题是 SonarCloud 已经将他们的 webhooks 从一组定义的 IP 地址更改为使用 HMAC 来验证真实性。这意味着 webhook 会被防火墙阻止,除非我们对所有互联网流量开放它。

https://sonarcloud.io/documentation/project-administration/webhooks/#securing-your-webhooks

如果我们可以配置 HAProxy 来验证 HMAC,那么我们可以向所有流量开放服务器并使用 HAProxy 来验证这些请求(以及其他现有的 IP 白名单范围)。

HAProxy 本身不执行 HMAC,但可以使用 HAProxy 的 Lua 集成来完成。

一种方法是找到一个 Lua 库,它可以执行您需要的 HMAC 风格,然后在 Lua 中编写一个 HAProxy 转换器以获取适当的输入并进行计算用于比较的摘要。

我曾经使用 HAProxy 1.6 和 Lua 5.x 实现了类似的东西,其中客户端发送了使用 HMAC-SHA1 签名的 URL,并且代理成功地检查了它的有效性.

不幸的是,我无法再访问该代码,但我写了 this HAProxy converter to do utf-8-aware URL-escaping (percent encoding) in Lua...我在这里提到它是因为它是使用 [=20= 扩展 HAProxy 功能的一种方法的完整有效示例],包括使用它所需的 Lua 代码和 HAProxy 配置,因此它可能会帮助您找到解决方案。

感谢 Michael 对 HAProxy/Lua 集成的指导。我的解决方案记在这里供参考。

创建了以下 Lua 脚本 (hmac_validate.lua):

hmac = require('openssl.hmac')

local function tohex(s)
    return (string.gsub(s, ".", function (c)
        return string.format("%.2x", string.byte(c))
    end))
end -- tohex

function validate_sonar_hmac(txn, hmac_header_key, hmac_secret)
    local payload = txn.req:dup() -- take a copy of the request content
    local body = string.sub(payload,string.find(payload,"\r\n\r\n")+4) -- strip off the headers
    local signature = txn.sf:req_fhdr(hmac_header_key) -- get the HMAC signature sent on the request

    -- calculate hmac from body & secret
    local sc_hmac = hmac.new(hmac_secret, "sha256")
    local calculated_signature = tohex(sc_hmac:final(body))

    local signatures_match = calculated_signature == signature
    if not signatures_match then
        core.Alert("Sonar Cloud HMAC signature mismatch - received '"..signature.."' but calculated '"..calculated_signature.."'")
    end

    txn:set_var("req.sonar_request_valid", signatures_match)
end;

core.register_action("validate-sonar-hmac", {"http-req"}, validate_sonar_hmac, 2)

HA 代理配置已更改以添加以下行:

global
    lua-load /etc/haproxy/hmac_validate.lua

frontend
    acl sonarcloud hdr(X-Sonar-Webhook-HMAC-SHA256) -m found
    http-request lua.validate-sonar-hmac X-Sonar-Webhook-HMAC-SHA256 {{ sonarcloud_hmac_secret }} if sonarcloud
    http-request deny if sonarcloud !{ var(req.sonar_request_valid) -m bool }