--------------
This module can block, rewrite, or alter inbound queries based on user-defined policies.
-By default, it blocks queries to reverse lookups in private subnets as per :rfc:`1918`, :rfc:`5735` and :rfc:`5737`.
-You can however extend it to deflect `Slow drip DNS attacks <https://blog.secure64.com/?p=377>`_ for example, or gray-list resolution of misbehaving zones.
+By default, if no rule applies to a query, rules for special-use domain names are applied, as required by :rfc:`6761`.
-There are several policies implemented:
+You can however extend it e.g. to deflect `Slow drip DNS attacks <https://secure64.com/water-torture-slow-drip-dns-ddos-attack>`_ or gray-list resolution of misbehaving zones.
-* ``pattern``
- - applies action if QNAME matches `regular expression <http://lua-users.org/wiki/PatternsTutorial>`_
-* ``suffix``
- - applies action if QNAME suffix matches given list of suffixes (useful for "is domain in zone" rules),
+There are several policy filters available in the ``policy.`` table:
+
+* ``all(action)``
+ - always applies the action
+* ``pattern(action, pattern)``
+ - applies the action if QNAME matches a `regular expression <http://lua-users.org/wiki/PatternsTutorial>`_
+* ``suffix(action, table)``
+ - applies the action if QNAME suffix matches one of suffixes in the table (useful for "is domain in zone" rules),
uses `Aho-Corasick`_ string matching algorithm implemented by `@jgrahamc`_ (CloudFlare, Inc.) (BSD 3-clause)
+* :any:`policy.suffix_common`
* ``rpz``
- - implementes a subset of the RPZ_ format. Currently it can be used with a zonefile, a binary database support is on the way. Binary database can be updated by an external process on the fly.
+ - implements a subset of RPZ_ in zonefile format. See below for details: :any:`policy.rpz`.
* custom filter function
-There are several defined actions:
+There are several actions available in the ``policy.`` table:
-* ``PASS`` - let the query pass through
-* ``DENY`` - return NXDOMAIN answer
-* ``DROP`` - terminate query resolution, returns SERVFAIL to requestor
+* ``PASS`` - let the query pass through; it's useful to make exceptions before wider rules
+* ``DENY`` - reply NXDOMAIN authoritatively
+* ``DROP`` - terminate query resolution and return SERVFAIL to the requestor
* ``TC`` - set TC=1 if the request came through UDP, forcing client to retry with TCP
* ``FORWARD(ip)`` - solve a query via forwarding to an IP while validating and caching locally;
the parameter can be a single IP (string) or a lua list of up to four IPs.
* ``STUB(ip)`` - similar to ``FORWARD(ip)`` but *without* attempting DNSSEC validation.
Each request may be either answered from cache or simply sent to one of the IPs with proxying back the answer.
-* ``MIRROR(ip)`` - mirror query to given IP and continue solving it (useful for partial snooping)
+* ``MIRROR(ip)`` - mirror query to given IP and continue solving it (useful for partial snooping); it's a chain action
* ``REROUTE({{subnet,target}, ...})`` - reroute addresses in response matching given subnet to given target, e.g. ``{'192.0.2.0/24', '127.0.0.0'}`` will rewrite '192.0.2.55' to '127.0.0.55', see :ref:`renumber module <mod-renumber>` for more information.
-* ``QTRACE`` - pretty-print DNS response packets into the log (useful for debugging weird DNS servers).
-* ``FLAGS(set, clear)`` - set and/or clear some flags for the query. There can be multiple flags to set/clear, combined by ``bit.bor`` from ``kres.query.*`` values.
+* ``QTRACE`` - pretty-print DNS response packets into the log for the query and its sub-queries. It's useful for debugging weird DNS servers. It's a chain action.
+* ``FLAGS(set, clear)`` - set and/or clear some flags for the query. There can be multiple flags to set/clear, combined by ``bit.bor`` from ``kres.query.*`` values. It's a chain action.
+
+Most actions stop the policy matching on the query, but "chain actions" allow to keep trying to match other rules, until a non-chain action is triggered.
.. warning:: The policy module currently only looks at whole DNS requests. The rules won't be re-applied e.g. when following CNAMEs.
.. note:: The module (and ``kres``) expects domain names in wire format, not textual representation. So each label in name is prefixed with its length, e.g. "example.com" equals to ``"\7example\3com"``. You can use convenience function ``todname('example.com')`` for automatic conversion.
-Example configuration
-^^^^^^^^^^^^^^^^^^^^^
+Examples
+^^^^^^^^
.. code-block:: lua
policy.add(policy.suffix(policy.FORWARD('192.168.1.1'), {todname('company.se')}))
-- Forward all queries matching pattern
policy.add(policy.pattern(policy.FORWARD('2001:DB8::1'), '\4bad[0-9]\2cz'))
- -- Forward all queries (complete stub mode)
- policy.add(policy.all(policy.FORWARD('2001:DB8::1')))
+ -- Forward all queries (to public resolvers https://www.nic.cz/odvr)
+ policy.add(policy.all(policy.FORWARD({'2001:678:1::206', '193.29.206.206'})))
-- Print all responses with matching suffix
policy.add(policy.suffix(policy.QTRACE, {todname('rhybar.cz.')}))
-- Print all responses
policy.add(policy.all(policy.QTRACE))
- -- Mirror all queries and retrieve information
- local rule = policy.add(policy.all(policy.MIRROR('127.0.0.2')))
- -- Print information about the rule
- print(string.format('id: %d, matched queries: %d', rule.id, rule.count)
- -- Reroute all addresses found in answer from 192.0.2.0/24 to 127.0.0.x
- -- this policy is enforced on answers, therefore 'postrule'
- local rule = policy.add(policy.REROUTE({'192.0.2.0/24', '127.0.0.0'}), true)
- -- Delete rule that we just created
- policy.del(rule.id)
-
-Properties
-^^^^^^^^^^
-
-.. envvar:: policy.PASS
-
- Pass-through all queries matching the rule.
-
-.. envvar:: policy.DENY
-
- Respond with NXDOMAIN to all queries matching the rule.
-
-.. envvar:: policy.DROP
-
- Drop all queries matching the rule.
-
-.. envvar:: policy.TC
-
- Respond with empty answer with TC bit set (if the query came through UDP).
-
-.. envvar:: policy.FORWARD (address)
-
- Forward query to given IP address.
-
-.. envvar:: policy.MIRROR (address)
-
- Forward query to given IP address.
-
-.. envvar:: policy.REROUTE({{subnet,target}, ...})
-
- Reroute addresses in response matching given subnet to given target, e.g. ``{'192.0.2.0/24', '127.0.0.0'}`` will rewrite '192.0.2.55' to '127.0.0.55'.
-
-.. envvar:: policy.QTRACE
+ -- Mirror all queries and retrieve information
+ local rule = policy.add(policy.all(policy.MIRROR('127.0.0.2')))
+ -- Print information about the rule
+ print(string.format('id: %d, matched queries: %d', rule.id, rule.count)
+ -- Reroute all addresses found in answer from 192.0.2.0/24 to 127.0.0.x
+ -- this policy is enforced on answers, therefore 'postrule'
+ local rule = policy.add(policy.REROUTE({'192.0.2.0/24', '127.0.0.0'}), true)
+ -- Delete rule that we just created
+ policy.del(rule.id)
+
+Additional properties
+^^^^^^^^^^^^^^^^^^^^^
- Print pretty-formate (dig-like) DNS answers for current query and
- all its subqueries that Knot Resolver receive from upstream
- (authoritative) DNS servers. Very useful when dealing with
- non-compliant DNS servers that violate DNS protocol.
+Most properties (actions, filters) are described above.
.. function:: policy.add(rule, postrule)
:param rule: added rule, i.e. ``policy.pattern(policy.DENY, '[0-9]+\2cz')``
:param postrule: boolean, if true the rule will be evaluated on answer instead of query
:return: rule description
-
+
Add a new policy rule that is executed either or queries or answers, depending on the ``postrule`` parameter. You can then use the returned rule description to get information and unique identifier for the rule, as well as match count.
.. function:: policy.del(id)
:param id: identifier of a given rule
:return: boolean
-
- Remove a rule from policy list.
-.. function:: policy.all(action)
-
- :param action: executed action for all queries
-
- Perform action for all queries (no filtering).
-
-.. function:: policy.pattern(action, pattern)
-
- :param action: action if the pattern matches QNAME
- :param pattern: regular expression
-
- Policy to block queries based on the QNAME regex matching.
-
-.. function:: policy.suffix(action, suffix_table)
-
- :param action: action if the pattern matches QNAME
- :param suffix_table: table of valid suffixes
-
- Policy to block queries based on the QNAME suffix match.
+ Remove a rule from policy list.
.. function:: policy.suffix_common(action, suffix_table[, common_suffix])
:param action: action if the pattern matches QNAME
:param suffix_table: table of valid suffixes
:param common_suffix: common suffix of entries in suffix_table
-
+
Like suffix match, but you can also provide a common suffix of all matches for faster processing (nil otherwise).
This function is faster for small suffix tables (in the order of "hundreds").
:param action: the default action for match in the zone (e.g. RH-value `.`)
:param path: path to zone file | database
-
+
Enforce RPZ_ rules. This can be used in conjunction with published blocklist feeds.
The RPZ_ operation is well described in this `Jan-Piet Mens's post`_,
or the `Pro DNS and BIND`_ book. Here's compatibility table:
.. function:: policy.todnames({name, ...})
:param: names table of domain names in textual format
-
+
Returns table of domain names in wire format converted from strings.
.. code-block:: lua