The PIPE back-end has been a part of PowerDNS since what feels like forever: it speaks to a program you write via stdin and stdout. PowerDNS hands it queries which your process responds to in a particular textual format, and the name server then converts those to DNS responses which it returns to its clients. This so-called coprocess is launched by the name server, and if it should die, it is re-launched.
While it is slightly slower, the REMOTE back-end has a lot more features than the PIPE back-end. For one, it can do full DNSSEC signing, and it can talk to your actual back-end program via Unix sockets, pipes, or via a RESTful HTTP interface (neither authentication nor TLS are supported, but these can be added by hiding the interface behind an appropriate proxy). The RESTful interface supports GET or POST requests, and if we use POST it sends queries in JSON-formatted RPC requests.
In order to activate the remote back-end, I configure the following in
remote because the former should be queried first (your mileage will vary), and the
remote-connection-string defines how PowerDNS accesses its remote back-end – in this case via HTTP.
The back-end process is in Python (code here) and it implements a
/lookup endpoint which is used by PowerDNS to get the data. When we query for a
LOC record, PowerDNS actually fires off an
ANY query to our interface (
NS queries are passed in with their qtypes), as in
Had I configured
url-suffix=.xyz, for example, the HTTP queries would have had
.xyz added to them (e.g.
/lookup/LAX.airports.aa/ANY.xyz) which might be useful to return something static, or when each of your queries are to be handled by their own PHP script.
But what about DNSSEC? I’ll restart the PowerDNS server with the comments in the above configuration removed. I mentioned earlier, that the remote back-end is much more capable than the pipe back-end is: it is able to do full DNSSEC by itself including delegation and key storage, and Aki Tuomi, the author of this back-end, has a complete example in Perl called autorev which demonstrates this. (BTW, Aki is the same person who implemented the PKCS#11 interface in PowerDNS.)
Now, I am far too lazy to do all this, so I’ll use PowerDNS for the heavy lifting, letting it create and store the keys for me. In order to be able to do that, I have to add our zone to the domains table so that PowerDNS can associate the DNSSEC keys we create for the zone (in the cryptokeys database table) with this domain:
I then use the utility to actually create the keys (KSK and ZSK), and I set NSEC3 narrow mode. PowerDNS’ Narrow mode uses “additional hashing calculations to provide hashed secure denial of existence ‘on the fly’, without further involving the database”. What this basically means is that it lies its pants off but is able to convince the client that something really doesn’t exist if it doesn’t. ;-)
Now I obtain the DS for this zone:
I then copy that DS record (or the DNSKEY of the KSK if you prefer) into a file which I configure in Unbound as follows:
Querying this Unbound server shows us a validated response (
+ad flag) and the data.
Not-quite so lazy DNSSEC
It turns out it’s a bit of a mystery why this works at all, or rather it may not actually be supposed to work: our friends at PowerDNS do not actually test for the ability to have keys in one back-end and DNS data in a second. In other words, I have to overcome my laziness and attempt to do this properly.
If we configure PowerDNS to have only the one remote back-end or launch it before gmysql we have to implement more functions, in particular those which provide domain metadata and key material to the server. I’ve done this experimentally and it appears to work. When a client queries PowerDNS for an existing name, we are asked all of this:
getDomainKeys must return one or more DNS keys in BIND Private Key format 2 which are easily created with
I then simply open the
.private key file and return the ASCII blob I find there. (I did say “lazy”, didn’t I?)
With a bit more work we’d have some sort of nice utility which creates keys and drops them into a data store from which we subsequently serve them. Alternatively, we can implement
addDomainKey and use the
pdnssec secure-zone utility to generate the keys and store them therein. The following command submits a PUT request to our back-end script:
If we’re asked for a non-existent name NSEC/NSEC3 come into the spiel:
getDomainMetadata function says “No” when asked whether it’s pre-signed, responds with
1 0 5 DEADBE when asked for the NSEC3PARAM record, and it responds with
1 when asked wether to do NSEC3NARROW (for the simple reason that I cannot be bothered to implement the
So this seems to work: when I query my validating Unbound server, I see:
And if you’re wondering where all this is documented, that is a very good question. The remote back-end is well documented, but the how and why which routine is invoked by PowerDNS proper is hard to come by. I was lucky to have a longish rant session
^W^Wchat with the chaps at PowerDNS who did their best to push me in the right directions – I herewith claim all mistakes and omissions.