Hi Jarno.

On 2025-05-29 (Do.) 15:32, Jarno Huuskonen wrote:
Hi,

I'm experimenting using haproxy in front of powerdns(pdns)
api(https://doc.powerdns.com/authoritative/http-api/index.html#enabling-the-api)
to use different apikeys and limit which records each apikey is allowed
to modify. (pdns api only allows one apikey that has access to modify
everything).

I'm trying to use json_query to get each $.rrset[*].name from pdns json
(request is something like:
curl -X PATCH -H "content-type: application/json" -H "X-API-Key:
mykey1" -d '{ "rrsets": [{ "name": "_acme-challenge.xxx.example.org.",
"type": "TXT", "ttl": 300, "changetype": "REPLACE", "records": [{
"content": "\"xxxx\"", "disabled": false }]}, { "name": "NOT-
allowed.example.org.", "type": "AAAA", "ttl": 300, "changetype":
"REPLACE", "records": [{ "content": "::1", "disabled": false }]}, {
"name": "nogo.sub.example.org.", "type": "TXT", "ttl": 300,
"changetype": "REPLACE", "records": [{ "content": "\"notallowed\"",
"disabled": false }]} ] }' ... url

I've tried these json_queries (tested with haproxy-3.2.0 and 3.0.x
(probably not version dependent)):
- http-request set-var(req.json_first)
req.body,json_query('$.rrsets[*].name')  # haproxy returns first rrset
name (jsonpath playgrounds return array with all names)
- http-request set-var(req.json_first) req.body,json_query('$..name') #
haproxy doesn't return anything (playground return array with all
names)
- http-request set-var(req.json_first)
req.body,json_query('$.rrsets[0].name') # returns first
- http-request set-var(req.json_first) req.body,json_query('$.rrsets[-
1].name') # returns nothing (playground returns last).

Does json_query support returning multiple rrset.name(s)
($.rrsets[*].name) or negative indexes ($.rrsets[-1].name) or ranges
$.rrsets[0:].name ?

For now I can workaround json_query not returning all/multiple
rrset.name(s) with something like this (deny if multiple rrset):
http-request set-var(req.json_first)
req.body,json_query('$.rrsets[0].name')
http-request set-var(req.json_second)
req.body,json_query('$.rrsets[1].name')
http-request deny if { var(req.json_second) -m found }

Idea is to concat X-Api-Key and rrset.name and use it for map lookup to
allow access: something like this:
     http-request set-var(req.json_first)
req.body,json_query('$.rrsets[0].name')
     http-request set-var(req.json_second)
req.body,json_query('$.rrsets[1].name')
    http-request deny deny_status 412 hdr Denial-Reason "Precondition
Failed more than one rrset" if { var(req.json_second) -m found }
     http-request set-var(req.key_and_name) req.hdr(X-API-
Key),concat(|,req.json_first)
     http-request set-header X-key-and-name %[var(req.key_and_name)] #
Debug
     http-request deny deny_status 412 hdr Denial-Reason "Precondition
Failed map/acl not found" unless {
var(req.key_and_name),map_str(pdns_apikey_acl.map) -m found }

Is lua or some other method better suited for this ? (Performance is
not important, I'm expecting very few actual requests.).

Well the current config give the array back as you can see in the code.
https://git.haproxy.org/?p=haproxy.git;a=blob;f=src/sample.c;h=980c27cb6a5073ba4843de47267dfa960add8881;hb=HEAD#l4428

result looks like: ["manage-account","manage-account-links","view-profile"]

I don't know if the json_query is exported to LUA, I assume not.

I think there could be a further parameter to json_query for example "index" which returns only the array element from the "index" parameter.

Maybe LUA JSON packages are easier to use.

(I'm attaching a simple test config, that I've used for testing).

-Jarno

Regards
Aleks


Reply via email to