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 [email protected]: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 [email protected]:8082
default_backend BE_pdns
backend BE_pdns
http-request deny deny_status 200
server pdns ::1:8081 id 1