Lua Examples (Authoritative) - PowerDNS/pdns GitHub Wiki

For LUA record examples, please see Lua Examples (Authoritative LUA records)

Please consult the authoritative documentation at

before deploying any of those Lua Scripts and be warned that you can break your nameserver service if not properly implemented.

Always test your Lua script before deploying it to your live authoritative servers.

Feel free to add your own Snipplets - this is a wiki! ;)

updatepolicy: Access control for RFC2136 dynamic updates

What is achieved with this script? It strictly limits RFC2136 dynamic updates to a single record based on TSIG key authentication. The idea is to use a unique TSIG key name as identifier to allow updating only the record with the same name as the key, and in a specific zone. Specific use case is to secure the Let's Encrypt DNS-01 challenge if you do not trust your individual hosts so much to hand them over full control over a zone but only want to allow them updating a specific TXT record.

Inspired by https://github.com/joohoi/acme-dns, this approach uses pdns' RFC2136 capabilities and its Lua Update Policy functionality via lua-dnsupdate-policy-script configuration parameter. Network source ranges for dynamic updates can be defined to further tighten security.

Prerequisite

Similar to acme-dns, this approach relies on _acme-challenge records (in multiple zones, even on different authoritative name servers) that are CNAME'd to unique TXT records in a single "auth zone", and those TXT records are access controlled. The code below could be adapted to work directly on _acme-challenge records instead (and skipping the CNAME indirection), but would then require modification by either implementing a mapping key name <> record, or by using the whole _acme-challenge.foo.tld FQDN as key name.

Example

  • www.website.tld shall get a Let's Encrypt certificate, using DNS-01 challenge
  • _acme-challenge.www.website.tld IN CNAME 9810a46e5cef.my-auth.tld DNS record
  • 9810a46e5cef.my-auth.tld IN TXT will hold the ACME challenge
  • ACME clients supporting CNAME detection (e.g. Lego) can then update the TXT record 9810a46e5cef.my-auth.tld with an RFC2136 update using a registered TSIG key named 9810a46e5cef

Implementation

  1. Add configuration to your pdns.conf
dnsupdate = yes
lua-dnsupdate-policy-script = /path/to/check-dynupdates.lua
  1. Register TSIG key in pdns with a unique key name equal to CNAME destination hostname-part, e.g. via pdnsutil generate-tsig-key [unique-key-name] hmac-sha256. On Linux, [unique-key-name] could be generated e.g. via cat /proc/sys/kernel/random/uuid

  2. Create DNS record _acme-challenge.www.website.tld IN CNAME [unique-key-name].my-auth.tld

  3. Deploy TSIG key to the updating host and prepare ACME client for RFC2136

  4. Put the following check-dynupdates.lua into the appropriate path that was set in pdns.conf, and modify necessary network ranges

function updatepolicy(input)

  -- target zone where the CNAMEs point to
  myzone = "my-auth.tld."

  -- allowed networks to accept updates from
  mynetworks = newNMG()
  mynetworks:addMasks({"1.2.3.0/24", "2.3.4.0/16", "127.0.0.0/8"})

  pdnslog("updatepolicy: incoming request", pdns.loglevels.Info)

  -- ignore non-authorized networks
  if not mynetworks:match(input:getRemote())
  then
    pdnslog("updatepolicy: network check failed from " .. input:getRemote():toString(), pdns.loglevels.Info)
    return false
  end

  -- ignore non-TSIG requests
  if input:getTsigName():countLabels() == 0
  then
    pdnslog("updatepolicy: missing TSIG", pdns.loglevels.Info)
    return false
  end

  -- only accept TXT record updates in myzone and for the TSIG-matching hostname
  if input:getQType() == pdns.TXT and input:getZoneName():toString() == myzone and input:getQName():toString() == input:getTsigName():toString() .. myzone
  then
    pdnslog("updatepolicy: query checks successful", pdns.loglevels.Info)
    return true
  end

  pdnslog("updatepolicy: query checks failed", pdns.loglevels.Info)
  return false
end

Restart pdns and run ACME client. Depending on your pdns loglevel, you will see only the dynamic updates or the logs from the Lua script too.