]>
Commit | Line | Data |
---|---|---|
20d81666 PL |
1 | Configuring Downstream Servers |
2 | ============================== | |
3 | ||
4 | As dnsdist is a loadbalancer and does not do any DNS resolving or serving by itself, it needs downstream servers. | |
5 | To add downstream servers, either include them on the command line:: | |
6 | ||
7 | dnsdist -l 130.161.252.29 -a 130.161.0.0/16 8.8.8.8 208.67.222.222 2620:0:ccc::2 2620:0:ccd::2 | |
8 | ||
9 | Or add them to the configuration file: | |
10 | ||
11 | .. code-block:: lua | |
12 | ||
13 | setLocal("130.161.252.29:53") | |
14 | setACL("130.161.0.0/16") | |
15 | newServer("8.8.8.8") | |
16 | newServer("208.67.222.222") | |
17 | newServer("2620:0:ccc::2") | |
18 | newServer("2620:0:0ccd::2") | |
19 | ||
20 | These two equivalent configurations give you sane load balancing using a very sensible distribution policy. | |
21 | Many users will simply be done with this configuration. | |
22 | It works as well for authoritative as for recursive servers. | |
23 | ||
98650fde RG |
24 | .. _Healthcheck: |
25 | ||
20d81666 PL |
26 | Healthcheck |
27 | ----------- | |
20d81666 | 28 | |
e3ab12d9 | 29 | dnsdist uses health-check queries, sent once every second, to determine the availability of a backend server. Since 1.8.0, it also supports a ``lazy`` health-checking mode which only sends active health-check queries after a configurable threshold of regular queries have failed, see below. |
20d81666 | 30 | |
e3ab12d9 | 31 | By default, an ``A`` query for the "a.root-servers.net." name is sent. A different query type, class and target can be specified by passing, respectively, the ``checkType``, ``checkClass`` and ``checkName`` parameters to :func:`newServer`. The interval between two health-check queries can be set via the ``checkInterval`` interval parameter, and the amount of time for a response to be received via the ``checkTimeout`` one. |
0fb7654e | 32 | |
98650fde RG |
33 | Since the 1.3.0 release, the ``checkFunction`` option is also supported, taking a ``Lua`` function as parameter. This function receives a DNSName, two integers and a ``DNSHeader`` object (:ref:`DNSHeader`) |
34 | representing the QName, QType and QClass of the health check query as well as the DNS header, as they are defined before the function was called. The function must return a DNSName and two integers | |
35 | representing the new QName, QType and QClass, and can directly modify the ``DNSHeader`` object. | |
36 | ||
37 | The following example sets the CD flag to true and change the QName to "powerdns.com." and the QType to AAAA while keeping the initial QClass. | |
ad9344ba | 38 | |
98650fde RG |
39 | .. code-block:: lua |
40 | ||
41 | function myHealthCheck(qname, qtype, qclass, dh) | |
42 | dh:setCD(true) | |
43 | ||
fc16b739 | 44 | return newDNSName("powerdns.com."), DNSQType.AAAA, qclass |
98650fde RG |
45 | end |
46 | ||
93ee0a27 | 47 | newServer({address="2620:0:0ccd::2", checkFunction=myHealthCheck}) |
98650fde | 48 | |
e3ab12d9 RG |
49 | The default behavior is to consider any valid response with an ``RCODE`` different from ``ServFail`` as valid. |
50 | If the ``mustResolve`` parameter of :func:`newServer` is set to ``true``, a response will only be considered valid if its ``RCODE`` differs from ``NXDomain``, ``ServFail`` and ``Refused``. | |
51 | ||
52 | The number of health check failures before a server is considered down is configurable via the ``maxCheckFailures`` parameter, defaulting to 1. In the same way, the number of consecutive successful health checks needed for a server to be considered available can be set via the ``rise`` parameter, defaulting to 1. | |
53 | ||
54 | The ``CD`` flag can be set on the query by setting ``setCD`` to true. | |
55 | e.g.:: | |
56 | ||
74c4cff1 | 57 | newServer({address="192.0.2.1", checkType="AAAA", checkClass=DNSClass.CHAOS, checkName="a.root-servers.net.", mustResolve=true}) |
e3ab12d9 RG |
58 | |
59 | You can turn on logging of health check errors using the :func:`setVerboseHealthChecks` function. | |
60 | ||
61 | Lazy health-checking | |
62 | ~~~~~~~~~~~~~~~~~~~~ | |
63 | ||
64 | In some setups, especially on low-end devices, it might not make sense to actively send queries to the backend at a regular interval. Using the feedback from the results of regular queries can instead be used to infer if a backend might not be working properly. | |
65 | ||
d390a979 | 66 | Since 1.8.0, dnsdist implements a ``lazy`` mode that can be set via the ``healthCheckMode`` option on :func:`newServer`. In this mode, dnsdist will only send active health-check queries after seeing a configurable amount of regular queries failing. It will then place the backend in a ``PotentialFailure`` state, from the initial ``Healthy`` one, and send health-check queries every ``checkInterval`` seconds. If ``maxCheckFailures`` of these fail, the backend is then moved to a ``Failed`` state and marked as ``down``, and active health-check queries are sent every ``lazyHealthCheckFailedInterval`` seconds. After ``rise`` successful, consecutive queries, the backend will be moved back to the ``Healthy`` state and marked as ``up`` again, and health-check queries will stop. |
e3ab12d9 RG |
67 | |
68 | .. figure:: ../imgs/DNSDistLazyHealthChecks.png | |
69 | :align: center | |
70 | :alt: DNSDist Lazy health checks | |
71 | ||
72 | The threshold of failed regular queries is configured via ``lazyHealthCheckThreshold``, indicating of percentage of regular queries that should have resulted in a failure over the last recent queries. Only the results of the last ``lazyHealthCheckSampleSize`` queries will be considered, as the results are kept in a in-memory circular buffer. The results of at least ``lazyHealthCheckMinSampleCount`` queries should be present for the threshold to be considered meaningful, to avoid an issue with a too small sample. | |
73 | ||
74 | By default both queries that resulted in a timeout and those that received a ``ServFail`` answer are considered failures, but it is possible to set ``lazyHealthCheckMode`` to ``TimeoutOnly`` so that only timeouts are considered failures. | |
75 | ||
76 | So for example, if we set ``healthCheckMode`` to ``lazy``, ``lazyHealthCheckSampleSize`` to 100, ``lazyHealthCheckMinSampleCount`` to 10, ``lazyHealthCheckThreshold`` to 30, ``maxCheckFailures`` to 2 and ``rise`` to 2: | |
77 | ||
78 | - nothing will happen until at least 10 queries have been received | |
79 | - only the results of the last 100 queries will be considered | |
80 | - if at least 30 of these last 100 have failed, the threshold will be reached and active health-check queries will be sent every ``checkInterval`` seconds | |
81 | - if the health-check query is successful, the backend will stay ``up`` and no more query will be sent | |
82 | - but if instead two consecutive queries fail, the backend will be marked as ``down`` and health-check queries will be sent every ``lazyHealthCheckFailedInterval`` seconds | |
412643ce RG |
83 | - it will take two consecutive, successful health-checks for the backend to go back to ``Healthy`` and be marked `up` again |
84 | ||
85 | .. code-block:: lua | |
86 | ||
87 | newServer({address="192.0.2.1", healthCheckMode='lazy', checkInterval=1, lazyHealthCheckFailedInterval=30, rise=2, maxCheckFailures=3, lazyHealthCheckThreshold=30, lazyHealthCheckSampleSize=100, lazyHealthCheckMinSampleCount=10, lazyHealthCheckMode='TimeoutOnly'}) | |
e3ab12d9 | 88 | |
93b395a5 RG |
89 | The 'lazy' mode also supports using an exponential back-off time between health-check queries, once a backend has been moved to the 'down' state. This can be enabled by setting the ``lazyHealthCheckUseExponentialBackOff`` parameter to 'true'. Once the backend has been marked as 'down', the first query will be sent after ``lazyHealthCheckFailedInterval`` seconds, the second one after 2 times ``lazyHealthCheckFailedInterval`` seconds, the third after 4 times ``lazyHealthCheckFailedInterval`` seconds, and so on and so forth, until ``lazyHealthCheckMaxBackOff`` has been reached. Then probes will be sent every ``lazyHealthCheckMaxBackOff`` seconds (default is 3600 so one hour) until the backend comes 'up' again. |
90 | ||
44f6dbd1 RG |
91 | Source address selection |
92 | ------------------------ | |
93 | ||
94 | In multi-homed setups, it can be useful to be able to select the source address or the outgoing | |
95 | interface used by dnsdist to contact a downstream server. This can be done by using the `source` parameter:: | |
96 | ||
97 | newServer({address="192.0.2.1", source="192.0.2.127"}) | |
98 | newServer({address="192.0.2.1", source="eth1"}) | |
99 | newServer({address="192.0.2.1", source="192.0.2.127@eth1"}) | |
100 | ||
101 | The supported values for source are: | |
fc9a4408 | 102 | |
44f6dbd1 RG |
103 | - an IPv4 or IPv6 address, which must exist on the system |
104 | - an interface name | |
105 | - an IPv4 or IPv6 address followed by '@' then an interface name | |
106 | ||
107 | Please note that specifying the interface name is only supported on system having `IP_PKTINFO`. | |
2f6dc9ee RG |
108 | |
109 | Securing the channel | |
110 | -------------------- | |
111 | ||
46297422 RG |
112 | Securing the path to the backend |
113 | -------------------------------- | |
114 | ||
115 | As explained briefly in the quickstart guide, dnsdist has always been designed as a load-balancer placed in | |
116 | front of authoritative or recursive servers, assuming that the network path between dnsdist and these servers | |
117 | is trusted. This is particularly important because for performance reasons it uses a single connected socket | |
118 | for UDP exchanges by default, and easy to predict DNS query IDs, which makes it easy for an attacker to poison | |
119 | responses. | |
120 | ||
121 | If dnsdist is instead intended to be deployed in such a way that the path to its backend is not secure, the | |
122 | UDP protocol should not be used, and 'TCP-only', DNS over TLS and DNS over HTTPS protocols used instead, as | |
123 | supported since 1.7.0. | |
124 | ||
125 | Using these protocols leads to all queries, regardless of whether they were initially received by dnsdist over | |
126 | UDP, TCP, DoT or DoH, being forwarded over a TCP socket, a secure DNS over TLS channel or a secure DNS over HTTPS | |
127 | channel. | |
128 | ||
129 | The TCP-only mode for a backend can be enabled by using the ``tcpOnly`` parameter of the :func:`newServer` command. | |
130 | ||
6a1e987d | 131 | The DNS over TLS mode via the ``tls`` parameter of the :func:`newServer` command. Additional parameters control the |
46297422 RG |
132 | validation of the certificate presented by the backend (``caStore``, ``validateCertificates``), the actual TLS ciphers |
133 | used (``ciphers``, ``ciphersTLS13``) and the SNI value sent (``subjectName``). | |
134 | ||
135 | The DNS over HTTPS mode in the same way than DNS over TLS but with the additional ``dohPath`` keyword indicating that | |
136 | DNS over HTTPS should be used instead of DNS over TLS. | |
137 | ||
138 | If it is absolutely necessary to support UDP exchanges over an untrusted network, a few options have been introduced in | |
139 | 1.8.0 to make spoofing attempts harder: | |
140 | ||
70f9051e | 141 | - :func:`setRandomizedIdsOverUDP` will randomize the IDs in outgoing queries, at a small performance cost. :func:`setMaxUDPOutstanding` |
dc81f76b | 142 | should be set at its highest possible value (default since 1.4.0) to make that setting fully efficient. |
489bab55 | 143 | |
46297422 | 144 | - :func:`setRandomizedOutgoingSockets` can be used to randomize the outgoing socket used when forwarding a query to a backend. |
dc81f76b RG |
145 | This requires configuring the backend to use more than one outgoing socket via the ``sockets`` parameter of :func:`newServer` |
146 | to be of any use. |