]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
modules/policy: Take multiple RR types from rpz file for a single domain name
authorLukáš Ježek <lukas.jezek@nic.cz>
Wed, 1 Apr 2020 10:49:45 +0000 (12:49 +0200)
committerPetr Špaček <petr.spacek@nic.cz>
Tue, 14 Apr 2020 14:55:56 +0000 (16:55 +0200)
NEWS
modules/policy/README.rst
modules/policy/policy.lua

diff --git a/NEWS b/NEWS
index 93e996a5701d52187acd9d1d7027c61d1ea32c62..355efbd950211168bd11ba71f76ad1a06d29ebc0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ Improvements
 - cache garbage collector: reduce filesystem operations when idle (!946)
 - policy.DEBUG_ALWAYS and policy.DEBUG_IF for limited verbose logging (!957)
 - daemon: improve TCP query latency under heavy TCP load (!968)
+- add policy.ANSWER action (!964, #192)
+- policy.rpz support fake A/AAAA (!964, #194)
 
 Bugfixes
 --------
index a11aa7db6bed2332edd06878bf345bcd500a6775..db9ed4dcb55995f56690e497ae76ffcc6d759a78 100644 (file)
@@ -146,6 +146,15 @@ Following actions stop the policy matching on the query, i.e. other rules are no
        -- (the "true" at the end of policy.add)
        policy.add(policy.REROUTE({'192.0.2.0/24', '127.0.0.0'}), true)
 
+.. function:: ANSWER({ type = { ttl=ttl, rdata=data} }, nodata)
+
+   Overwrite rr data in response. ``rdata`` takes just IP address. If `nodata` is `true` policy return `NODATA` when requested type from client isn't specified (default: ``nodata=false``).
+
+   .. code-block:: lua
+
+       -- this policy change IPv4 adress and TTL for `exmaple.com`
+       policy.add(policy.suffix(policy.ANSWER({ [kres.type.A] = { ttl=300, rdata='\192\0\2\7' } }), { todname('example.com') }))
+
 More complex non-chain actions are described in their own chapters, namely:
 
   * :ref:`forwarding`
@@ -541,7 +550,8 @@ Response policy zones
    "``rpz-passthru.``", ":func:`policy.PASS`", "yes"
    "``rpz-tcp-only.``", ":func:`policy.TC`", "yes"
    "``rpz-drop.``", ":func:`policy.DROP`", "no [#]_"
-   "fake A/AAAA", "*not supported*", "no"
+   "fake A/AAAA", ":func:`policy.ANSWER`", "yes"
+   "fake CNAME", "not supported", "no"
 
   .. [#] RPZ action ``*.`` in BIND causes *NODATA* answer
      but typically our users configure ``policy.rpz(policy.DENY, ...)``
index d20891374606f975920f6d0ee6781a77bf31613e..a75d5fdf149569cb85aa4cf622e012392ac6aa23 100644 (file)
@@ -210,16 +210,26 @@ function policy.FLAGS(opts_set, opts_clear)
 end
 
 -- Create answer with passed arguments
-function policy.ANSWER(rtype, rdata, ttl)
+function policy.ANSWER(rtable, nodata)
        return function(_, req)
                local qry = req:current()
                local answer = req.answer
+               local data = rtable[qry.stype]
+
                ffi.C.kr_pkt_make_auth_header(answer)
 
-               if (rtype == qry.stype) then
+               if data == nil then
+                       if nodata == true then
+                               answer:rcode(kres.rcode.NOERROR)
+                               return kres.DONE
+                       end
+               else
+                       local ttl = data.ttl or 1
+
                        answer:rcode(kres.rcode.NOERROR)
                        answer:begin(kres.section.ANSWER)
-                       answer:put(qry.sname, ttl, qry.sclass, rtype, rdata)
+                       answer:put(qry.sname, ttl, qry.sclass, qry.stype, data.rdata)
+
                        return kres.DONE
                end
        end
@@ -367,6 +377,7 @@ end
 
 local function rpz_parse(action, path)
        local rules = {}
+       local new_actions = {}
        local action_map = {
                -- RPZ Policy Actions
                ['\0'] = action,
@@ -376,6 +387,22 @@ local function rpz_parse(action, path)
                ['\012rpz-tcp-only\0'] = policy.TC,
                -- Policy triggers @NYI@
        }
+       local unsupp_rrs = function (rtype)
+               local set = {
+                       kres.type.DNAME,
+                       kres.type.NS,
+                       kres.type.SOA,
+                       kres.type.DNSKEY,
+                       kres.type.DS,
+                       kres.type.RRSIG,
+                       kres.type.NSEC,
+                       kres.type.NSEC3,
+               }
+               for _, l in pairs(set) do
+                       if rtype == l then return true end
+               end
+               return false
+       end
        local parser = require('zonefile').new()
        local ok, errstr = parser:open(path)
        if not ok then
@@ -393,10 +420,22 @@ local function rpz_parse(action, path)
                rules[name] = action_map[name_action]
                -- Warn when NYI
                if #name > 1 and not action_map[name_action] then
-                       rules[name] = policy.ANSWER(parser.r_type, name_action, parser.r_ttl)
+
+                       if parser.r_type == kres.type.CNAME then
+                               log('[poli] RPZ %s:%d: CNAME in RPZ is not supported', path, tonumber(parser.line_counter))
+                       elseif unsupp_rrs(parser.r_type) then
+                               log('[poli] RPZ %s:%d: RR type %s is not allowed in RPZ', path, tonumber(parser.line_counter),
+                                   kres.tostring.type[parser.r_type])
+                       else
+                               if new_actions[name] == nil then new_actions[name] = {} end
+                               new_actions[name][parser.r_type] = { ttl=parser.r_ttl, rdata=name_action }
+                       end
                end
        end
        collectgarbage()
+       for k, v in pairs(new_actions) do
+               rules[k] = policy.ANSWER(v, true)
+       end
        return rules
 end