]> git.ipfire.org Git - thirdparty/pdns.git/blob - docs/lua-records/index.rst
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / docs / lua-records / index.rst
1 Lua Records
2 ===========
3
4 To facilitate dynamic behaviour, such as Global Server Load Balancing,
5 PowerDNS Authoritative Server version 4.2 and later support dynamic DNS
6 records.
7
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.
11
12 Capabilities range from very simple to highly advanced multi-pool
13 geographically & weighed load balanced IP address selection.
14
15 Although users need not be aware, PowerDNS dynamic DNS records are actually
16 tiny (or larger) `Lua <https://www.lua.org>`_ statements.
17
18 .. note::
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
22 supported standard.
23
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.
26
27 In addition, to benefit from the geographical features, make sure the PowerDNS
28 launch statement includes the ``geoip`` backend.
29
30 .. warning::
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.
34
35 Examples
36 --------
37
38 Before delving into the details, some examples may be of use to explain what
39 dynamic records can do.
40
41 Here is a very basic example::
42
43 www IN LUA A "ifportup(443, {'192.0.2.1', '192.0.2.2'})"
44
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.
48
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.
51
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.
55
56 For example::
57
58 www IN LUA A "ifportup(443, {{'192.0.2.1', '192.0.2.2'}, {'192.0.3.1'}})"
59
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.
63
64 Another example::
65
66 www IN LUA A "pickclosest({'192.0.2.1','192.0.2.2','198.51.100.1'})"
67
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
70 addresses.
71
72 ``pickclosest`` and ifportup can be combined as follows::
73
74 www IN LUA A ("ifportup(443, {'192.0.2.1', '192.0.2.2', '198.51.100.1'}"
75 ", {selector='pickclosest'}) ")
76
77 This will pick from the viable IP addresses the one deemed closest to the user.
78
79 LUA records can also contain more complex code, for example::
80
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"
82
83 As you can see you can return both single string value or array of strings.
84
85 An example Lua record accessing ``qname``::
86
87 *.example.net 10 IN LUA TXT "; return 'Got a TXT query for ' .. qname:toString() .. '; First label is: ' .. qname:getRawLabels()[1]"
88
89 ``qtype`` cannot be accessed from a Lua script, the value is fixed per Lua record.
90 See :doc:`functions` for available variables.
91
92
93 Using LUA Records with Generic SQL backends
94 -------------------------------------------
95
96 It's possible to use Lua records with the Generic SQL backends such as gmysql and gpgsql.
97
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.
100
101 Here is an example from the previous section (``pickclosest``) which should work
102 for both **MySQL** and **PostgreSQL**::
103
104 -- Create the zone example.com
105 INSERT INTO domains (id, name, type) VALUES (1, 'example.com', 'NATIVE');
106
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);
110
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)
114 VALUES (
115 1,
116 'www.example.com',
117 'LUA',
118 'A "pickclosest({''192.0.2.1'',''192.0.2.2'',''198.51.100.1''})"',
119 600
120 );
121
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.
124
125 See `Details & Security`_ for more information about enabling Lua records, and the risks involved.
126
127 Record format
128 -------------
129 .. note::
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.
133
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
137 actual Lua snippet.
138
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
141 for further details.
142
143 More powerful example
144 ---------------------
145
146 A more powerful example::
147
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'}) " )
151
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
154 in Lua'.
155
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.
159
160 This configuration makes sense in the following context::
161
162 www IN LUA CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
163 "else return 'usa.powerdns.org' end" )
164
165
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
168 be::
169
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'}) " )
173
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.
177
178 Advanced topics
179 ---------------
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.
183
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::
186
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'} ")
190
191 www IN LUA CNAME ( ";if(continent('EU')) then return 'west.powerdns.org' "
192 "else return 'usa.powerdns.org' end" )
193
194 usa IN LUA A ( ";include('config') "
195 "return ifurlup('https://www.lua.org/', "
196 "{USAips, EUips}, settings) " )
197
198 west IN LUA A ( ";include('config') "
199 "return ifurlup('https://www.lua.org/', "
200 "{EUips, USAips}, settings) " )
201
202 .. _lua-details-security:
203
204 Details & Security
205 ------------------
206 LUA records are synthesized on query. They can also be transferred via AXFR
207 to other PowerDNS servers.
208
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.
211
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.
214
215 .. note::
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!
219
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
223 LUA records.
224
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).
228
229 .. _lua-records-shared-state:
230
231 Shared Lua state model
232 ----------------------
233
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.
240
241 To use this mode, set ``enable-lua-records=shared``.
242 Note that this enables LUA records for all zones.
243
244 Reference
245 ---------
246
247 .. toctree::
248 :maxdepth: 2
249
250 functions
251 reference/index