]>
Commit | Line | Data |
---|---|---|
773470ca | 1 | dnsdist |
2 | ------- | |
773470ca | 3 | `dnsdist` is a highly DNS-, DoS- and abuse-aware loadbalancer. Its goal in |
4 | life is to route traffic to the best server, delivering top performance | |
5 | to legitimate users while shunting or blocking abusive traffic. | |
6 | ||
7 | `dnsdist` is dynamic, in the sense that its configuration can be changed at | |
8 | runtime, and that its statistics can be queried from a console-like | |
9 | interface. | |
10 | ||
539921be | 11 | Compiling |
12 | --------- | |
13fe35db | 13 | `dnsdist` depends on boost, Lua or LuaJIT and a pretty recent C++ |
ee52e6fa | 14 | compiler (g++ 4.8 or higher, clang 3.5 or higher). It can optionally use libsodium |
537524f8 | 15 | for encrypted communications with its client. |
539921be | 16 | |
6ab65223 PL |
17 | Should `dnsdist` be run on a system with systemd, it is highly recommended to have |
18 | the systemd header files (`libsystemd-dev` on debian and `systemd-devel` on CentOS) | |
19 | installed to have `dnsdist` support systemd-notify. | |
20 | ||
1a5f0162 | 21 | To compile on CentOS 6 / RHEL6, use this script to install a working compiler: |
260dd1c4 | 22 | |
23 | ``` | |
24 | wget -O /etc/yum.repos.d/slc6-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo | |
25 | yum install devtoolset-2 | |
26 | scl enable devtoolset-2 bash | |
27 | ./configure | |
28 | make | |
29 | ``` | |
30 | ||
f2c0afef PD |
31 | To build on OS X, `./configure LIBEDIT_LIBS='-L/usr/lib -ledit' LIBEDIT_CFLAGS=-I/usr/include/editline` |
32 | ||
1a5f0162 | 33 | On other recent platforms, installing a Lua and the system C++ compiler should be enough. |
34 | ||
6715a732 PL |
35 | `dnsdist` can drop privileges using the `--uid` and `--gid` commandline-switches |
36 | to ensure it does not run with root privileges after binding its listen-sockets. | |
37 | It is highly recommended to create a system user and group for `dnsdist`. Note that | |
38 | most packaged versions of `dnsdist` already create this user. | |
39 | ||
0440c642 | 40 | Packaged |
41 | -------- | |
ee52e6fa RG |
42 | We build packages for `dnsdist` on our [repositories](https://repo.powerdns.com). In addition |
43 | `dnsdist` has been packaged for FreeBSD and can be found on https://freshports.org/dns/dnsdist | |
0440c642 | 44 | |
539921be | 45 | Examples |
46 | -------- | |
e9ff40cd | 47 | |
537524f8 | 48 | The absolute minimum configuration: |
49 | ||
1a5f0162 | 50 | ``` |
537524f8 | 51 | # dnsdist 2001:4860:4860::8888 8.8.8.8 |
2332b03c | 52 | ``` |
537524f8 | 53 | |
54 | This will listen on 0.0.0.0:53 and forward queries to the two listed IP | |
55 | addresses, with a sensible load balancing policy. | |
56 | ||
57 | Here is a more complete configuration: | |
773470ca | 58 | |
59 | ``` | |
1d637f62 | 60 | $ cat /etc/dnsdist.conf |
bf9edc24 RG |
61 | newServer({address="2001:4860:4860::8888", qps=1}) |
62 | newServer({address="2001:4860:4860::8844", qps=1}) | |
63 | newServer({address="2620:0:ccc::2", qps=10}) | |
64 | newServer({address="2620:0:ccd::2", name="dns1", qps=10}) | |
773470ca | 65 | newServer("192.168.1.2") |
c9262563 | 66 | setServerPolicy(firstAvailable) -- first server within its QPS limit |
773470ca | 67 | |
021e3aba | 68 | $ dnsdist --local=0.0.0.0:5200 |
773470ca | 69 | Marking downstream [2001:4860:4860::8888]:53 as 'up' |
70 | Marking downstream [2001:4860:4860::8844]:53 as 'up' | |
71 | Marking downstream [2620:0:ccc::2]:53 as 'up' | |
72 | Marking downstream [2620:0:ccd::2]:53 as 'up' | |
73 | Marking downstream 192.168.1.2:53 as 'up' | |
74 | Listening on 0.0.0.0:5200 | |
c9262563 | 75 | > |
773470ca | 76 | ``` |
77 | ||
78 | We can now send queries to port 5200, and get answers: | |
79 | ||
80 | ``` | |
81 | $ dig -t aaaa powerdns.com @127.0.0.1 -p 5200 +short | |
82 | 2001:888:2000:1d::2 | |
83 | ``` | |
84 | ||
ee52e6fa | 85 | Note that `dnsdist` offered us a prompt above, and on it we can get some |
773470ca | 86 | statistics: |
87 | ||
88 | ``` | |
cd29dcb1 | 89 | > showServers() |
91c52b48 OS |
90 | # Address State Qps Qlim Ord Wt Queries Drops Drate Lat Pools |
91 | 0 [2001:4860:4860::8888]:53 up 0.0 1 1 1 1 0 0.0 0.0 | |
92 | 1 [2001:4860:4860::8844]:53 up 0.0 1 1 1 0 0 0.0 0.0 | |
93 | 2 [2620:0:ccc::2]:53 up 0.0 10 1 1 0 0 0.0 0.0 | |
94 | 3 [2620:0:ccd::2]:53 up 0.0 10 1 1 0 0 0.0 0.0 | |
95 | 4 192.168.1.2:53 up 0.0 0 1 1 0 0 0.0 0.0 | |
96 | All 0.0 1 0 | |
773470ca | 97 | ``` |
98 | ||
a6c02c23 | 99 | Here we also see our configuration. 5 downstream servers have been configured, of |
773470ca | 100 | which the first 4 have a QPS limit (of 1, 1, 10 and 10 queries per second, |
a6c02c23 | 101 | respectively). The final server has no limit, which we can easily test: |
773470ca | 102 | |
103 | ``` | |
104 | $ for a in {0..1000}; do dig powerdns.com @127.0.0.1 -p 5200 +noall > /dev/null; done | |
91c52b48 OS |
105 | > showServers() |
106 | # Address State Qps Qlim Ord Wt Queries Drops Drate Lat Pools | |
107 | 0 [2001:4860:4860::8888]:53 up 1.0 1 1 1 7 0 0.0 1.6 | |
108 | 1 [2001:4860:4860::8844]:53 up 1.0 1 1 1 6 0 0.0 0.6 | |
109 | 2 [2620:0:ccc::2]:53 up 10.3 10 1 1 64 0 0.0 2.4 | |
110 | 3 [2620:0:ccd::2]:53 up 10.3 10 1 1 63 0 0.0 2.4 | |
111 | 4 192.168.1.2:53 up 125.8 0 1 1 671 0 0.0 0.4 | |
112 | All 145.0 811 0 | |
773470ca | 113 | ``` |
114 | ||
115 | Note that the first 4 servers were all limited to near their configured QPS, | |
116 | and that our final server was taking up most of the traffic. No queries were | |
117 | dropped, and all servers remain up. | |
118 | ||
119 | To force a server down, try: | |
120 | ||
121 | ``` | |
122 | > getServer(0):setDown() | |
91c52b48 OS |
123 | > showServers() |
124 | # Address State Qps Qlim Ord Wt Queries Drops Drate Lat Pools | |
125 | 0 [2001:4860:4860::8888]:53 DOWN 0.0 1 1 1 8 0 0.0 0.0 | |
773470ca | 126 | ... |
127 | ``` | |
128 | ||
129 | The 'DOWN' in all caps means it was forced down. A lower case 'down' | |
ee52e6fa | 130 | would've meant that `dnsdist` itself had concluded the server was down. |
773470ca | 131 | Similarly, setUp() forces a server to be up, and setAuto() returns it to the |
132 | default availability-probing. | |
133 | ||
134 | To change the QPS for a server: | |
135 | ``` | |
136 | > getServer(0):setQPS(1000) | |
137 | ``` | |
138 | ||
66407d9e RG |
139 | By default, the availability of a downstream server is checked by regularly |
140 | sending an A query for "a.root-servers.net.". A different query type and target | |
9e87dcb8 | 141 | can be specified by passing, respectively, the `checkType` and `checkName` |
66407d9e | 142 | parameters to `newServer`. The default behavior is to consider any valid response |
9e87dcb8 | 143 | with a RCODE different from ServFail as valid. If the `mustResolve` parameter |
66407d9e RG |
144 | of `newServer` is set to true, a response will only be considered valid if |
145 | its RCODE differs from NXDomain, ServFail and Refused. | |
9e87dcb8 RG |
146 | The number of health check failures before a server is considered down is |
147 | configurable via the`maxCheckFailures` parameter, defaulting to 1. | |
66407d9e RG |
148 | |
149 | ``` | |
bf9edc24 | 150 | newServer({address="192.0.2.1", checkType="AAAA", checkName="a.root-servers.net.", mustResolve=true}) |
66407d9e RG |
151 | ``` |
152 | ||
ca404e94 | 153 | In order to provide the downstream server with the address of the real client, |
a1400993 | 154 | or at least the one talking to `dnsdist`, the `useClientSubnet` parameter can be used |
ca404e94 RG |
155 | when declaring a new server. This parameter indicates whether an EDNS Client Subnet option |
156 | should be added to the request. If the incoming request already contains an EDNS Client Subnet value, | |
a1400993 RG |
157 | it will not be overriden unless `setECSOverride()` is set to true. |
158 | The default source prefix-length is 24 for IPv4 and 56 for IPv6, meaning that for a query | |
159 | received from 192.0.2.42, the EDNS Client Subnet value sent to the backend will | |
160 | be 192.0.2.0. This can be changed with: | |
ca404e94 RG |
161 | ``` |
162 | > setECSSourcePrefixV4(24) | |
163 | > setECSSourcePrefixV6(56) | |
164 | ``` | |
165 | ||
3f6d07a4 RG |
166 | TCP timeouts |
167 | ------------ | |
168 | ||
169 | By default, a 2 seconds timeout is enforced on the TCP connection from the client, | |
170 | meaning that a connection will be closed if the query can't be read in less than 2s | |
171 | or if the answer can't be sent in less than 2s. This can be configured with: | |
172 | ``` | |
173 | > setTCPRecvTimeout(5) | |
174 | > setTCPSendTimeout(5) | |
175 | ``` | |
176 | ||
177 | The same kind of timeouts is enforced on the TCP connections to the downstream servers. | |
178 | The default value of 30s can be modified by passing the `tcpRecvTimeout` and `tcpSendTimeout` | |
9e87dcb8 RG |
179 | parameters to `newServer`. If the TCP connection to a downstream server fails, `dnsdist` |
180 | will try to establish a new one up to `retries` times before giving up. | |
3f6d07a4 | 181 | ``` |
9e87dcb8 | 182 | newServer({address="192.0.2.1", tcpRecvTimeout=10, tcpSendTimeout=10, retries=5}) |
3f6d07a4 RG |
183 | ``` |
184 | ||
fbe2a2e0 RG |
185 | Source address |
186 | -------------- | |
187 | ||
188 | In multi-homed setups, it can be useful to be able to select the source address or the outgoing | |
189 | interface used by `dnsdist` to contact a downstream server. | |
190 | This can be done by using the `source` parameter: | |
191 | ``` | |
192 | newServer({address="192.0.2.1", source="192.0.2.127"}) | |
193 | newServer({address="192.0.2.1", source="eth1"}) | |
194 | newServer({address="192.0.2.1", source="192.0.2.127@eth1"}) | |
195 | ``` | |
196 | ||
197 | The supported values for `source` are: | |
a1400993 | 198 | |
fbe2a2e0 RG |
199 | * an IPv4 or IPv6 address, which must exist on the system |
200 | * an interface name | |
201 | * an IPv4 or IPv6 address followed by '@' then an interface name | |
202 | ||
203 | Specifying the interface name is only supported on system having IP_PKTINFO. | |
204 | ||
205 | ||
6149a8bf | 206 | Configuration management |
207 | ------------------------ | |
208 | At startup, configuration is read from the command line and the | |
209 | configuration file. The config can also be inspected and changed from the | |
210 | console. Sadly, our architecture does not allow us to serialize the running | |
211 | configuration for you. However, we do try to offer the next best thing: | |
212 | `delta()`. | |
213 | ||
214 | `delta()` shows all commands entered that changed the configuration. So | |
215 | adding a new downstream server with `newServer()` would show up, but | |
216 | `showServers()` or even `delta()` itself would not. | |
217 | ||
218 | It is suggested to study the output of `delta()` carefully before appending | |
219 | it to your configuration file. | |
220 | ||
bf9edc24 RG |
221 | ``` |
222 | > setACL("192.0.2.0/24") | |
223 | > showACL() | |
224 | 192.0.2.0/24 | |
225 | > delta() | |
2e439533 | 226 | -- Wed Dec 23 2015 15:15:35 CET |
bf9edc24 RG |
227 | setACL("192.0.2.0/24") |
228 | > addACL("127.0.0.1/8") | |
229 | > showACL() | |
230 | 192.0.2.0/24 | |
231 | 127.0.0.1/8 | |
232 | > delta() | |
2e439533 | 233 | -- Wed Dec 23 2015 15:15:35 CET |
bf9edc24 | 234 | setACL("192.0.2.0/24") |
2e439533 | 235 | -- Wed Dec 23 2015 15:15:44 CET |
bf9edc24 RG |
236 | addACL("127.0.0.1/8") |
237 | > | |
238 | ``` | |
239 | ||
240 | ||
b5735af6 | 241 | Webserver |
242 | --------- | |
ee52e6fa | 243 | To visually interact with `dnsdist`, try adding: |
b5735af6 | 244 | ``` |
5ef0af18 | 245 | webserver("127.0.0.1:8083", "supersecretpassword", "supersecretAPIkey") |
b5735af6 | 246 | ``` |
247 | ||
248 | to the configuration, and point your browser at http://127.0.0.1:8083 and | |
249 | log in with any username, and that password. Enjoy! | |
250 | ||
002decab RG |
251 | By default, our web server sends some security-related headers: |
252 | ||
253 | * X-Content-Type-Options: nosniff | |
254 | * X-Frame-Options: deny | |
255 | * X-Permitted-Cross-Domain-Policies: none | |
256 | * X-XSS-Protection: 1; mode=block | |
257 | * Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline' | |
258 | ||
259 | You can override those headers, or add custom headers by using the last parameter to | |
260 | `webserver()`. For example, to remove the `X-Frame-Options` header and add a | |
261 | `X-Custom` one: | |
262 | ``` | |
263 | webserver("127.0.0.1:8080", "supersecret", "apikey", {["X-Frame-Options"]= "", ["X-Custom"]="custom"}) | |
264 | ``` | |
265 | ||
266 | ||
d95585af | 267 | Server pools |
268 | ------------ | |
773470ca | 269 | Now for some cool stuff. Let's say we know we're getting a whole bunch of |
270 | traffic for a domain used in DoS attacks, for example 'sh43354.cn'. We can | |
271 | do two things with this kind of traffic. Either we block it outright, like | |
272 | this: | |
273 | ||
274 | ``` | |
275 | > addDomainBlock("sh43354.cn.") | |
276 | ``` | |
277 | ||
22b2b326 | 278 | Or we configure a server pool dedicated to receiving the nasty stuff: |
773470ca | 279 | |
280 | ``` | |
bf9edc24 | 281 | > newServer({address="192.168.1.3", pool="abuse"}) |
22b2b326 | 282 | > addPoolRule({"sh43353.cn.", "ezdns.it."}, "abuse") |
773470ca | 283 | ``` |
284 | ||
285 | The wonderful thing about this last solution is that it can also be used for | |
286 | things where a domain might possibly be legit, but it is still causing load | |
287 | on the system and slowing down the internet for everyone. With such an abuse | |
288 | server, 'bad traffic' still gets a chance of an answer, but without | |
289 | impacting the rest of the world (too much). | |
290 | ||
291 | We can similarly add clients to the abuse server: | |
292 | ||
293 | ``` | |
22b2b326 | 294 | > addPoolRule({"192.168.12.0/24", "192.168.13.14"}, "abuse") |
295 | ``` | |
296 | ||
20357a9a | 297 | To define a pool that should receive only a QPS-limited amount of traffic, do: |
fd010ca3 | 298 | |
299 | ``` | |
300 | > addQPSPoolRule("com.", 10000, "gtld-cluster") | |
301 | ``` | |
302 | ||
20357a9a RG |
303 | Traffic exceeding the QPS limit will not match that rule, and subsequent |
304 | rules will apply normally. | |
fd010ca3 | 305 | |
0940e4eb | 306 | Both `addDomainBlock` and `addPoolRule` end up the list of Rules |
307 | and Actions (for which see below). | |
308 | ||
309 | Servers can be added or removed to pools with: | |
22b2b326 | 310 | ``` |
311 | > getServer(7):addPool("abuse") | |
537524f8 | 312 | > getServer(4):rmPool("abuse") |
773470ca | 313 | ``` |
314 | ||
0940e4eb | 315 | |
316 | Rules | |
317 | ----- | |
318 | Rules can be inspected with `showRules()`, and can be deleted with | |
319 | `rmRule()`. Rules are evaluated in order, and this order can be changed | |
320 | with `mvRule(from, to)` (see below for exact semantics). | |
321 | ||
322 | Rules have selectors and actions. Current selectors are: | |
caec2956 | 323 | |
0940e4eb | 324 | * Source address |
325 | * Query type | |
326 | * Query domain | |
2332b03c | 327 | * QPS Limit total |
328 | * QPS Limit per IP address or subnet | |
df3e393a | 329 | * QClass (QClassRule) |
b7860997 | 330 | * QType (QTypeRule) |
6eecd4c2 | 331 | * RegexRule on query name |
0bdb1955 | 332 | * RE2Rule on query name (optional) |
6eecd4c2 | 333 | * Packet requests DNSSEC processing |
490a29bb | 334 | * Query received over UDP or TCP |
55baa1f2 RG |
335 | * Opcode (OpcodeRule) |
336 | * Number of entries in a given section (RecordsCountRule) | |
337 | * Number of entries of a specific type in a given section (RecordsTypeCountRule) | |
338 | * Presence of trailing data (TrailingDataRule) | |
0940e4eb | 339 | |
e7a1029c | 340 | Special rules are: |
caec2956 | 341 | |
e7a1029c RG |
342 | * `AndRule{rule1, rule2}`, which only matches if all of its subrules match |
343 | * `OrRule{rule1, rule2}`, which matches if at least one of its subrules match | |
344 | * `NotRule(rule)`, which matches if its subrule does not match | |
b7860997 | 345 | |
0940e4eb | 346 | Current actions are: |
caec2956 | 347 | |
ecc8a33b | 348 | * Drop (DropAction) |
349 | * Route to a pool (PoolAction) | |
0940e4eb | 350 | * Return with TC=1 (truncated, ie, instruction to retry with TCP) |
351 | * Force a ServFail, NotImp or Refused answer | |
352 | * Send out a crafted response (NXDOMAIN or "real" data) | |
1a2a4e68 RG |
353 | * Delay a response by n milliseconds (DelayAction), over UDP only |
354 | * Modify query to clear the RD or CD bit | |
355 | * Add the source MAC address to the query (MacAddrAction) | |
886e2cf2 | 356 | * Skip the cache, if any |
27dfcc14 RG |
357 | * Log query content to a remote server (RemoteLogAction) |
358 | ||
359 | Current response actions are: | |
360 | ||
361 | * Log response content to a remote server (RemoteLogResponseAction) | |
0940e4eb | 362 | |
ee52e6fa | 363 | Rules can be added via: |
caec2956 | 364 | |
ecc8a33b | 365 | * addAction(DNS rule, DNS Action) |
ee52e6fa RG |
366 | * addAnyTCRule() |
367 | * addDelay(DNS rule, delay in milliseconds) | |
368 | * addDisableValidationRule(DNS rule) | |
369 | * addDomainBlock(domain) | |
7b9d167c | 370 | * addDomainSpoof(domain, IPv4[, IPv6]) or addDomainSpoof(domain, {IP, IP, IP..}) |
87c605c4 | 371 | * addDomainCNAMESpoof(domain, CNAME) |
ee52e6fa RG |
372 | * addLuaAction(DNS rule, lua function) |
373 | * addNoRecurseRule(DNS rule) | |
374 | * addPoolRule(DNS rule, destination pool) | |
375 | * addQPSLimit(DNS rule, qps limit) | |
376 | * addQPSPoolRule(DNS rule, qps limit, destination pool) | |
377 | ||
27dfcc14 RG |
378 | Response rules can be added via: |
379 | ||
380 | * addResponseAction(DNS rule, DNS Response Action) | |
381 | ||
ee52e6fa | 382 | A DNS rule can be: |
caec2956 | 383 | |
ee52e6fa RG |
384 | * an AllRule |
385 | * an AndRule | |
386 | * a MaxQPSIPRule | |
387 | * a MaxQPSRule | |
388 | * a NetmaskGroupRule | |
e7a1029c | 389 | * a NotRule |
55baa1f2 | 390 | * an OpcodeRule |
e7a1029c | 391 | * an OrRule |
df3e393a | 392 | * a QClassRule |
ee52e6fa RG |
393 | * a QTypeRule |
394 | * a RegexRule | |
0bdb1955 | 395 | * a RE2Rule |
55baa1f2 RG |
396 | * a RecordsCountRule |
397 | * a RecordsTypeCountRule | |
ee52e6fa | 398 | * a SuffixMatchNodeRule |
490a29bb | 399 | * a TCPRule |
55baa1f2 | 400 | * a TrailingDataRule |
ee52e6fa | 401 | |
1a2a4e68 | 402 | Some specific actions do not stop the processing when they match, contrary to all other actions: |
13fe35db | 403 | |
1a2a4e68 RG |
404 | * Delay |
405 | * Disable Validation | |
406 | * Log | |
407 | * MacAddr | |
408 | * No Recurse | |
1a2a4e68 RG |
409 | * and of course None |
410 | ||
ecc8a33b | 411 | A convenience function `makeRule()` is supplied which will make a NetmaskGroupRule for you or a SuffixMatchNodeRule |
412 | depending on how you call it. `makeRule("0.0.0.0/0")` will for example match all IPv4 traffic, `makeRule{"be","nl","lu"}` will | |
413 | match all Benelux DNS traffic. | |
414 | ||
773470ca | 415 | More power |
416 | ---------- | |
417 | More powerful things can be achieved by defining a function called | |
418 | `blockFilter()` in the configuration file, which can decide to drop traffic | |
2d1d4a16 | 419 | on any reason it wants. If you return 'true' from there, the query will get |
420 | blocked. | |
421 | ||
422 | A demo on how to do this and many other things can be found on | |
ee52e6fa | 423 | https://github.com/powerdns/pdns/blob/master/pdns/dnsdistconf.lua and |
537524f8 | 424 | the exact definition of `blockFilter()` is at the end of this document. |
2d1d4a16 | 425 | |
426 | ANY or whatever to TC | |
427 | --------------------- | |
490a29bb RG |
428 | The `blockFilter()` also gets passed read/writable copy of the DNS Header, |
429 | via `dq.dh`. | |
ee52e6fa | 430 | If you invoke setQR(1) on that, `dnsdist` knows you turned the packet into |
2d1d4a16 | 431 | a response, and will send the answer directly to the original client. |
432 | ||
433 | If you also called setTC(1), this will tell the remote client to move to | |
434 | TCP/IP, and in this way you can implement ANY-to-TCP even for downstream | |
435 | servers that lack this feature. | |
436 | ||
0940e4eb | 437 | Note that calling `addAnyTCRule()` achieves the same thing, without |
438 | involving Lua. | |
439 | ||
2332b03c | 440 | Rules for traffic exceeding QPS limits |
441 | -------------------------------------- | |
442 | Traffic that exceeds a QPS limit, in total or per IP (subnet) can be matched by a rule. | |
443 | ||
444 | For example: | |
445 | ||
446 | ``` | |
447 | addDelay(MaxQPSIPRule(5, 32, 48), 100) | |
448 | ``` | |
449 | ||
450 | This measures traffic per IPv4 address and per /48 of IPv6, and if traffic for such | |
451 | an address (range) exceeds 5 qps, it gets delayed by 100ms. | |
452 | ||
453 | As another example: | |
454 | ||
455 | ``` | |
456 | addAction(MaxQPSIPRule(5), NoRecurseAction()) | |
457 | ``` | |
458 | ||
459 | This strips the Recursion Desired (RD) bit from any traffic per IPv4 or IPv6 /64 | |
460 | that exceeds 5 qps. This means any those traffic bins is allowed to make a recursor do 'work' | |
461 | for only 5 qps. | |
462 | ||
463 | If this is not enough, try: | |
464 | ||
465 | ``` | |
466 | addAction(MaxQPSIPRule(5), DropAction()) | |
467 | -- or | |
468 | addAction(MaxQPSIPRule(5), TCAction()) | |
469 | ``` | |
470 | ||
471 | This will respectively drop traffic exceeding that 5 QPS limit per IP or range, or return it with TC=1, forcing | |
472 | clients to fall back to TCP/IP. | |
473 | ||
20357a9a | 474 | To turn this per IP or range limit into a global limit, use NotRule(MaxQPSRule(5000)) instead of MaxQPSIPRule. |
2332b03c | 475 | |
379de8b0 | 476 | TeeAction |
477 | --------- | |
478 | This action sends off a copy of a UDP query to another server, and keeps statistics | |
479 | on the responses received. Sample use: | |
480 | ||
481 | ``` | |
482 | > addAction(AllRule(), TeeAction("192.168.1.54")) | |
483 | > getAction(0):printStats() | |
484 | refuseds 0 | |
485 | nxdomains 0 | |
486 | noerrors 0 | |
487 | servfails 0 | |
488 | recv-errors 0 | |
489 | tcp-drops 0 | |
490 | responses 0 | |
491 | other-rcode 0 | |
492 | send-errors 0 | |
493 | queries 0 | |
494 | ``` | |
495 | ||
496 | It is also possible to share a TeeAction between several rules. Statistics | |
497 | will be combined in that case. | |
498 | ||
d8d85a30 | 499 | Lua actions in rules |
500 | -------------------- | |
501 | While we can pass every packet through the `blockFilter()` functions, it is also | |
502 | possible to configure `dnsdist` to only hand off some packets for Lua inspection. | |
503 | If you think Lua is too slow for your query load, or if you are doing heavy processing in Lua, | |
504 | this may make sense. | |
505 | ||
506 | To select specific packets for Lua attention, use `addLuaAction(x, func)`, | |
507 | where x is either a netmask, or a domain suffix, or a table of netmasks or a | |
508 | table of domain suffixes. This is identical to how `addPoolRule()` selects. | |
509 | ||
510 | The function should look like this: | |
511 | ``` | |
497a6e3a RG |
512 | function luarule(dq) |
513 | if(dq.qtype==35) -- NAPTR | |
d8d85a30 | 514 | then |
515 | return DNSAction.Pool, "abuse" -- send to abuse pool | |
516 | else | |
517 | return DNSAction.None, "" -- no action | |
518 | end | |
519 | end | |
520 | ``` | |
521 | ||
ee52e6fa | 522 | Valid return values for `LuaAction` functions are: |
13fe35db | 523 | |
ee52e6fa | 524 | * DNSAction.Allow: let the query pass, skipping other rules |
1a2a4e68 | 525 | * DNSAction.Delay: delay the response for the specified milliseconds (UDP-only), continue to the next rule |
ee52e6fa RG |
526 | * DNSAction.Drop: drop the query |
527 | * DNSAction.HeaderModify: indicate that the query has been turned into a response | |
528 | * DNSAction.None: continue to the next rule | |
529 | * DNSAction.Nxdomain: return a response with a NXDomain rcode | |
88d05ca1 | 530 | * DNSAction.Pool: use the specified pool to forward this query |
7791f83a | 531 | * DNSAction.Spoof: spoof the response using the supplied IPv4 (A), IPv6 (AAAA) or string (CNAME) value |
ee52e6fa | 532 | |
b4fd86c3 | 533 | DNSSEC |
534 | ------ | |
535 | To provide DNSSEC service from a separate pool, try: | |
536 | ``` | |
bf9edc24 RG |
537 | newServer({address="2001:888:2000:1d::2", pool="dnssec"}) |
538 | newServer({address="2a01:4f8:110:4389::2", pool="dnssec"}) | |
b4fd86c3 | 539 | setDNSSECPool("dnssec") |
540 | topRule() | |
541 | ``` | |
542 | ||
543 | This routes all queries with a DNSSEC OK (DO) or CD bit set to on to the "dnssec" pool. | |
544 | The final `topRule()` command moves this rule to the top, so it gets evaluated first. | |
545 | ||
6eecd4c2 | 546 | Regular Expressions |
547 | ------------------- | |
548 | `RegexRule()` matches a regular expression on the query name, and it works like this: | |
549 | ||
550 | ``` | |
551 | addAction(RegexRule("[0-9]{5,}"), DelayAction(750)) -- milliseconds | |
552 | addAction(RegexRule("[0-9]{4,}\\.cn$"), DropAction()) | |
553 | ``` | |
554 | ||
555 | This delays any query for a domain name with 5 or more consecutive digits in it. | |
556 | The second rule drops anything with more than 4 consecutive digits within a .CN domain. | |
557 | ||
558 | Note that the query name is presented without a trailing dot to the regex. | |
559 | The regex is applied case insensitively. | |
560 | ||
0bdb1955 | 561 | Alternatively, if compiled in, RE2Rule provides similar functionality, but against libre2. |
562 | ||
c9262563 | 563 | Inspecting live traffic |
564 | ----------------------- | |
565 | This is still much in flux, but for now, try: | |
566 | ||
2a05b4a9 | 567 | * `grepq(Netmask|DNS Name|100ms [, n])`: shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms |
568 | * `grepq({"::1", "powerdns.com", "100ms"} [, n])`: shows the last n queries and responses matching the specified client address AND range (Netmask) AND the specified DNS Name AND slower than 100ms | |
569 | * `topBandwidth(top)`: show top-`top` clients that consume the most bandwidth over length of ringbuffer | |
2e439533 | 570 | * `topClients(n)`: show top-`n` clients sending the most queries over length of ringbuffer |
bf9edc24 RG |
571 | * `topQueries(20)`: shows the top-20 queries |
572 | * `topQueries(20,2)`: shows the top-20 two-level domain queries (so `topQueries(20,1)` only shows TLDs) | |
573 | * `topResponses(20, 2)`: top-20 servfail responses (use ,3 for NXDOMAIN) | |
2a05b4a9 | 574 | * `topSlow([top][, limit][, labels])`: show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels |
c9262563 | 575 | |
ee52e6fa RG |
576 | For example: |
577 | ``` | |
578 | > grepq("127.0.0.1/24") | |
40df1b89 RG |
579 | Time Client Server ID Name Type Lat. TC RD AA Rcode |
580 | -11.9 127.0.0.1:52599 16127 nxdomain.powerdns.com. A RD Question | |
581 | -11.7 127.0.0.1:52599 127.0.0.1:53 16127 nxdomain.powerdns.com. A 175.6 RD Non-Existent domain | |
ee52e6fa | 582 | > grepq("powerdns.com") |
40df1b89 RG |
583 | Time Client Server ID Name Type Lat. TC RD AA Rcode |
584 | -38.7 127.0.0.1:52599 16127 nxdomain.powerdns.com. A RD Question | |
585 | -38.6 127.0.0.1:52599 127.0.0.1:53 16127 nxdomain.powerdns.com. A 175.6 RD Non-Existent domain | |
ee52e6fa | 586 | ``` |
7c0860e1 | 587 | |
178c03ce | 588 | Live histogram of latency |
589 | ------------------------- | |
590 | ``` | |
591 | > showResponseLatency() | |
8a47f4c5 | 592 | Average response latency: 78.84 msec |
178c03ce | 593 | msec |
594 | 0.10 | |
595 | 0.20 . | |
596 | 0.40 ********************** | |
597 | 0.80 *********** | |
598 | 1.60 . | |
599 | 3.20 | |
600 | 6.40 . | |
601 | 12.80 * | |
602 | 25.60 * | |
603 | 51.20 * | |
604 | 102.40 ********************************************************************** | |
605 | 204.80 ************************* | |
606 | 409.60 ** | |
607 | 819.20 : | |
608 | 1638.40 . | |
609 | ``` | |
610 | ||
611 | Where : stands for 'half a star' and . for 'less than half a star, but | |
612 | something was there'. | |
613 | ||
7c0860e1 | 614 | Per domain or subnet QPS limiting |
615 | --------------------------------- | |
616 | If certain domains or source addresses are generating onerous amounts of | |
617 | traffic, you can put ceilings on the amount of traffic you are willing to | |
618 | forward: | |
619 | ||
620 | ``` | |
621 | > addQPSLimit("h4xorbooter.xyz.", 10) | |
622 | > addQPSLimit({"130.161.0.0/16", "145.14.0.0/16"} , 20) | |
623 | > addQPSLimit({"nl.", "be."}, 1) | |
0940e4eb | 624 | > showRules() |
625 | # Matches Rule Action | |
626 | 0 0 h4xorbooter.xyz. qps limit to 10 | |
627 | 1 0 130.161.0.0/16, 145.14.0.0/16 qps limit to 20 | |
628 | 2 0 nl., be. qps limit to 1 | |
7c0860e1 | 629 | ``` |
630 | ||
0940e4eb | 631 | To delete a limit (or a rule in general): |
7c0860e1 | 632 | ``` |
0940e4eb | 633 | > rmRule(1) |
634 | > showRules() | |
635 | # Matches Rule Action | |
636 | 0 0 h4xorbooter.xyz. qps limit to 10 | |
637 | 1 0 nl., be. qps limit to 1 | |
7c0860e1 | 638 | ``` |
639 | ||
947f59ea | 640 | Delaying answers |
641 | ---------------- | |
642 | Sometimes, runaway scripts will hammer your servers with back-to-back | |
643 | queries. While it is possible to drop such packets, this may paradoxically | |
644 | lead to more traffic. | |
645 | ||
646 | An attractive middleground is to delay answers to such back-to-back queries, | |
647 | causing a slowdown on the side of the source of the traffic. | |
648 | ||
649 | To do so, use: | |
650 | ``` | |
651 | > addDelay("yourdomain.in.ua.", 500) | |
652 | > addDelay({"65.55.37.0/24"}, 500) | |
653 | ``` | |
654 | This will delay responses for questions to the mentioned domain, or coming | |
655 | from the configured subnet, by half a second. | |
656 | ||
657 | Like the QPSLimits and other rules, the delaying instructions can be | |
658 | inspected or edited using showRule(), rmRule(), topRule(), mvRule() etc. | |
659 | ||
2d1d4a16 | 660 | Dynamic load balancing |
661 | ---------------------- | |
5efb31db RG |
662 | The default load balancing policy is called `leastOutstanding`, which means |
663 | we pick the server with the least queries 'in the air' (and within those, | |
664 | the one with the lowest `order`, and within those, the one with the lowest latency). | |
c9262563 | 665 | |
5efb31db RG |
666 | Another policy, `firstAvailable`, picks the server with the lowest `order` that has not |
667 | exceeded its QPS limit. For now this is the only policy using the QPS limit. | |
c9262563 | 668 | |
5efb31db RG |
669 | A further policy, `wrandom` assigns queries randomly, but based on the |
670 | `weight` parameter passed to `newServer`. `whashed` is a similar weighted policy, | |
a7f3108c | 671 | but assigns questions with identical hash to identical servers, allowing for |
672 | better cache concentration ('sticky queries'). | |
c9262563 | 673 | |
674 | If you don't like the default policies you can create your own, like this | |
675 | for example: | |
773470ca | 676 | |
677 | ``` | |
678 | counter=0 | |
497a6e3a | 679 | function luaroundrobin(servers, dq) |
a6c02c23 | 680 | counter=counter+1 |
773470ca | 681 | return servers[1+(counter % #servers)] |
682 | end | |
683 | ||
22b2b326 | 684 | setServerPolicyLua("luaroundrobin", luaroundrobin) |
773470ca | 685 | ``` |
686 | ||
5f504638 | 687 | Incidentally, this is similar to setting: `setServerPolicy(roundrobin)` |
688 | which uses the C++ based roundrobin policy. | |
689 | ||
6bb62841 | 690 | Lua server policies |
691 | ------------------- | |
692 | If the built in rules do not suffice to pick a server pool, full flexibility is available from Lua. For example: | |
75a2db75 | 693 | |
694 | ``` | |
6bb62841 | 695 | newServer("192.168.1.2") |
696 | newServer({address="8.8.4.4", pool="numbered"}) | |
75a2db75 | 697 | |
497a6e3a | 698 | function splitSetup(servers, dq) |
6bb62841 | 699 | if(string.match(dq.qname:toString(), "%d")) |
700 | then | |
701 | print("numbered pool") | |
702 | return leastOutstanding.policy(getPoolServers("numbered"), dq) | |
703 | else | |
704 | print("standard pool") | |
705 | return leastOutstanding.policy(servers, dq) | |
706 | end | |
75a2db75 | 707 | end |
c9262563 | 708 | |
22b2b326 | 709 | setServerPolicyLua("splitsetup", splitSetup) |
75a2db75 | 710 | ``` |
711 | ||
6bb62841 | 712 | This will forward queries containing a number to the pool of "numbered" |
0940e4eb | 713 | servers, and will apply the default load balancing policy to all other |
714 | queries. | |
75a2db75 | 715 | |
60d836d0 | 716 | Dynamic Rule Generation |
717 | ----------------------- | |
718 | To set dynamic rules, based on recent traffic, define a function called `maintenance()` in Lua. It will | |
719 | get called every second, and from this function you can set rules to block traffic based on statistics. | |
720 | ||
721 | As an example: | |
722 | ||
723 | ``` | |
724 | function maintenance() | |
725 | addDynBlocks(exceedQRate(20, 10), "Exceeded query rate", 60) | |
726 | end | |
727 | ``` | |
728 | ||
729 | This will dynamically block all hosts that exceeded 20 queries/s as measured | |
730 | over the past 10 seconds, and the dynamic block will last for 60 seconds. | |
731 | ||
732 | Dynamic blocks in force are displayed with `showDynBlocks()` and can be cleared | |
733 | with `clearDynBlocks()`. Full set of `exceed` functions is listed in the table of | |
734 | all functions below. | |
735 | ||
736 | ||
8c732ebf | 737 | Running it for real |
738 | ------------------- | |
739 | First run on the command line, and generate a key: | |
740 | ||
741 | ``` | |
021e3aba | 742 | # dnsdist |
8c732ebf | 743 | > makeKey() |
744 | setKey("sepuCcHcQnSAZgNbNPCCpDWbujZ5esZJmrt/wh6ldkQ=") | |
745 | ``` | |
021e3aba | 746 | |
fa7d3e7c | 747 | Now add this setKey line to `dnsdistconf.lua`, and also add: |
8c732ebf | 748 | |
fa7d3e7c | 749 | ``` |
750 | controlSocket("0.0.0.0") -- or add portnumber too | |
751 | ``` | |
8c732ebf | 752 | |
fa7d3e7c | 753 | Then start `dnsdist` as a daemon, and then connect to it: |
8c732ebf | 754 | ``` |
021e3aba | 755 | # dnsdist --daemon |
8c732ebf | 756 | # dnsdist --client |
757 | > | |
758 | ``` | |
fa7d3e7c | 759 | |
107d4911 RG |
760 | Please note that, without libsodium support, 'makeKey()' will return |
761 | setKey("plaintext") and the communication between the client and the | |
762 | server will not be encrypted. | |
763 | ||
6d69b621 RG |
764 | Some versions of libedit, notably the CentOS 6 one, may require the following |
765 | addition to ~/.editrc in order to support searching through the history: | |
766 | ||
767 | ``` | |
768 | bind "^R" em-inc-search-prev | |
769 | ``` | |
770 | ||
a40df301 | 771 | ACL, who can use dnsdist |
772 | ------------------------ | |
ee52e6fa | 773 | For safety reasons, by default only private networks can use `dnsdist`, see below |
a40df301 | 774 | how to query and change the ACL: |
775 | ||
776 | ``` | |
777 | > showACL() | |
778 | 127.0.0.0/8 | |
779 | 10.0.0.0/8 | |
780 | (...) | |
781 | ::1/128 | |
782 | fc00::/7 | |
783 | fe80::/10 | |
784 | > addACL("130.161.0.0/16") | |
785 | > setACL({"::/0"}) -- resets the list to this array | |
786 | > showACL() | |
787 | ::/0 | |
788 | ``` | |
8a47f4c5 | 789 | |
886e2cf2 RG |
790 | Caching |
791 | ------- | |
886e2cf2 RG |
792 | `dnsdist` implements a simple but effective packet cache, not enabled by default. |
793 | It is enabled per-pool, but the same cache can be shared between several pools. | |
794 | The first step is to define a cache, then to assign that cache to the chosen pool, | |
795 | the default one being represented by the empty string: | |
796 | ||
797 | ``` | |
cc8cefe1 | 798 | pc = newPacketCache(10000, 86400, 0, 60, 60) |
886e2cf2 RG |
799 | getPool(""):setCache(pc) |
800 | ``` | |
801 | ||
1ea747c0 RG |
802 | The first parameter is the maximum number of entries stored in the cache, and is the |
803 | only one required. All the others parameters are optional and in seconds. | |
804 | The second one is the maximum lifetime of an entry in the cache, the third one is | |
805 | the minimum TTL an entry should have to be considered for insertion in the cache, | |
806 | the fourth one is the TTL used for a Server Failure response. The last one is the | |
807 | TTL that will be used when a stale cache entry is returned. | |
808 | ||
809 | The `setStaleCacheEntriesTTL(n)` directive can be used to allow `dnsdist` to use | |
810 | expired entries from the cache when no backend is available. Only entries that have | |
811 | expired for less than `n` seconds will be used, and the returned TTL can be set | |
812 | when creating a new cache with `newPacketCache()`. | |
813 | ||
814 | A reference to the cache affected to a specific pool can be retrieved with: | |
815 | ||
816 | ``` | |
817 | getPool("poolname"):getCache() | |
818 | ``` | |
819 | ||
f87c4aff RG |
820 | And removed with: |
821 | ||
822 | ``` | |
823 | getPool("poolname"):unsetCache() | |
824 | ``` | |
825 | ||
1ea747c0 RG |
826 | Cache usage stats (hits, misses, deferred inserts and lookups, collisions) |
827 | can be displayed by using the `printStats()` method: | |
828 | ||
829 | ``` | |
830 | getPool("poolname"):getCache():printStats() | |
831 | ``` | |
832 | ||
833 | Expired cached entries can be removed from a cache using the `purgeExpired(n)` | |
834 | method, which will remove expired entries from the cache until at least `n` | |
835 | entries remain in the cache. For example, to remove all expired entries: | |
836 | ||
837 | ``` | |
838 | getPool("poolname"):getCache():purgeExpired(0) | |
839 | ``` | |
840 | ||
841 | Specific entries can also be removed using the `expungeByName(DNSName [, qtype=ANY])` | |
842 | method. | |
843 | ||
844 | ``` | |
845 | getPool("poolname"):getCache():expungeByName(newDNSName("powerdns.com"), dnsdist.A) | |
846 | ``` | |
847 | ||
848 | Finally, the `expunge(n)` method will remove all entries until at most `n` | |
849 | entries remain in the cache: | |
850 | ||
851 | ``` | |
852 | getPool("poolname"):getCache():expunge(0) | |
853 | ``` | |
886e2cf2 RG |
854 | |
855 | ||
13fe35db RG |
856 | Performance tuning |
857 | ------------------ | |
858 | First, a few words about `dnsdist` architecture: | |
859 | ||
860 | * Each local bind has its own thread listening for incoming UDP queries | |
861 | * and its own thread listening for incoming TCP connections, | |
862 | dispatching them right away to a pool of threads | |
863 | * Each backend has its own thread listening for UDP responses | |
864 | * A maintenance thread calls the `maintenance()` Lua function every second | |
865 | if any, and is responsible for cleaning the cache | |
866 | * A health check thread checks the backends availability | |
867 | * A control thread handles console connections | |
2e439533 | 868 | * A carbon thread exports statistics to carbon servers if needed |
13fe35db RG |
869 | * One or more webserver threads handle queries to the internal webserver |
870 | ||
871 | The maximum number of threads in the TCP pool is controlled by the | |
872 | `setMaxTCPClientThreads()` directive, and defaults to 10. This number can be | |
873 | increased to handle a large number of simultaneous TCP connections. | |
6c1ca990 RG |
874 | If all the TCP threads are busy, new TCP connections are queued while |
875 | they wait to be picked up. The maximum number of queued connections | |
876 | can be configured with `setMaxTCPQueuedConnections()`, and any value other | |
877 | than 0 (the default) will cause new connections to be dropped if there | |
878 | are already too many queued. | |
13fe35db RG |
879 | |
880 | When dispatching UDP queries to backend servers, `dnsdist` keeps track of at | |
881 | most `n` outstanding queries for each backend. This number `n` can be tuned by | |
882 | the `setMaxUDPOutstanding()` directive, defaulting to 10240, with a maximum | |
883 | value of 65535. Large installations are advised to increase the default value | |
884 | at the cost of a slightly increased memory usage. | |
885 | ||
886 | Most of the query processing is done in C++ for maximum performance, | |
887 | but some operations are executed in Lua for maximum flexibility: | |
888 | ||
889 | * the `blockfilter()` function | |
890 | * rules added by `addLuaAction()` | |
891 | * server selection policies defined via `setServerPolicyLua()` or `newServerPolicy()` | |
892 | ||
893 | While Lua is fast, its use should be restricted to the strict necessary in order | |
894 | to achieve maximum performance, it might be worth considering using LuaJIT instead | |
895 | of Lua. When Lua inspection is needed, the best course of action is to restrict | |
896 | the queries sent to Lua inspection by using `addLuaAction()` instead of inspecting | |
897 | all queries in the `blockfilter()` function. | |
898 | ||
899 | `dnsdist` design choices mean that the processing of UDP queries is done by only | |
900 | one thread per local bind. This is great to keep lock contention to a low level, | |
901 | but might not be optimal for setups using a lot of processing power, caused for | |
902 | example by a large number of complicated rules. To be able to use more CPU cores | |
903 | for UDP queries processing, it is possible to use the `reuseport` parameter of | |
904 | the `addLocal()` and `setLocal()` directives to be able to add several identical | |
905 | local binds to `dnsdist`: | |
906 | ||
907 | ``` | |
908 | addLocal("192.0.2.1:53", true, true) | |
909 | addLocal("192.0.2.1:53", true, true) | |
910 | addLocal("192.0.2.1:53", true, true) | |
911 | addLocal("192.0.2.1:53", true, true) | |
912 | ``` | |
913 | ||
914 | `dnsdist` will then add four identical local binds as if they were different IPs | |
915 | or ports, start four threads to handle incoming queries and let the kernel load | |
916 | balance those randomly to the threads, thus using four CPU cores for rules | |
917 | processing. Note that this require SO_REUSEPORT support in the underlying | |
918 | operating system (added for example in Linux 3.9). | |
919 | Please also be aware that doing so will increase lock contention and might not | |
920 | therefore scale linearly. This is especially true for Lua-intensive setups, | |
921 | because Lua processing in `dnsdist` is serialized by an unique lock for all | |
922 | threads. | |
923 | ||
924 | Another possibility is to use the reuseport option to run several `dnsdist` | |
925 | processes in parallel on the same host, thus avoiding the lock contention issue | |
926 | at the cost of having to deal with the fact that the different processes will | |
927 | not share informations, like statistics or DDoS offenders. | |
928 | ||
929 | The UDP threads handling the responses from the backends do not use a lot of CPU, | |
930 | but if needed it is also possible to add the same backend several times to the | |
931 | `dnsdist` configuration to distribute the load over several responder threads. | |
932 | ||
933 | ``` | |
934 | newServer({address="192.0.2.127:53", name="Backend1"}) | |
935 | newServer({address="192.0.2.127:53", name="Backend2"}) | |
936 | newServer({address="192.0.2.127:53", name="Backend3"}) | |
937 | newServer({address="192.0.2.127:53", name="Backend4"}) | |
938 | ``` | |
939 | ||
940 | ||
42fae326 | 941 | Carbon/Graphite/Metronome |
942 | ------------------------- | |
943 | To emit metrics to Graphite, or any other software supporting the Carbon protocol, use: | |
944 | ``` | |
945 | carbonServer('ip-address-of-carbon-server', 'ourname', 30) | |
946 | ``` | |
947 | ||
948 | Where 'ourname' can be used to override your hostname, and '30' is the | |
949 | reporting interval in seconds. The last two arguments can be omitted. The | |
950 | latest version of [PowerDNS | |
951 | Metronome](https://github.com/ahupowerdns/metronome) comes with attractive | |
ee52e6fa | 952 | graphs for `dnsdist` by default. |
42fae326 | 953 | |
11e1e08b RG |
954 | DNSCrypt |
955 | -------- | |
956 | `dnsdist`, when compiled with --enable-dnscrypt, can be used as a DNSCrypt server, | |
957 | uncurving queries before forwarding them to downstream servers and curving responses back. | |
bf9edc24 | 958 | To make `dnsdist` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, |
11e1e08b RG |
959 | with a provider name of "2.providername", using a resolver certificate and associated key |
960 | stored respectively in the `resolver.cert` and `resolver.key` files, the `addDnsCryptBind()` | |
961 | directive can be used: | |
962 | ||
963 | ``` | |
964 | addDNSCryptBind("127.0.0.1:8443", "2.providername", "/path/to/resolver.cert", "/path/to/resolver.key") | |
965 | ``` | |
966 | ||
967 | To generate the provider and resolver certificates and keys, you can simply do: | |
968 | ||
969 | ``` | |
970 | > generateDNSCryptProviderKeys("/path/to/providerPublic.key", "/path/to/providerPrivate.key") | |
b8db58a2 | 971 | Provider fingerprint is: E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA |
11e1e08b RG |
972 | > generateDNSCryptCertificate("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil) |
973 | ``` | |
974 | ||
975 | Ideally, the certificates and keys should be generated on an offline dedicated hardware and not on the resolver. | |
976 | The resolver key should be regularly rotated and should never touch persistent storage, being stored in a tmpfs | |
977 | with no swap configured. | |
978 | ||
979 | You can display the currently configured DNSCrypt binds with: | |
980 | ``` | |
981 | > showDNSCryptBinds() | |
982 | # Address Provider Name Serial Validity P. Serial P. Validity | |
983 | 0 127.0.0.1:8443 2.name 14 2016-04-10 08:14:15 0 - | |
984 | ``` | |
985 | ||
b8db58a2 RG |
986 | If you forgot to write down the provider fingerprint value after generating the provider keys, you can use `printDNSCryptProviderFingerprint()` to retrieve it later: |
987 | ``` | |
988 | > printDNSCryptProviderFingerprint("/path/to/providerPublic.key") | |
989 | Provider fingerprint is: E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA | |
990 | ``` | |
991 | ||
548c8b66 RG |
992 | AXFR, IXFR and NOTIFY |
993 | --------------------- | |
994 | When `dnsdist` is deployed in front of a master authoritative server, it might | |
995 | receive AXFR or IXFR queries destined to this master. There are two issues | |
996 | that can arise in this kind of setup: | |
997 | ||
998 | * If the master is part of a pool of servers, the first SOA query can be directed | |
999 | by `dnsdist` to a different server than the following AXFR/IXFR one. If all servers are not | |
1000 | perfectly synchronised at all times, it might to synchronisation issues. | |
1001 | * If the master only allows AXFR/IXFR based on the source address of the requestor, | |
1002 | it might be confused by the fact that the source address will be the one from | |
1003 | the `dnsdist` server. | |
1004 | ||
1005 | The first issue can be solved by routing SOA, AXFR and IXFR requests explicitely | |
1006 | to the master: | |
1007 | ||
1008 | ``` | |
1009 | > newServer({address="192.168.1.2", name="master", pool={"master", "otherpool"}}) | |
1010 | > addAction(OrRule({QTypeRule(dnsdist.SOA), QTypeRule(dnsdist.AXFR), QTypeRule(dnsdist.IXFR)}), PoolAction("master")) | |
1011 | ``` | |
1012 | ||
1013 | The second one might requires allowing AXFR/IXFR from the `dnsdist` source address | |
1014 | and moving the source address check on `dnsdist`'s side: | |
1015 | ||
1016 | ``` | |
1017 | > addAction(AndRule({OrRule({QTypeRule(dnsdist.AXFR), QTypeRule(dnsdist.IXFR)}), NotRule(makeRule("192.168.1.0/24"))}), RCodeAction(dnsdist.REFUSED)) | |
1018 | ``` | |
1019 | ||
1020 | When `dnsdist` is deployed in front of slaves, however, an issue might arise with NOTIFY | |
1021 | queries, because the slave will receive a notification coming from the `dnsdist` address, | |
1022 | and not the master's one. One way to fix this issue is to allow NOTIFY from the `dnsdist` | |
1023 | address on the slave side (for example with PowerDNS's `trusted-notification-proxy`) and | |
1024 | move the address check on `dnsdist`'s side: | |
1025 | ||
1026 | ``` | |
1027 | > addAction(AndRule({OpcodeRule(DNSOpcode.Notify), NotRule(makeRule("192.168.1.0/24"))}), RCodeAction(dnsdist.REFUSED)) | |
1028 | ``` | |
1029 | ||
87b515ed RG |
1030 | eBPF Socket Filtering |
1031 | --------------------- | |
1032 | `dnsdist` can use eBPF socket filtering on recent Linux kernels (4.1+) built with eBPF | |
1033 | support (`CONFIG_BPF`, `CONFIG_BPF_SYSCALL`, ideally `CONFIG_BPF_JIT`). | |
d45189b7 | 1034 | This feature might require an increase of the memory limit associated to a socket, via |
87b515ed RG |
1035 | `the sysctl` setting `net.core.optmem_max`. When attaching an eBPF program to a socket, |
1036 | the size of the program is checked against this limit, and the default value might not be | |
1037 | enough. Large map sizes might also require an increase of `RLIMIT_MEMLOCK`. | |
1038 | ||
1039 | This feature allows `dnsdist` to ask the kernel to discard incoming packets in kernel-space | |
1040 | instead of them being copied to userspace just to be dropped, thus being a lot of faster. | |
1041 | ||
1042 | The BPF filter can be used to block incoming queries manually: | |
1043 | ||
1044 | ``` | |
1045 | > bpf = newBPFFilter(1024, 1024, 1024) | |
1046 | > bpf:attachToAllBinds() | |
d45189b7 | 1047 | > bpf:block(newCA("2001:DB8::42")) |
87b515ed RG |
1048 | > bpf:blockQName(newDNSName("evildomain.com"), 255) |
1049 | > bpf:getStats() | |
1050 | [2001:DB8::42]: 0 | |
1051 | evildomain.com. 255: 0 | |
d45189b7 | 1052 | > bpf:unblock(newCA("2001:DB8::42")) |
87b515ed RG |
1053 | > bpf:unblockQName(newDNSName("evildomain.com"), 255) |
1054 | > bpf:getStats() | |
1055 | > | |
1056 | ``` | |
1057 | ||
1058 | The `blockQName()` method can be used to block queries based on the exact qname supplied, | |
d45189b7 RG |
1059 | in a case-insensitive way, and an optional qtype. Using the 255 (ANY) qtype will block all |
1060 | queries for the qname, regardless of the qtype. | |
1061 | Contrary to source address filtering, qname filtering only works over UDP. TCP qname | |
1062 | filtering can be done the usual way: | |
87b515ed RG |
1063 | |
1064 | ``` | |
1065 | > addAction(AndRule({TCPRule(true), makeRule("evildomain.com")}), DropAction()) | |
1066 | ``` | |
1067 | ||
1068 | The `attachToAllBinds()` method attach the filter to every existing binds at runtime, | |
1069 | but it's also possible to define a default BPF filter at configuration time, so that | |
1070 | it's automatically attached to every binds: | |
1071 | ||
1072 | ``` | |
1073 | bpf = newBPFFilter(1024, 1024, 1024) | |
1074 | setDefaultBPFFilter(bpf) | |
1075 | ``` | |
1076 | ||
1077 | Finally, it's also possible to attach it to only specific binds at runtime: | |
1078 | ||
1079 | ``` | |
1080 | > bpf = newBPFFilter(1024, 1024, 1024) | |
1081 | > showBinds() | |
1082 | # Address Protocol Queries | |
1083 | 0 [::]:53 UDP 0 | |
1084 | 1 [::]:53 TCP 0 | |
1085 | > bd = getBind(0) | |
1086 | > bd:attachFilter(bpf) | |
1087 | ``` | |
1088 | ||
1089 | `dnsdist` also supports adding dynamic, expiring blocks to a BPF filter: | |
1090 | ||
1091 | ``` | |
1092 | bpf = newBPFFilter(1024, 1024, 1024) | |
1093 | setDefaultBPFFilter(bpf) | |
1094 | dbpf = newDynBPFFilter(bpf) | |
1095 | function maintenance() | |
1096 | addBPFFilterDynBlocks(exceedQRate(20, 10), dbpf, 60) | |
1097 | dbpf:purgeExpired() | |
1098 | end | |
1099 | ``` | |
1100 | ||
1101 | This will dynamically block all hosts that exceeded 20 queries/s as measured | |
1102 | over the past 10 seconds, and the dynamic block will last for 60 seconds. | |
1103 | ||
d45189b7 RG |
1104 | This feature has been successfully tested on Arch Linux, Arch Linux ARM, |
1105 | Fedora Core 23 and Ubuntu Xenial. | |
548c8b66 | 1106 | |
8a47f4c5 | 1107 | All functions and types |
1108 | ----------------------- | |
1109 | Within `dnsdist` several core object types exist: | |
caec2956 | 1110 | |
8a47f4c5 | 1111 | * Server: generated with newServer, represents a downstream server |
1112 | * ComboAddress: represents an IP address and port | |
1113 | * DNSName: represents a domain name | |
1114 | * NetmaskGroup: represents a group of netmasks | |
1115 | * QPSLimiter: implements a QPS-based filter | |
1116 | * SuffixMatchNode: represents a group of domain suffixes for rapid testing of membership | |
1117 | * DNSHeader: represents the header of a DNS packet | |
1118 | ||
1119 | The existence of most of these objects can mostly be ignored, unless you | |
1120 | plan to write your own hooks and policies, but it helps to understand an | |
1121 | expressions like: | |
1122 | ||
1123 | ``` | |
1124 | > getServer(0).order=12 -- set order of server 0 to 12 | |
1125 | > getServer(0):addPool("abuse") -- add this server to the abuse pool | |
1126 | ``` | |
1127 | The '.' means 'order' is a data member, while the ':' meand addPool is a member function. | |
1128 | ||
1129 | Here are all functions: | |
1130 | ||
0940e4eb | 1131 | * Practical |
caec2956 PL |
1132 | * `shutdown()`: shut down `dnsdist` |
1133 | * quit or ^D: exit the console | |
002decab | 1134 | * `webserver(address:port, password [, apiKey [, customHeaders ]])`: launch a webserver with stats on that address with that password |
8a47f4c5 | 1135 | * ACL related: |
caec2956 PL |
1136 | * `addACL(netmask)`: add to the ACL set who can use this server |
1137 | * `setACL({netmask, netmask})`: replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us | |
1138 | * `showACL()`: show our ACL set | |
87b515ed RG |
1139 | * ClientState related: |
1140 | * function `showBinds()`: list every local binds | |
1141 | * function `getBind(n)`: return the corresponding `ClientState` object | |
1142 | * member `attachFilter(BPFFilter)`: attach a BPF Filter to this bind | |
1143 | * member `toString()`: print the address this bind listens to | |
5949b95b | 1144 | * Network related: |
4c34246d RG |
1145 | * `addLocal(netmask, [false], [false])`: add to addresses we listen on. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available. |
1146 | * `setLocal(netmask, [false], [false])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available. | |
8a47f4c5 | 1147 | * Blocking related: |
caec2956 | 1148 | * `addDomainBlock(domain)`: block queries within this domain |
42fae326 | 1149 | * Carbon/Graphite/Metronome statistics related: |
caec2956 | 1150 | * `carbonServer(serverIP, [ourname], [interval])`: report statistics to serverIP using our hostname, or 'ourname' if provided, every 'interval' seconds |
8a47f4c5 | 1151 | * Control socket related: |
caec2956 PL |
1152 | * `makeKey()`: generate a new server access key, emit configuration line ready for pasting |
1153 | * `setKey(key)`: set access key to that key. | |
1154 | * `testCrypto()`: test of the crypto all works | |
1155 | * `controlSocket(addr)`: open a control socket on this address / connect to this address in client mode | |
8a47f4c5 | 1156 | * Diagnostics and statistics |
caec2956 PL |
1157 | * `dumpStats()`: print all statistics we gather |
1158 | * `grepq(Netmask|DNS Name|100ms [, n])`: shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms | |
1159 | * `grepq({"::1", "powerdns.com", "100ms"} [, n])`: shows the last n queries and responses matching the specified client address AND range (Netmask) AND the specified DNS Name AND slower than 100ms | |
1160 | * `topQueries(n[, labels])`: show top 'n' queries, as grouped when optionally cut down to 'labels' labels | |
1161 | * `topResponses(n, kind[, labels])`: show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels | |
1162 | * `topSlow([top][, limit][, labels])`: show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels | |
1163 | * `topBandwidth(top)`: show top-`top` clients that consume the most bandwidth over length of ringbuffer | |
2e439533 | 1164 | * `topClients(n)`: show top-`n` clients sending the most queries over length of ringbuffer |
caec2956 | 1165 | * `showResponseLatency()`: show a plot of the response time latency distribution |
537524f8 | 1166 | * Logging related |
caec2956 PL |
1167 | * `infolog(string)`: log at level info |
1168 | * `warnlog(string)`: log at level warning | |
1169 | * `errlog(string)`: log at level error | |
fd23d9bb | 1170 | * `setVerboseHealthChecks(bool)`: set whether health check errors will be logged |
8a47f4c5 | 1171 | * Server related: |
caec2956 | 1172 | * `newServer("ip:port")`: instantiate a new downstream server with default settings |
9e87dcb8 | 1173 | * `newServer({address="ip:port", qps=1000, order=1, weight=10, pool="abuse", retries=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName="a.root-servers.net.", checkType="A", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source="address|interface name|address@interface"})`: |
66407d9e | 1174 | instantiate a server with additional parameters |
caec2956 PL |
1175 | * `showServers()`: output all servers |
1176 | * `getServer(n)`: returns server with index n | |
1177 | * `getServers()`: returns a table with all defined servers | |
1178 | * `rmServer(n)`: remove server with index n | |
1179 | * `rmServer(server)`: remove this server object | |
8a47f4c5 | 1180 | * Server member functions: |
caec2956 | 1181 | * `addPool(pool)`: add this server to that pool |
46a839bf RG |
1182 | * `getName()`: return the server name if any |
1183 | * `getNameWithAddr()`: return a string containing the server name if any plus the server address and port | |
caec2956 PL |
1184 | * `getOutstanding()`: this *returns* the number of outstanding queries (doesn't print it!) |
1185 | * `rmPool(pool)`: remove server from that pool | |
1186 | * `setQPS(n)`: set the QPS setting to n | |
1187 | * `setAuto()`: set this server to automatic availability testing | |
1188 | * `setDown()`: force this server to be down | |
1189 | * `setUp()`: force this server to be UP | |
1190 | * `isUp()`: if this server is available | |
8a47f4c5 | 1191 | * Server member data: |
caec2956 | 1192 | * `upStatus`: if `dnsdist` considers this server available (overridden by `setDown()` and `setUp()`) |
46a839bf | 1193 | * `name`: name of the server |
caec2956 PL |
1194 | * `order`: order of this server in order-based server selection policies |
1195 | * `weight`: weight of this server in weighted server selection policies | |
0940e4eb | 1196 | * Rule related: |
caec2956 PL |
1197 | * `AllRule()`: matches all traffic |
1198 | * `AndRule()`: matches if all sub-rules matches | |
1199 | * `DNSSECRule()`: matches queries with the DO flag set | |
1200 | * `MaxQPSIPRule(qps, v4Mask=32, v6Mask=64)`: matches traffic exceeding the qps limit per subnet | |
20357a9a | 1201 | * `MaxQPSRule(qps)`: matches traffic **not** exceeding this qps limit |
caec2956 PL |
1202 | * `NetmaskGroupRule()`: matches traffic from the specified network range |
1203 | * `NotRule()`: matches if the sub-rule does not match | |
1204 | * `OrRule()`: matches if at least one of the sub-rules matches | |
55baa1f2 | 1205 | * `OpcodeRule()`: matches queries with the specified opcode |
caec2956 PL |
1206 | * `QClassRule(qclass)`: matches queries with the specified qclass (numeric) |
1207 | * `QTypeRule(qtype)`: matches queries with the specified qtype | |
1208 | * `RegexRule(regex)`: matches the query name against the supplied regex | |
55baa1f2 RG |
1209 | * `RecordsCountRule(section, minCount, maxCount)`: matches if there is at least `minCount` and at most `maxCount` records in the `section` section |
1210 | * `RecordsTypeCountRule(section, type, minCount, maxCount)`: matches if there is at least `minCount` and at most `maxCount` records of type `type` in the `section` section | |
1211 | * `RE2Rule(regex)`: matches the query name against the supplied regex using the RE2 engine | |
291729f3 | 1212 | * `SuffixMatchNodeRule(smn, [quiet-bool])`: matches based on a group of domain suffixes for rapid testing of membership. Pass `true` as second parameter to prevent listing of all domains matched. |
caec2956 | 1213 | * `TCPRule(tcp)`: matches question received over TCP if `tcp` is true, over UDP otherwise |
55baa1f2 | 1214 | * `TrailingDataRule()`: matches if the query has trailing data |
ee52e6fa | 1215 | * Rule management related: |
379de8b0 | 1216 | * `getAction(num)`: returns the Action associate with rule 'num'. |
caec2956 | 1217 | * `showRules()`: show all defined rules (Pool, Block, QPS, addAnyTCRule) |
d4d5c5c3 RG |
1218 | * `mvResponseRule(from, to)`: move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, |
1219 | in which case the rule will be moved to the last position. | |
caec2956 | 1220 | * `mvRule(from, to)`: move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, |
0940e4eb | 1221 | in which case the rule will be moved to the last position. |
d4d5c5c3 RG |
1222 | * `rmResponseRule(n)`: remove response rule n |
1223 | * `rmRule(n)`: remove rule n | |
1224 | * `topResponseRule()`: move the last response rule to the first position | |
caec2956 | 1225 | * `topRule()`: move the last rule to the first position |
8e50d605 | 1226 | * Built-in Actions for Rules: |
caec2956 | 1227 | * `AllowAction()`: let these packets go through |
b1bec9f0 | 1228 | * `DelayAction(milliseconds)`: delay the response by the specified amount of milliseconds (UDP-only) |
caec2956 PL |
1229 | * `DisableValidationAction()`: set the CD bit in the question, let it go through |
1230 | * `DropAction()`: drop these packets | |
18029431 | 1231 | * `LogAction([filename], [binary])`: Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form |
caec2956 | 1232 | * `NoRecurseAction()`: strip RD bit from the question, let it go through |
b1bec9f0 | 1233 | * `PoolAction(poolname)`: set the packet into the specified pool |
20357a9a | 1234 | * `QPSPoolAction(maxqps, poolname)`: set the packet into the specified pool only if it **does not** exceed the specified QPS limits, letting the subsequent rules apply otherwise |
b1bec9f0 RG |
1235 | * `QPSAction(rule, maxqps)`: drop these packets if the QPS limits are exceeded |
1236 | * `RCodeAction(rcode)`: reply immediatly by turning the query into a response with the specified rcode | |
27dfcc14 RG |
1237 | * `RemoteLogAction(RemoteLogger)`: send the content of this query to a remote logger via Protocol Buffer |
1238 | * `RemoteLogResponseAction(RemoteLogger)`: send the content of this response to a remote logger via Protocol Buffer | |
886e2cf2 | 1239 | * `SkipCacheAction()`: don't lookup the cache for this query, don't store the answer |
7b9d167c | 1240 | * `SpoofAction(ip[, ip])` or `SpoofAction({ip, ip, ..}): forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in |
b1bec9f0 | 1241 | * `SpoofCNAMEAction(cname)`: forge a response with the specified CNAME value |
caec2956 | 1242 | * `TCAction()`: create answer to query with TC and RD bits set, to move to TCP/IP |
379de8b0 | 1243 | * `TeeAction(remote)`: send copy of query to remote, keep stats on responses |
b4fd86c3 | 1244 | * Specialist rule generators |
caec2956 | 1245 | * `addAnyTCRule()`: generate TC=1 answers to ANY queries received over UDP, moving them to TCP |
7b9d167c | 1246 | * `addDomainSpoof(domain, ip[, ip6])` or `addDomainSpoof(domain, {IP, IP, IP..})`: generate answers for A/AAAA/ANY queries using the ip parameters |
caec2956 PL |
1247 | * `addDomainCNAMESpoof(domain, cname)`: generate CNAME answers for queries using the specified value |
1248 | * `addDisableValidationRule(domain)`: set the CD flags to 1 for all queries matching the specified domain | |
1249 | * `addNoRecurseRule(domain)`: clear the RD flag for all queries matching the specified domain | |
1250 | * `setDNSSECPool()`: move queries requesting DNSSEC processing to this pool | |
ee52e6fa | 1251 | * Policy member data: |
caec2956 PL |
1252 | * `name`: the policy name |
1253 | * `policy`: the policy function | |
8a47f4c5 | 1254 | * Pool related: |
caec2956 PL |
1255 | * `addPoolRule(domain, pool)`: send queries to this domain to that pool |
1256 | * `addPoolRule({domain, domain}, pool)`: send queries to these domains to that pool | |
1257 | * `addPoolRule(netmask, pool)`: send queries to this netmask to that pool | |
1258 | * `addPoolRule({netmask, netmask}, pool)`: send queries to these netmasks to that pool | |
20357a9a | 1259 | * `addQPSPoolRule(x, limit, pool)`: like `addPoolRule`, but only select at most 'limit' queries/s for this pool, letting the subsequent rules apply otherwise |
886e2cf2 | 1260 | * `getPool(poolname)`: return the ServerPool named `poolname` |
caec2956 | 1261 | * `getPoolServers(pool)`: return servers part of this pool |
886e2cf2 | 1262 | * `showPools()`: list the current server pools |
d8d85a30 | 1263 | * Lua Action related: |
caec2956 | 1264 | * `addLuaAction(x, func)`: where 'x' is all the combinations from `addPoolRule`, and func is a |
f659cc0c PL |
1265 | function with the parameter `dq`, which returns an action to be taken on this packet. |
1266 | Good for rare packets but where you want to do a lot of processing. | |
8a47f4c5 | 1267 | * Server selection policy related: |
caec2956 PL |
1268 | * `setServerPolicy(policy)`: set server selection policy to that policy |
1269 | * `setServerPolicyLua(name, function)`: set server selection policy to one named 'name' and provided by 'function' | |
1270 | * `showServerPolicy()`: show name of currently operational server selection policy | |
1271 | * `newServerPolicy(name, function)`: create a policy object from a Lua function | |
8a47f4c5 | 1272 | * Available policies: |
caec2956 PL |
1273 | * `firstAvailable`: Pick first server that has not exceeded its QPS limit, ordered by the server 'order' parameter |
1274 | * `whashed`: Weighted hashed ('sticky') distribution over available servers, based on the server 'weight' parameter | |
1275 | * `wrandom`: Weighted random over available servers, based on the server 'weight' parameter | |
1276 | * `roundrobin`: Simple round robin over available servers | |
1277 | * `leastOutstanding`: Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency | |
537524f8 | 1278 | * Shaping related: |
caec2956 PL |
1279 | * `addQPSLimit(domain, n)`: limit queries within that domain to n per second |
1280 | * `addQPSLimit({domain, domain}, n)`: limit queries within those domains (together) to n per second | |
1281 | * `addQPSLimit(netmask, n)`: limit queries within that netmask to n per second | |
1282 | * `addQPSLimit({netmask, netmask}, n)`: limit queries within those netmasks (together) to n per second | |
947f59ea | 1283 | * Delaying related: |
caec2956 PL |
1284 | * `addDelay(domain, n)`: delay answers within that domain by n milliseconds |
1285 | * `addDelay({domain, domain}, n)`: delay answers within those domains (together) by n milliseconds | |
1286 | * `addDelay(netmask, n)`: delay answers within that netmask by n milliseconds | |
1287 | * `addDelay({netmask, netmask}, n)`: delay answers within those netmasks (together) by n milliseconds | |
6ad8b29a | 1288 | * Answer changing functions: |
caec2956 PL |
1289 | * `truncateTC(bool)`: if set (default) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. |
1290 | * `fixupCase(bool)`: if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer | |
e35d713e | 1291 | * Dynamic Block related: |
caec2956 PL |
1292 | * `maintenance()`: called every second by dnsdist if defined, call functions below from it |
1293 | * `clearDynBlocks()`: clear all dynamic blocks | |
1294 | * `showDynBlocks()`: show dynamic blocks in force | |
1295 | * `addDynBlocks(addresses, message[, seconds])`: block the set of addresses with message `msg`, for `seconds` seconds (10 by default) | |
87b515ed | 1296 | * `addBPFFilterDynBlocks(addresses, DynBPFFilter[, seconds])`: block the set of addresses using the supplied BPF Filter, for `seconds` seconds (10 by default) |
caec2956 PL |
1297 | * `exceedServFails(rate, seconds)`: get set of addresses that exceed `rate` servails/s over `seconds` seconds |
1298 | * `exceedNXDOMAINs(rate, seconds)`: get set of addresses that exceed `rate` NXDOMAIN/s over `seconds` seconds | |
1299 | * `exceedRespByterate(rate, seconds)`: get set of addresses that exeeded `rate` bytes/s answers over `seconds` seconds | |
1300 | * `exceedQRate(rate, seconds)`: get set of address that exceed `rate` queries/s over `seconds` seconds | |
1301 | * `exceedQTypeRate(type, rate, seconds)`: get set of address that exceed `rate` queries/s for queries of type `type` over `seconds` seconds | |
886e2cf2 RG |
1302 | * ServerPool related: |
1303 | * `getCache()`: return the current packet cache, if any | |
1304 | * `setCache(PacketCache)`: set the cache for this pool | |
f87c4aff | 1305 | * `unsetCache()`: remove the packet cache from this pool |
886e2cf2 | 1306 | * PacketCache related: |
4275aaba RG |
1307 | * `expunge(n)`: remove entries from the cache, leaving at most `n` entries |
1308 | * `expungeByName(DNSName [, qtype=ANY])`: remove entries matching the supplied DNSName and type from the cache | |
886e2cf2 | 1309 | * `isFull()`: return true if the cache has reached the maximum number of entries |
cc8cefe1 | 1310 | * `newPacketCache(maxEntries[, maxTTL=86400, minTTL=0, servFailTTL=60, stateTTL=60])`: return a new PacketCache |
886e2cf2 | 1311 | * `printStats()`: print the cache stats (hits, misses, deferred lookups and deferred inserts) |
4275aaba | 1312 | * `purgeExpired(n)`: remove expired entries from the cache until there is at most `n` entries remaining in the cache |
886e2cf2 | 1313 | * `toString()`: return the number of entries in the Packet Cache, and the maximum number of entries |
8a47f4c5 | 1314 | * Advanced functions for writing your own policies and hooks |
caec2956 PL |
1315 | * ComboAddress related: |
1316 | * `newCA(address)`: return a new ComboAddress | |
1317 | * `getPort()`: return the port number | |
1318 | * `tostring()`: return in human-friendly format | |
1319 | * `toString()`: alias for `tostring()` | |
1320 | * `tostringWithPort()`: return in human-friendly format, with port number | |
1321 | * `toStringWithPort()`: alias for `tostringWithPort()` | |
1322 | * DNSName related: | |
1323 | * `newDNSName(name)`: make a DNSName based on this .-terminated name | |
1324 | * member `isPartOf(dnsname)`: is this dnsname part of that dnsname | |
1325 | * member `tostring()`: return as a human friendly . terminated string | |
1326 | * member `toString()`: alias for `tostring()` | |
1327 | * DNSQuestion related: | |
1328 | * member `dh`: DNSHeader | |
1329 | * member `len`: the question length | |
1330 | * member `localaddr`: ComboAddress of the local bind this question was received on | |
55baa1f2 | 1331 | * member `opcode`: the question opcode |
caec2956 PL |
1332 | * member `qname`: DNSName of this question |
1333 | * member `qclass`: QClass (as an unsigned integer) of this question | |
1334 | * member `qtype`: QType (as an unsigned integer) of this question | |
1335 | * member `remoteaddr`: ComboAddress of the remote client | |
1336 | * member `rcode`: RCode of this question | |
1337 | * member `size`: the total size of the buffer starting at `dh` | |
a14c7f7b | 1338 | * member `skipCache`: whether to skip cache lookup / storing the answer for this question (settable) |
caec2956 PL |
1339 | * member `tcp`: whether this question was received over a TCP socket |
1340 | * DNSHeader related | |
1341 | * member `getRD()`: get recursion desired flag | |
1342 | * member `setRD(bool)`: set recursion desired flag | |
1343 | * member `setTC(bool)`: set truncation flag (TC) | |
1344 | * member `setQR(bool)`: set Query Response flag (setQR(true) indicates an *answer* packet) | |
1345 | * member `getCD()`: get checking disabled flag | |
1346 | * member `setCD(bool)`: set checking disabled flag | |
1347 | * NetmaskGroup related | |
1348 | * function `newNMG()`: returns a NetmaskGroup | |
1349 | * member `addMask(mask)`: adds `mask` to the NetmaskGroup | |
1350 | * member `match(ComboAddress)`: checks if ComboAddress is matched by this NetmaskGroup | |
1351 | * member `clear()`: clears the NetmaskGroup | |
1352 | * member `size()`: returns number of netmasks in this NetmaskGroup | |
1353 | * QPSLimiter related: | |
1354 | * `newQPSLimiter(rate, burst)`: configure a QPS limiter with that rate and that burst capacity | |
1355 | * member `check()`: check if this QPSLimiter has a token for us. If yes, you must use it. | |
1356 | * SuffixMatchNode related: | |
1357 | * `newSuffixMatchNode()`: returns a new SuffixMatchNode | |
1358 | * member `check(DNSName)`: returns true if DNSName is matched by this group | |
1359 | * member `add(DNSName)`: add this DNSName to the node | |
e41f8165 | 1360 | * Tuning related: |
caec2956 PL |
1361 | * `setTCPRecvTimeout(n)`: set the read timeout on TCP connections from the client, in seconds |
1362 | * `setTCPSendTimeout(n)`: set the write timeout on TCP connections from the client, in seconds | |
1363 | * `setMaxTCPClientThreads(n)`: set the maximum of TCP client threads, handling TCP connections | |
6c1ca990 | 1364 | * `setMaxTCPQueuedConnections(n)`: set the maximum number of TCP connections queued (waiting to be picked up by a client thread) |
13fe35db | 1365 | * `setMaxUDPOutstanding(n)`: set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 10240 |
19109f73 | 1366 | * `setCacheCleaningDelay(n)`: set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries |
1ea747c0 | 1367 | * `setStaleCacheEntriesTTL(n)`: allows using cache entries expired for at most `n` seconds when no backend available to answer for a query |
bf9edc24 | 1368 | * DNSCrypt related: |
4c34246d | 1369 | * `addDNSCryptBind("127.0.0.1:8443", "provider name", "/path/to/resolver.cert", "/path/to/resolver.key", [false]):` listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of "provider name", using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The last optional parameter sets SO_REUSEPORT when available |
caec2956 PL |
1370 | * `generateDNSCryptProviderKeys("/path/to/providerPublic.key", "/path/to/providerPrivate.key"):` generate a new provider keypair |
1371 | * `generateDNSCryptCertificate("/path/to/providerPrivate.key", "/path/to/resolver.cert", "/path/to/resolver.key", serial, validFrom, validUntil):` generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key | |
1372 | * `printDNSCryptProviderFingerprint("/path/to/providerPublic.key")`: display the fingerprint of the provided resolver public key | |
1373 | * `showDNSCryptBinds():`: display the currently configured DNSCrypt binds | |
87b515ed RG |
1374 | * BPFFilter related: |
1375 | * function `newBPFFilter(maxV4, maxV6, maxQNames)`: return a new eBPF socket filter with a maximum of maxV4 IPv4, maxV6 IPv6 and maxQNames qname entries in the block tables | |
1376 | * function `setDefaultBPFFilter(BPFFilter)`: when used at configuration time, the corresponding BPFFilter will be attached to every binds | |
1377 | * member `attachToAllBinds()`: attach this filter to every binds already defined. This is the run-time equivalent of `setDefaultBPFFilter(bpf)` | |
1378 | * member `block(ComboAddress)`: block this address | |
1379 | * member `blockQName(DNSName [, qtype=255])`: block queries for this exact qname. An optional qtype can be used, default to 255 | |
1380 | * member `getStats()`: print the block tables | |
1381 | * member `unblock(ComboAddress)`: unblock this address | |
1382 | * member `unblockQName(DNSName [, qtype=255])`: remove this qname from the block list | |
1383 | * DynBPFFilter related: | |
1384 | * function `newDynBPFFilter(BPFFilter)`: return a new DynBPFFilter object using this BPF Filter | |
1385 | * member `block(ComboAddress[, seconds]): add this address to the underlying BPF Filter for `seconds` seconds (default to 10 seconds) | |
1386 | * member `purgeExpired()`: remove expired entries | |
27dfcc14 | 1387 | * RemoteLogger related: |
ec469dd7 | 1388 | * `newRemoteLogger(address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1])`: create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()` |
8a47f4c5 | 1389 | |
1390 | All hooks | |
1391 | --------- | |
1392 | `dnsdist` can call Lua per packet if so configured, and will do so with the following hooks: | |
1393 | ||
15e20e4b | 1394 | * `bool blockFilter(dq)`: if defined, called for every packet. If this |
8a47f4c5 | 1395 | returns true, the packet is dropped. If false is returned, `dnsdist` will check if the DNSHeader indicates |
1396 | the packet is now a query response. If so, `dnsdist` will answer the client directly with the modified packet. | |
497a6e3a | 1397 | * `server policy(candidates, DNSQuestion)`: if configured with `setServerPolicyLua()` |
8a47f4c5 | 1398 | gets called for every packet. Candidates is a table of potential servers to pick from, ComboAddress is the |
1399 | address of the requestor, DNSName and qtype describe name and type of query. DNSHeader meanwhile is available for | |
1400 | your inspection. | |
1401 | ||
1402 | ||
1a5f0162 | 1403 |