4 To facilitate dynamic behaviour, such as Global Server Load Balancing,
5 PowerDNS Authoritative Server version 4.2 and later support dynamic DNS
8 These records contain small snippets of configuration that enable dynamic
9 behaviour based on requester IP address, requester's EDNS Client Subnet,
10 server availability or other factors.
12 Capabilities range from very simple to highly advanced multi-pool
13 geographically & weighed load balanced IP address selection.
15 Although users need not be aware, PowerDNS dynamic DNS records are actually
16 tiny (or larger) `Lua <https://www.lua.org>`_ statements.
19 This is a PowerDNS specific feature, and is not (yet) standardized by the
20 IETF or other standards bodies. We are committed however to
21 interoperability, and strive to turn this functionality into a broadly
24 To enable this feature, either set 'enable-lua-records' in the configuration,
25 or set the 'ENABLE-LUA-RECORDS' per-zone metadata item to 1.
27 In addition, to benefit from the geographical features, make sure the PowerDNS
28 launch statement includes the ``geoip`` backend.
31 When using AXFR to transfer long LUA records, ensure that the record content is split into segments no longer than 255 bytes.
32 Otherwise, due to the nature of the transfer method (as TXT content), the content will be split into 255 byte chunks.
33 This can lead to the code being split in the wrong places on the secondary.
38 Before delving into the details, some examples may be of use to explain what
39 dynamic records can do.
41 Here is a very basic example::
43 www IN LUA A "ifportup(443, {'192.0.2.1', '192.0.2.2'})"
45 This turns the 'www' name within a zone into a special record that will
46 randomly return 192.0.2.1 or 192.0.2.2, as long as both of these IP
47 addresses listen on port 443.
49 If either IP address stops listening, only the other address will be
50 returned. If all IP addresses are down, all candidates are returned.
52 You can also provide multiple sets of IP addresses to prioritize a set over the
53 rest. If an IP address from the first set is available, it will be returned. If
54 no addresses work in the first set, the second set is tried.
58 www IN LUA A "ifportup(443, {{'192.0.2.1', '192.0.2.2'}, {'192.0.3.1'}})"
60 Because DNS queries require rapid answers, server availability is not checked
61 synchronously. In the background, a process periodically determines if IP
62 addresses mentioned in availability rules are, in fact, available.
66 www IN LUA A "pickclosest({'192.0.2.1','192.0.2.2','198.51.100.1'})"
68 This uses the GeoIP backend to find indications of the geographical location of
69 the requester and the listed IP addresses. It will return with one of the closest
72 ``pickclosest`` and ifportup can be combined as follows::
74 www IN LUA A ("ifportup(443, {'192.0.2.1', '192.0.2.2', '198.51.100.1'}"
75 ", {selector='pickclosest'}) ")
77 This will pick from the viable IP addresses the one deemed closest to the user.
79 LUA records can also contain more complex code, for example::
81 www IN LUA A ";if country('US') then return {'192.0.2.1','192.0.2.2','198.51.100.1'} else return '192.0.2.2' end"
83 As you can see you can return both single string value or array of strings.
85 An example Lua record accessing ``qname``::
87 *.example.net 10 IN LUA TXT "; return 'Got a TXT query for ' .. qname:toString() .. '; First label is: ' .. qname:getRawLabels()[1]"
89 ``qtype`` cannot be accessed from a Lua script, the value is fixed per Lua record.
90 See :doc:`functions` for available variables.
93 Using LUA Records with Generic SQL backends
94 -------------------------------------------
96 It's possible to use Lua records with the Generic SQL backends such as gmysql and gpgsql.
98 Be aware that due to the fact that Lua records uses both double and single quotes, you will
99 need to appropriately escape them in INSERT/UPDATE queries.
101 Here is an example from the previous section (``pickclosest``) which should work
102 for both **MySQL** and **PostgreSQL**::
104 -- Create the zone example.com
105 INSERT INTO domains (id, name, type) VALUES (1, 'example.com', 'NATIVE');
107 -- Enable Lua records for the zone (if not enabled globally)
108 INSERT INTO domainmetadata (domain_id, kind, content)
109 VALUES (1, 'ENABLE-LUA-RECORDS', 1);
111 -- Create a pickClosest() Lua A record.
112 -- Double single quotes are used to escape single quotes in both MySQL and PostgreSQL
113 INSERT INTO records (domain_id, name, type, content, ttl)
118 'A "pickclosest({''192.0.2.1'',''192.0.2.2'',''198.51.100.1''})"',
122 The above queries create a zone ``example.com``, enable Lua records for the zone using ``ENABLE-LUA-RECORDS``,
123 and finally insert a LUA A record for the ``www`` subdomain using the previous pickclosest example.
125 See `Details & Security`_ for more information about enabling Lua records, and the risks involved.
130 The fine authors of the Lua programming language insist that it is Lua and
131 not LUA. Lua means 'moon' in Portuguese, and it is not an abbreviation.
132 Sadly, it is DNS convention for record types to be all uppercase. Sorry.
134 The LUA record consists of an initial query type, which is the selector on
135 which the snippet will trigger. Optionally this query type itself can be LUA
136 again for configuration scripts. The query type is then followed by the
139 LUA records can have TTL settings, and these will be honoured. In addition,
140 LUA records output can be DNSSEC signed like any other record, but see below
143 More powerful example
144 ---------------------
146 A more powerful example::
148 west IN LUA A ( "ifurlup('https://www.lua.org/', "
149 "{{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}}, "
150 "{stringmatch='Programming in Lua'}) " )
152 In this case, IP addresses are tested to see if they will serve
153 https for 'www.lua.org', and if that page contains the string 'Programming
156 Two sets of IP addresses are supplied. If an IP address from the first set
157 is available, it will be returned. If no addresses work in the first set,
158 the second set is tried.
160 This configuration makes sense in the following context::
162 www IN LUA CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
163 "else return 'usa.powerdns.org' end" )
166 This sends queries that are geolocated to Europe to 'west.powerdns.org', and
167 the rest to 'usa.powerdns.org'. The configuration for that name would then
170 usa IN LUA A ( "ifurlup('https://www.lua.org/', "
171 "{{'198.51.100.1'}, {'192.0.2.1', '192.0.2.2'}}, "
172 "{stringmatch='Programming in Lua'}) " )
174 Note that the sets of IP addresses have reversed order - visitors geolocated
175 outside of Europe will hit 198.51.100.1 as long as it is available, and the
176 192.0.2.1 and 192.0.2.2 servers as backup.
180 By default, LUA records are executed with 'return ' prefixed to them. This saves
181 a lot of typing for common cases. To run actual Lua scripts, start a record with a ';'
182 which indicates no 'return ' should be prepended.
184 To keep records more concise and readable, configuration can be stored in
185 separate records. The full example from above can also be written as::
187 config IN LUA LUA ("settings={stringmatch='Programming in Lua'} "
188 "EUips={'192.0.2.1', '192.0.2.2'} "
189 "USAips={'198.51.100.1'} ")
191 www IN LUA CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
192 "else return 'usa.powerdns.org' end" )
194 usa IN LUA A ( ";include('config') "
195 "return ifurlup('https://www.lua.org/', "
196 "{USAips, EUips}, settings) " )
198 west IN LUA A ( ";include('config') "
199 "return ifurlup('https://www.lua.org/', "
200 "{EUips, USAips}, settings) " )
202 .. _lua-details-security:
206 LUA records are synthesized on query. They can also be transferred via AXFR
207 to other PowerDNS servers.
209 LUA records themselves cannot be queried however, as this would allow third parties to see load balancing internals
210 they do not need to see.
212 A non-supporting DNS server will also serve a zone with LUA records, but
213 they will not function, and will in fact leak the content of the LUA records.
216 Under NO circumstances serve LUA records from zones from untrusted sources!
217 LUA records will be able to bring down your system and possible take over
218 control of it. Use TSIG on AXFR even from trusted sources!
220 LUA records can be DNSSEC signed, but because they are dynamic, it is not
221 possible to combine pre-signed DNSSEC zone and LUA records. In other words,
222 the signing key must be available on the server creating answers based on
225 Note that to protect operators, support for LUA records must be enabled
226 explicitly, either globally (``enable-lua-records``) or per zone
227 (``ENABLE-LUA-RECORDS`` = 1).
229 .. _lua-records-shared-state:
231 Shared Lua state model
232 ----------------------
234 The default mode of operation for LUA records is to create a fresh Lua state for every query that hits a LUA record.
235 This way, different LUA records cannot accidentally interfere with each other, by leaving around global objects, or perhaps even deleting relevant functions.
236 However, creating a Lua state (and registering all our functions for it, see Reference below) takes measurable time.
237 For users that are confident they can write Lua scripts that will not interfere with eachother, a mode is supported where Lua states are created on the first query, and then reused forever.
238 Note that the state is per-thread (for UDP, plus one shared state for all TCP), so while data sharing between LUA invocations is possible (useful for caching and reducing the cost of ``require``), there is no single shared Lua environment.
239 In non-scientific testing this has yielded up to 10x QPS increases.
241 To use this mode, set ``enable-lua-records=shared``.
242 Note that this enables LUA records for all zones.