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.). (I'm attaching a simple test config, that I've used for testing). -Jarno -- Jarno Huuskonen
global log /dev/log local2 info stats socket /tmp/stats level admin defaults mode http log global option httplog frontend test option http-buffer-request bind ipv4@127.0.0.1:8080 # Various Content-Type, Content-Length, X-Api-Key, url/path acl/checks omitted 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') # Debug headers http-request set-header X-json-first %[var(req.json_first)] http-request set-header X-json-second %[var(req.json_second)] http-request set-header X-json-len %[var(req.json_first),length] http-request deny deny_status 412 hdr Denial-Reason "Precondition Failed more than one rrset" if { var(req.json_second) -m found } # min length should be .domain. + 1 http-request deny deny_status 412 hdr Denial-Reason "Precondition Failed json body length" if { var(req.json_first),length lt 10 } http-request deny deny_status 412 hdr Denial-Reason "Precondition Failed json body length" if { var(req.json_first),length gt 64 } 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 acl/map not found" unless { var(req.key_and_name),map_str(pdns_apikey_acl.map) -m found } # Map X-Api-Key to apikey pdns expects #http-request set-header X-API-Key %[req.hdr(X-API-Key),map_str(pdns_apikey.map,NO-ACCESS)] default_backend BE_debug backend BE_debug server dbg 127.0.0.1:8082 id 1 frontend debug bind ipv4@127.0.0.1:8082 default_backend BE_pdns backend BE_pdns http-request deny deny_status 200 server pdns ::1:8081 id 1