HAProxy ACL With Variable In Substring

Hi,

You cannot specify multiple -m on the same acl line: it gets rewritten each time you use it, thus only the last one will be considered.

For instance:

acl allowed_origins_in_payload var(txn.token_payload) -m found -m sub allowed-origins

Will be simply be evaluated as:

acl allowed_origins_in_payload var(txn.token_payload) -m sub allowed-origins

Moreover, -m sub will interpret the given patterns as raw strings, ie: it will not evaluate fetches, so if you use this:

acl src_found_in_allowed_origins var(txn.token_payload) -m sub src 

It will try to look for “src” keyword in txn.token_payload, not for the actual IP address that src sample fetch points to.

Unfortunately I can’t think of a trivial way to do this from pure config. Perhaps strcmp(), jwt_query() and json_query() converters could give you a hand, but it looks quite complicated to do this from pure config IMO.

If you’re not afraid about performance implications, another option could be to leverage Lua to implement your own fetch. Since Lua fetches have access to the TXN context, it should be pretty straightforward to extract allowed IP origins from the txn.token_payload variable and compare then with client’s IP address so that the fetch returns 1 on success and 0 on failure for instance.

match.lua:

function extract_allowed_ips_from_jwt(arg)
  local ips = { } -- array of strings ip
   -- your code to populate ips
  return ips
end

core.register_fetches("check_allowed", function(txn)
    -- Get source IP
    local clientip = txn.f:src()
    -- Get allowed ips
    local allowedips = extract_allowed_ips_from_jwt(txn:get_var("txn.token_payload"))

  for key,value in pairs(allowedips) do
      if string.match(value, clientip) then
          return true
      end
  end
  return false
end)

haproxy.conf:

global
  lua-load match.lua

frontend test-443
  # Binding
  bind *:443

  acl src_found_in_allowed_origins lua.check_allowed -m bool eq true
  http-request deny content-type 'text/html' string 'Host was not found in allowed_origins' if !src_found_in_allowed_origins