-
Ondřej Surý authoredOndřej Surý authored
Query policies
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 for example, or gray-list resolution of misbehaving zones.
There are several policies implemented:
-
pattern
- applies action if QNAME matches regular expression -
suffix
- applies action if QNAME suffix matches given list of suffixes (useful for "is domain in zone" rules), uses Aho-Corasick string matching algorithm implemented by @jgrahamc (CloudFlare, Inc.) (BSD 3-clause) -
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. - custom filter function
There are several defined actions:
-
PASS
- let the query pass through -
DENY
- return NXDOMAIN answer -
DROP
- terminate query resolution, returns SERVFAIL to requestor -
TC
- set TC=1 if the request came through UDP, forcing client to retry with TCP -
FORWARD(ip)
- forward query to given IP and proxy back response (stub mode); it can be a single IP (string) or a list of up to four IPs. -
MIRROR(ip)
- mirror query to given IP and continue solving it (useful for partial snooping) -
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)
Warning
The policy module only looks at the inbound DNS queries. Thus the FORWARD(ip)
policy does only forward inbound query to the specified IP address(es) and it doesn't and it can't do DNSSEC validation. If you need DNSSEC validation, you either need to disable FORWARD(ip)
policy or use an upstream DNSSEC-validating resolver.
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
-- Load default policies
modules = { 'policy' }
-- Whitelist 'www[0-9].badboy.cz'
policy.add(policy.pattern(policy.PASS, '\4www[0-9]\6badboy\2cz'))
-- Block all names below badboy.cz
policy.add(policy.suffix(policy.DENY, {todname('badboy.cz.')}))
-- Custom rule
policy.add(function (req, query)
if query:qname():find('%d.%d.%d.224\7in-addr\4arpa') then
return policy.DENY
end
end)
-- Disallow ANY queries
policy.add(function (req, query)
if query.stype == kres.type.ANY then
return policy.DROP
end
end)
-- Enforce local RPZ
policy.add(policy.rpz(policy.DENY, 'blacklist.rpz'))
-- Forward all queries below 'company.se' to given resolver
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')))
-- 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)