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

Reply via email to