2
votes

Is it possible to check HMAC validity in HAProxy? Ideally I'd like to set an acl if the HMAC is valid so I can use this in rules.

Our Ubuntu 18.04 build server (running Jenkins) sits behind a firewall with only specific IP ranges white-listed.

We have an HAProxy (1.8) instance receiving all inbound requests and routing to the appropriate backend service.

The problem is SonarCloud have changed their webhooks from a defined set of IP addresses to using HMAC to validate authenticity. This means the webhooks are blocked by the firewall unless we open it to all internet traffic.

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

If we can congifure HAProxy to validate the HMAC then we can open the server to all traffic & use HAProxy to validate these requests (as well as other existing IP whitelisted ranges).

2

2 Answers

2
votes

Thanks to Michael for the pointer to HAProxy/Lua integration. My solution noted here for reference.

Created the following Lua script (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 Proxy config changed to add these lines:

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 }
1
votes

HAProxy doesn't do HMAC natively, but it can be done using HAProxy's Lua integration.

One approach would be to find a Lua library that can do the flavor of HMAC that you need, and then write an HAProxy converter in Lua to take the appropriate inputs and do the calculation of the digest for comparison.

I implemented something similar, once, using HAProxy 1.6 with Lua 5.x where the client sent a URL signed using HMAC-SHA1, and the proxy successfully checked it for validity.

Unfortunately, I no longer have access to that code, but I wrote this HAProxy converter to do utf-8-aware URL-escaping (percent encoding) in Lua... I mention it here because it's a complete, working example of one way to extend HAProxy functionality using Lua, including the Lua code and HAProxy configuration needed to use it, so it might help you work toward a solution.