README.rst 18.5 KB
Newer Older
1

2 3 4 5
************************
Knot DNS Resolver daemon 
************************

6
The server is in the `daemon` directory, it works out of the box without any configuration.
7

8
.. code-block:: bash
9

10 11
   $ kresd -h # Get help
   $ kresd -a ::1
12

13 14 15 16 17 18 19 20 21
Enabling DNSSEC
===============

The resolver supports DNSSEC including :rfc:`5011` automated DNSSEC TA updates and :rfc:`7646` negative trust anchors.
To enable it, you need to provide at least _one_ trust anchor. This step is not automatic, as you're supposed to obtain
the trust anchor `using a secure channel <http://jpmens.net/2015/01/21/opendnssec-rfc-5011-bind-and-unbound/>`_.
From there, the Knot DNS Resolver can perform automatic updates for you.

1. Check the current TA published on `IANA website <https://data.iana.org/root-anchors/root-anchors.xml>`_
Marek Vavruša's avatar
Marek Vavruša committed
22
2. Fetch current keys (DNSKEY), verify digests
23
3. Deploy them
24 25 26

.. code-block:: bash

27
   $ kdig DNSKEY . @a.root-servers.net +noall +answer | grep 257 > root.keys
Marek Vavruša's avatar
Marek Vavruša committed
28
   $ ldns-key2ds -n root.keys # Only print to stdout
29 30 31 32
   ... verify that digest matches TA published by IANA ...
   $ kresd -k root.keys

You've just enabled DNSSEC!
33

34 35
CLI interface
=============
36

37
The daemon features a CLI interface, type ``help`` to see the list of available commands.
38 39 40

.. code-block:: bash

41 42 43 44
   $ kresd /var/run/knot-resolver
   [system] started in interactive mode, type 'help()'
   > cache.count()
   53
45 46 47 48

.. role:: lua(code)
   :language: lua

49 50 51 52 53 54 55 56 57 58 59 60
Verbose output
--------------

If the debug logging is compiled in, you can turn on verbose tracing of server operation with the ``-v`` option.
You can also toggle it on runtime with ``verbose(true|false)`` command.

.. code-block:: bash

   $ kresd -v

Scaling out
===========
Marek Vavruša's avatar
Marek Vavruša committed
61 62

The server can clone itself into multiple processes upon startup, this enables you to scale it on multiple cores.
63 64
Multiple processes can serve different addresses, but still share the same working directory and cache.
You can add start and stop processes on runtime based on the load.
Marek Vavruša's avatar
Marek Vavruša committed
65 66 67

.. code-block:: bash

Marek Vavruša's avatar
Marek Vavruša committed
68
   $ kresd -f 4 rundir > kresd.log &
69 70 71 72 73 74 75 76 77 78 79 80
   $ kresd -f 2 rundir > kresd_2.log & # Extra instances
   $ pstree $$ -g
   bash(3533)─┬─kresd(19212)─┬─kresd(19212)
              │              ├─kresd(19212)
              │              └─kresd(19212)
              ├─kresd(19399)───kresd(19399)
              └─pstree(19411)
   $ kill 19399 # Kill group 2, former will continue to run
   bash(3533)─┬─kresd(19212)─┬─kresd(19212)
              │              ├─kresd(19212)
              │              └─kresd(19212)
              └─pstree(19460)  
Marek Vavruša's avatar
Marek Vavruša committed
81 82 83

.. note:: On recent Linux supporting ``SO_REUSEPORT`` (since 3.9, backported to RHEL 2.6.32) it is also able to bind to the same endpoint and distribute the load between the forked processes. If the kernel doesn't support it, you can still fork multiple processes on different ports, and do load balancing externally (on firewall or with `dnsdist <http://dnsdist.org/>`_).

84
Notice the absence of an interactive CLI. You can attach to the the consoles for each process, they are in ``rundir/tty/PID``.
Marek Vavruša's avatar
Marek Vavruša committed
85 86 87 88 89 90 91

.. code-block:: bash

	$ nc -U rundir/tty/3008 # or socat - UNIX-CONNECT:rundir/tty/3008
	> cache.count()
	53

92 93 94
The *direct output* of the CLI command is captured and sent over the socket, while also printed to the daemon standard outputs (for accountability). This gives you an immediate response on the outcome of your command.
Error or debug logs aren't captured, but you can find them in the daemon standard outputs.

Marek Vavruša's avatar
Marek Vavruša committed
95 96 97 98 99
This is also a way to enumerate and test running instances, the list of files int ``tty`` correspond to list
of running processes, and you can test the process for liveliness by connecting to the UNIX socket.

.. warning:: This is very basic way to orchestrate multi-core deployments and doesn't scale in multi-node clusters. Keep an eye on the prepared ``hive`` module that is going to automate everything from service discovery to deployment and consistent configuration.

100 101 102 103 104 105 106 107 108 109 110 111 112
Configuration
=============

.. contents::
   :depth: 2
   :local:

In it's simplest form it requires just a working directory in which it can set up persistent files like
cache and the process state. If you don't provide the working directory by parameter, it is going to make itself
comfortable in the current working directory.

.. code-block:: sh

113
	$ kresd /var/run/kresd
114 115 116

And you're good to go for most use cases! If you want to use modules or configure daemon behavior, read on.

117 118
There are several choices on how you can configure the daemon, a RPC interface, a CLI, and a configuration file.
Fortunately all share common syntax and are transparent to each other.
119 120 121 122 123 124

Configuration example
---------------------
.. code-block:: lua

	-- 10MB cache
Marek Vavruša's avatar
Marek Vavruša committed
125 126
	cache.size = 10*MB
	-- load some modules
127
	modules = { 'policy', 'cachectl' }
128
	-- interfaces
129
	net = { '127.0.0.1', '::1' }
130 131 132 133 134 135 136 137 138 139 140 141 142 143

Configuration syntax
--------------------

The configuration is kept in the ``config`` file in the daemon working directory, and it's going to get loaded automatically.
If there isn't one, the daemon is going to start with sane defaults, listening on `localhost`.
The syntax for options is like follows: ``group.option = value`` or ``group.action(parameters)``.
You can also comment using a ``--`` prefix.

A simple example would be to load static hints.

.. code-block:: lua

	modules = {
Marek Vavruša's avatar
Marek Vavruša committed
144
		'hints' -- no configuration
145 146
	}

Marek Vavruša's avatar
Marek Vavruša committed
147
If the module accepts accepts configuration, you can call the ``module.config({...})`` or provide options table.
148 149 150 151 152 153 154
The syntax for table is ``{ key1 = value, key2 = value }``, and it represents the unpacked `JSON-encoded`_ string, that
the modules use as the :ref:`input configuration <mod-properties>`.

.. code-block:: lua

	modules = {
		cachectl = true,
155
		hints = '/etc/hosts'
156 157 158 159 160 161 162 163 164 165 166 167
	}

.. tip:: The configuration and CLI syntax is Lua language, with which you may already be familiar with.
         If not, you can read the `Learn Lua in 15 minutes`_ for a syntax overview. Spending just a few minutes
         will allow you to break from static configuration, write more efficient configuration with iteration, and
         leverage events and hooks. Lua is heavily used for scripting in applications ranging from embedded to game engines,
         but in DNS world notably in `PowerDNS Recursor`_. Knot DNS Resolver does not simply use Lua modules, but it is
         the heart of the daemon for everything from configuration, internal events and user interaction.

Dynamic configuration
^^^^^^^^^^^^^^^^^^^^^

168 169 170
Knowing that the the configuration is a Lua in disguise enables you to write dynamic rules, and also avoid
repetition and templating. This is unavoidable with static configuration, e.g. when you want to configure
each node a little bit differently.
171 172 173 174

.. code-block:: lua

	if hostname() == 'hidden' then
175
		net.listen(net.eth0, 5353)
176
	else
177
		net = { '127.0.0.1', net.eth1.addr[1] }
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
	end

Another example would show how it is possible to bind to all interfaces, using iteration.

.. code-block:: lua

	for name, addr_list in pairs(net.interfaces()) do
		net.listen(addr_list)
	end

You can also use third-party packages (available for example through LuaRocks_) as on this example
to download cache from parent, to avoid cold-cache start.

.. code-block:: lua

	local http = require('socket.http')
	local ltn12 = require('ltn12')

	if cache.count() == 0 then
		-- download cache from parent
		http.request { 
			url = 'http://parent/cache.mdb',
			sink = ltn12.sink.file(io.open('cache.mdb', 'w'))
		}
		-- reopen cache with 100M limit
203
		cache.size = 100*MB
204 205 206 207 208
	end

Events and services
^^^^^^^^^^^^^^^^^^^

209 210 211
The Lua supports a concept called closures_, this is extremely useful for scripting actions upon various events,
say for example - prune the cache within minute after loading, publish statistics each 5 minutes and so on.
Here's an example of an anonymous function with :func:`event.recurrent()`:
212

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
.. code-block:: lua

	-- every 5 minutes
	event.recurrent(5 * minute, function()
		cachectl.prune()
	end)

Note that each scheduled event is identified by a number valid for the duration of the event,
you may cancel it at any time. You can do this with anonymous functions, if you accept the event
as a parameter, but it's not very useful as you don't have any *non-global* way to keep persistent variables.

.. code-block:: lua

	-- make a closure, encapsulating counter
	function pruner()
		local i = 0
		-- pruning function
		return function(e)
			cachectl.prune()
			-- cancel event on 5th attempt
			i = i + 1
			if i == 5 then
				event.cancel(e)
			fi
		end
	end

	-- make recurrent event that will cancel after 5 times
	event.recurrent(5 * minute, pruner())
242 243 244 245

* File watchers
* Data I/O

246 247 248
.. note:: Work in progress, come back later!

.. _closures: http://www.lua.org/pil/6.1.html
249 250 251 252 253 254

Configuration reference
-----------------------

This is a reference for variables and functions available to both configuration file and CLI.

255 256 257 258
.. contents::
   :depth: 1
   :local:

259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
Environment
^^^^^^^^^^^

.. envvar:: env (table)

   Return environment variable.

   .. code-block:: lua

	env.USER -- equivalent to $USER in shell

.. function:: hostname()

   :return: Machine hostname.

274 275 276 277
.. function:: verbose(true | false)

   :return: Toggle verbose logging.

278 279 280
Network configuration
^^^^^^^^^^^^^^^^^^^^^

281 282 283 284 285 286 287 288
For when listening on ``localhost`` just doesn't cut it.

.. tip:: Use declarative interface for network.

         .. code-block:: lua

         	net = { '127.0.0.1', net.eth0, net.eth1.addr[1] }

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
.. function:: net.listen(address, [port = 53])

   :return: boolean

   Listen on address, port is optional.

.. function:: net.listen({address1, ...}, [port = 53])

   :return: boolean

   Listen on list of addresses.

.. function:: net.listen(interface, [port = 53])

   :return: boolean

   Listen on all addresses belonging to an interface.

   Example:

   .. code-block:: lua

	net.listen(net.eth0) -- listen on eth0

.. function:: net.close(address, [port = 53])

   :return: boolean

   Close opened address/port pair, noop if not listening.

.. function:: net.list()

   :return: Table of bound interfaces.

   Example output:

   .. code-block:: lua

	[127.0.0.1] => {
	    [port] => 53
	    [tcp] => true
	    [udp] => true
	}

.. function:: net.interfaces()

   :return: Table of available interfaces and their addresses.

   Example output:

   .. code-block:: lua

	[lo0] => {
	    [addr] => {
	        [1] => ::1
	        [2] => 127.0.0.1
	    }
	    [mac] => 00:00:00:00:00:00
	}
	[eth0] => {
	    [addr] => {
	        [1] => 192.168.0.1
	    }
	    [mac] => de:ad:be:ef:aa:bb
	}

   .. tip:: You can use ``net.<iface>`` as a shortcut for specific interface, e.g. ``net.eth0``

357 358 359 360 361 362 363 364 365 366 367 368 369
.. function:: net.bufsize([udp_bufsize])

   Get/set maximum EDNS payload available. Default is 1452 (the maximum unfragmented datagram size).
   You cannot set less than 1220 (minimum size for DNSSEC) or more than 65535 octets.

   Example output:

   .. code-block:: lua

	> net.bufsize(4096)
	> net.bufsize()
	4096

370 371 372 373 374 375 376
Trust anchors and DNSSEC
^^^^^^^^^^^^^^^^^^^^^^^^

.. function:: trust_anchors.config(keyfile)

   :param string keyfile: File containing DNSKEY records, should be writeable.

Marek Vavruša's avatar
Marek Vavruša committed
377
   You can use only DNSKEY records in managed mode. It is equivalent to CLI parameter ``-k <keyfile>`` or ``trust_anchors.file = keyfile``.
378 379 380 381 382

   Example output:

   .. code-block:: lua

Marek Vavruša's avatar
Marek Vavruša committed
383 384
      > trust_anchors.config('root.keys')
      [trust_anchors] key: 19036 state: Valid
385 386 387 388 389 390

.. function:: trust_anchors.set_insecure(nta_set)

   :param table nta_list: List of domain names (text format) representing NTAs.

   When you use a domain name as an NTA, DNSSEC validation will be turned off at/below these names.
Marek Vavruša's avatar
Marek Vavruša committed
391
   Each function call replaces the previous NTA set. You can find the current active set in ``trust_anchors.insecure`` variable.
392 393 394 395 396 397 398

   .. tip:: Use the `trust_anchors.negative = {}` alias for easier configuration.

   Example output:

   .. code-block:: lua

Marek Vavruša's avatar
Marek Vavruša committed
399 400 401 402
      > trust_anchors.negative = { 'bad.boy', 'example.com' }
      > trust_anchors.insecure
      [1] => bad.boy
      [2] => example.com
403

404 405
.. function:: trust_anchors.add(rr_string)

Marek Vavruša's avatar
Marek Vavruša committed
406
   :param string rr_string: DS/DNSKEY records in presentation format (e.g. ``. 3600 IN DS 19036 8 2 49AAC11...``)
407

Marek Vavruša's avatar
Marek Vavruša committed
408 409
   Inserts DS/DNSKEY record(s) into current keyset. These will not be managed or updated, use it only for testing
   or if you have a specific use case for not using a keyfile.
410 411 412 413 414

   Example output:

   .. code-block:: lua

Marek Vavruša's avatar
Marek Vavruša committed
415
      > trust_anchors.add('. 3600 IN DS 19036 8 2 49AAC11...')
416

417 418 419 420 421
Modules configuration
^^^^^^^^^^^^^^^^^^^^^

The daemon provides an interface for dynamic loading of :ref:`daemon modules <modules-implemented>`.

422
.. tip:: Use declarative interface for module loading.
423 424 425

         .. code-block:: lua

Marek Vavruša's avatar
Marek Vavruša committed
426
         	modules = { 'cachectl' }
427 428 429
		modules = {
			hints = {file = '/etc/hosts'}
		}
430 431 432 433 434 435

         Equals to:

         .. code-block:: lua

		modules.load('cachectl')
Marek Vavruša's avatar
Marek Vavruša committed
436 437
		modules.load('hints')
		hints.config({file = '/etc/hosts'})
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461


.. function:: modules.list()

   :return: List of loaded modules.

.. function:: modules.load(name)

   :param string name: Module name, e.g. "hints"
   :return: boolean

   Load a module by name.

.. function:: modules.unload(name)

   :param string name: Module name
   :return: boolean

   Unload a module by name.

Cache configuration
^^^^^^^^^^^^^^^^^^^

The cache in Knot DNS Resolver is persistent with LMDB backend, this means that the daemon doesn't lose
462 463
the cached data on restart or crash to avoid cold-starts. The cache may be reused between cache
daemons or manipulated from other processes, making for example synchronised load-balanced recursors possible.
464

465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
.. envvar:: cache.size (number)

   Get/set the cache maximum size in bytes. Note that this is only a hint to the backend,
   which may or may not respect it. See :func:`cache.open()`.

   .. code-block:: lua

	print(cache.size)
	cache.size = 100 * MB -- equivalent to `cache.open(100 * MB)`

.. envvar:: cache.storage (string)

   Get or change the cache storage backend configuration, see :func:`cache.backends()` for
   more information. If the new storage configuration is invalid, it is not set.

   .. code-block:: lua

	print(cache.storage)
	cache.storage = 'lmdb://.'

485 486 487 488 489 490 491 492 493 494 495 496 497 498
.. function:: cache.backends()

   :return: map of backends

   The cache supports runtime-changeable backends, using the optional :rfc:`3986` URI, where the scheme
   represents backend protocol and the rest of the URI backend-specific configuration. By default, it
   is a ``lmdb`` backend in working directory, i.e. ``lmdb://``.

   Example output:

   .. code-block:: lua

   	[lmdb://] => true

499 500 501 502 503 504 505
.. function:: cache.stats()

   :return: table of cache counters

  The cache collects counters on various operations (hits, misses, transactions, ...). This function call returns a table of
  cache counters that can be used for calculating statistics.

506
.. function:: cache.open(max_size[, config_uri])
507 508 509 510 511 512 513 514 515

   :param number max_size: Maximum cache size in bytes.
   :return: boolean

   Open cache with size limit. The cache will be reopened if already open.
   Note that the max_size cannot be lowered, only increased due to how cache is implemented.

   .. tip:: Use ``kB, MB, GB`` constants as a multiplier, e.g. ``100*MB``.

516 517 518 519 520 521 522
   The cache supports runtime-changeable backends, see :func:`cache.backends()` for mor information and
   default. Refer to specific documentation of specific backends for configuration string syntax.

   - ``lmdb://``

   As of now it only allows you to change the cache directory, e.g. ``lmdb:///tmp/cachedir``.

523 524 525 526 527 528 529 530 531 532
.. function:: cache.count()

   :return: Number of entries in the cache.

.. function:: cache.close()

   :return: boolean

   Close the cache.

533
   .. note:: This may or may not clear the cache, depending on the used backend. See :func:`cachectl.clear()`. 
534

535 536 537 538 539 540 541 542 543 544 545
.. function:: cache.stats()

   Return table of statistics, note that this tracks all operations over cache, not just which
   queries were answered from cache or not.

   Example:

   .. code-block:: lua

	print('Insertions:', cache.stats().insert)

546 547 548
Timers and events
^^^^^^^^^^^^^^^^^

549 550 551 552
The timer represents exactly the thing described in the examples - it allows you to execute closures 
after specified time, or event recurrent events. Time is always described in milliseconds,
but there are convenient variables that you can use - ``sec, minute, hour``.
For example, ``5 * hour`` represents five hours, or 5*60*60*100 milliseconds.
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

.. function:: event.after(time, function)

   :return: event id

   Execute function after the specified time has passed.
   The first parameter of the callback is the event itself.

   Example:

   .. code-block:: lua

	event.after(1 * minute, function() print('Hi!') end)

.. function:: event.recurrent(interval, function)

   :return: event id

   Similar to :func:`event.after()`, periodically execute function after ``interval`` passes. 

   Example:

   .. code-block:: lua

	msg_count = 0
	event.recurrent(5 * sec, function(e) 
		msg_count = msg_count + 1
		print('Hi #'..msg_count)
	end)

.. function:: event.cancel(event_id)

   Cancel running event, it has no effect on already canceled events.
   New events may reuse the event_id, so the behaviour is undefined if the function
   is called after another event is started.

   Example:

   .. code-block:: lua

	e = event.after(1 * minute, function() print('Hi!') end)
	event.cancel(e)

596 597 598 599 600 601 602 603 604 605
Scripting worker
^^^^^^^^^^^^^^^^

Worker is a service over event loop that tracks and schedules outstanding queries,
you can see the statistics or schedule new queries.

.. function:: worker.stats()

   Return table of statistics.

606 607
   * ``udp`` - number of outbound queries over UDP
   * ``tcp`` - number of outbound queries over TCP
608 609
   * ``ipv6`` - number of outbound queries over IPv6
   * ``ipv4`` - number of outbound queries over IPv4
610 611
   * ``concurrent`` - number of concurrent queries at the moment

612 613 614 615 616 617
   Example:

   .. code-block:: lua

	print(worker.stats().concurrent)

618
.. function:: worker.resolve(qname, qtype[, qclass = kres.class.IN, options = 0, callback = nil])
619 620

   :param string qname: Query name (e.g. 'com.')
Marek Vavruša's avatar
Marek Vavruša committed
621 622
   :param number qtype: Query type (e.g. ``kres.type.NS``)
   :param number qclass: Query class *(optional)* (e.g. ``kres.class.IN``)
623
   :param number options: Resolution options (see query flags)
624
   :param function callback: Callback to be executed when resolution completes (e.g. `function cb (pkt) end`). The callback gets a packet containing the final answer and doesn't have to return anything.
625 626
   :return: boolean

627 628 629 630 631 632 633
.. _`JSON-encoded`: http://json.org/example
.. _`Learn Lua in 15 minutes`: http://tylerneylon.com/a/learn-lua/
.. _`PowerDNS Recursor`: https://doc.powerdns.com/md/recursor/scripting/
.. _LuaRocks: https://rocks.moonscript.org/
.. _libuv: https://github.com/libuv/libuv
.. _Lua: http://www.lua.org/about.html
.. _LuaJIT: http://luajit.org/luajit.html