From: Remi Gacogne Date: Wed, 18 Nov 2020 10:10:39 +0000 (+0100) Subject: dnsdist: Add an optional name to rules X-Git-Tag: auth-4.5.0-alpha0~1^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F9746%2Fhead;p=thirdparty%2Fpdns.git dnsdist: Add an optional name to rules --- diff --git a/pdns/dnsdist-console.cc b/pdns/dnsdist-console.cc index e3373f54bd..b1b589d507 100644 --- a/pdns/dnsdist-console.cc +++ b/pdns/dnsdist-console.cc @@ -343,7 +343,7 @@ void doConsole() const std::vector g_consoleKeywords{ /* keyword, function, parameters, description */ { "addACL", true, "netmask", "add to the ACL set who can use this server" }, - { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "add a rule" }, + { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "add a rule" }, { "addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter" }, { "addConsoleACL", true, "netmask", "add a netmask to the console ACL" }, { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" }, @@ -351,9 +351,9 @@ const std::vector g_consoleKeywords{ { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, { "addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" }, { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" }, - { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" }, - { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" }, - { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a self-answered response rule" }, + { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a cache hit response rule" }, + { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a response rule" }, + { "addSelfAnsweredResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\", name=\"name\"}}]", "add a self-answered response rule" }, { "addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table" }, { "AllowAction", true, "", "let these packets go through" }, { "AllowResponseAction", true, "", "let these packets go through" }, @@ -471,7 +471,7 @@ const std::vector g_consoleKeywords{ { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" }, { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" }, { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" }, - { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, + { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\", name=\"name\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" }, { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1, reconnectOnUp=false}", "instantiate a server" }, { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" }, { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" }, @@ -503,10 +503,10 @@ const std::vector g_consoleKeywords{ { "RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes. `serverID` is the server identifier." }, { "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records. `serverID` is the server identifier." }, { "rmACL", true, "netmask", "remove netmask from ACL" }, - { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, - { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, - { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, - { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string" }, + { "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, + { "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, + { "rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, + { "rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" }, { "rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string" }, { "roundrobin", false, "", "Simple round robin over available servers" }, { "sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"}, diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index aef1511f46..9ea2eedefd 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -1456,13 +1456,14 @@ template static void addAction(GlobalStateHolder > *someRulActions, const luadnsrule_t& var, const std::shared_ptr& action, boost::optional& params) { setLuaSideEffect(); + std::string name; boost::uuids::uuid uuid; uint64_t creationOrder; - parseRuleParams(params, uuid, creationOrder); + parseRuleParams(params, uuid, name, creationOrder); - auto rule=makeRule(var); - someRulActions->modify([&rule, &action, &uuid, creationOrder](vector& rulactions){ - rulactions.push_back({std::move(rule), std::move(action), std::move(uuid), creationOrder}); + auto rule = makeRule(var); + someRulActions->modify([&rule, &action, &uuid, creationOrder, &name](vector& rulactions){ + rulactions.push_back({std::move(rule), std::move(action), std::move(name), std::move(uuid), creationOrder}); }); } @@ -1510,10 +1511,11 @@ void setupLuaActions(LuaContext& luaCtx) luaCtx.writeFunction("newRuleAction", [](luadnsrule_t dnsrule, std::shared_ptr action, boost::optional params) { boost::uuids::uuid uuid; uint64_t creationOrder; - parseRuleParams(params, uuid, creationOrder); + std::string name; + parseRuleParams(params, uuid, name, creationOrder); - auto rule=makeRule(dnsrule); - DNSDistRuleAction ra({std::move(rule), action, uuid, creationOrder}); + auto rule = makeRule(dnsrule); + DNSDistRuleAction ra({std::move(rule), action, std::move(name), uuid, creationOrder}); return std::make_shared(ra); }); diff --git a/pdns/dnsdist-lua-rules.cc b/pdns/dnsdist-lua-rules.cc index a3124d758d..e5af721c6d 100644 --- a/pdns/dnsdist-lua-rules.cc +++ b/pdns/dnsdist-lua-rules.cc @@ -67,7 +67,7 @@ static boost::uuids::uuid makeRuleID(std::string& id) return getUniqueID(id); } -void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid, uint64_t& creationOrder) +void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid, std::string& name, uint64_t& creationOrder) { static uint64_t s_creationOrder = 0; @@ -77,6 +77,9 @@ void parseRuleParams(boost::optional params, boost::uuids::uuid if (params->count("uuid")) { uuidStr = boost::get((*params)["uuid"]); } + if (params->count("name")) { + name = boost::get((*params)["name"]); + } } uuid = makeRuleID(uuidStr); @@ -103,20 +106,20 @@ static std::string rulesToString(const std::vector& rules, boost::optionaltoString().substr(0, truncateRuleWidth); - result += (fmt % num % boost::uuids::to_string(lim.d_id) % lim.d_creationOrder % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + string desc = lim.d_rule->toString().substr(0, truncateRuleWidth); + result += (fmt % num % lim.d_name % boost::uuids::to_string(lim.d_id) % lim.d_creationOrder % lim.d_rule->d_matches % desc % lim.d_action->toString()).str(); ++num; } } else { - boost::format fmt("%-3d %9d %-56s %s\n"); - result += (fmt % "#" % "Matches" % "Rule" % "Action").str(); + boost::format fmt("%-3d %-30s %9d %-56s %s\n"); + result += (fmt % "#" % "Name" % "Matches" % "Rule" % "Action").str(); for(const auto& lim : rules) { - string name = lim.d_rule->toString().substr(0, truncateRuleWidth); - result += (fmt % num % lim.d_rule->d_matches % name % lim.d_action->toString()).str(); + string desc = lim.d_rule->toString().substr(0, truncateRuleWidth); + result += (fmt % num % lim.d_name % lim.d_rule->d_matches % desc % lim.d_action->toString()).str(); ++num; } } @@ -136,13 +139,25 @@ static void rmRule(GlobalStateHolder > *someRulActions, boost::variant setLuaSideEffect(); auto rules = someRulActions->getCopy(); if (auto str = boost::get(&id)) { - const auto uuid = getUniqueID(*str); - if (rules.erase(std::remove_if(rules.begin(), - rules.end(), - [uuid](const T& a) { return a.d_id == uuid; }), - rules.end()) == rules.end()) { - g_outputBuffer = "Error: no rule matched\n"; - return; + try { + const auto uuid = getUniqueID(*str); + if (rules.erase(std::remove_if(rules.begin(), + rules.end(), + [uuid](const T& a) { return a.d_id == uuid; }), + rules.end()) == rules.end()) { + g_outputBuffer = "Error: no rule matched\n"; + return; + } + } + catch (const std::runtime_error& e) { + /* it was not an UUID, let's see if it was a name instead */ + if (rules.erase(std::remove_if(rules.begin(), + rules.end(), + [&str](const T& a) { return a.d_name == *str; }), + rules.end()) == rules.end()) { + g_outputBuffer = "Error: no rule matched\n"; + return; + } } } else if (auto pos = boost::get(&id)) { @@ -299,8 +314,8 @@ void setupLuaRules(LuaContext& luaCtx) for (const auto& pair : newruleactions) { const auto& newruleaction = pair.second; if (newruleaction->d_action) { - auto rule=makeRule(newruleaction->d_rule); - gruleactions.push_back({std::move(rule), newruleaction->d_action, newruleaction->d_id, newruleaction->d_creationOrder}); + auto rule = makeRule(newruleaction->d_rule); + gruleactions.push_back({std::move(rule), newruleaction->d_action, newruleaction->d_name, newruleaction->d_id, newruleaction->d_creationOrder}); } } }); diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdist-lua.hh index 97f2e1d0de..25d5e10c8e 100644 --- a/pdns/dnsdist-lua.hh +++ b/pdns/dnsdist-lua.hh @@ -90,7 +90,7 @@ private: typedef boost::variant>, std::shared_ptr, DNSName, vector > > luadnsrule_t; std::shared_ptr makeRule(const luadnsrule_t& var); typedef std::unordered_map > luaruleparams_t; -void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid, uint64_t& creationOrder); +void parseRuleParams(boost::optional params, boost::uuids::uuid& uuid, std::string& name, uint64_t& creationOrder); typedef NetmaskTree nmts_t; diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index 2497e9d76c..0816460262 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -298,6 +298,7 @@ static json11::Json::array someResponseRulesToJson(GlobalStateHolder>* {"id", num++}, {"creationOrder", (double)a.d_creationOrder}, {"uuid", boost::uuids::to_string(a.d_id)}, + {"name", a.d_name}, {"matches", (double)a.d_rule->d_matches}, {"rule", a.d_rule->toString()}, {"action", a.d_action->toString()}, @@ -307,7 +308,15 @@ static json11::Json::array someResponseRulesToJson(GlobalStateHolder>* return responseRules; } -using namespace json11; +template +static void addRulesToPrometheusOutput(std::ostringstream& output, GlobalStateHolder >& rules) +{ + auto localRules = rules.getLocal(); + for (const auto& entry : *localRules) { + std::string id = !entry.d_name.empty() ? entry.d_name : boost::uuids::to_string(entry.d_id); + output << "dnsdist_rule_hits{id=\"" << id << "\"} " << entry.d_rule->d_matches << "\n"; + } +} static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) { @@ -651,6 +660,13 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) } } + output << "# HELP dnsdist_rule_hits " << "Number of hits of that rule" << "\n"; + output << "# TYPE dnsdist_rule_hits " << "counter" << "\n"; + addRulesToPrometheusOutput(output, g_rulactions); + addRulesToPrometheusOutput(output, g_resprulactions); + addRulesToPrometheusOutput(output, g_cachehitresprulactions); + addRulesToPrometheusOutput(output, g_selfansweredresprulactions); + output << "# HELP dnsdist_info " << "Info from dnsdist, value is always 1" << "\n"; output << "# TYPE dnsdist_info " << "gauge" << "\n"; output << "dnsdist_info{version=\"" << VERSION << "\"} " << "1" << "\n"; @@ -659,6 +675,8 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) resp.headers["Content-Type"] = "text/plain"; } +using namespace json11; + static void handleJSONStats(const YaHTTP::Request& req, YaHTTP::Response& resp) { handleCORS(req, resp); @@ -923,9 +941,11 @@ static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp) } Json::array rules; + /* unfortunately DNSActions have getStats(), + and DNSResponseActions do not. */ auto localRules = g_rulactions.getLocal(); - num=0; - for(const auto& a : *localRules) { + num = 0; + for (const auto& a : *localRules) { Json::object rule{ {"id", num++}, {"creationOrder", (double)a.d_creationOrder}, diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index d9b447e6b7..2b358b18c4 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -1027,6 +1027,7 @@ struct DNSDistRuleAction { std::shared_ptr d_rule; std::shared_ptr d_action; + std::string d_name; boost::uuids::uuid d_id; uint64_t d_creationOrder; }; @@ -1035,6 +1036,7 @@ struct DNSDistResponseRuleAction { std::shared_ptr d_rule; std::shared_ptr d_action; + std::string d_name; boost::uuids::uuid d_id; uint64_t d_creationOrder; }; diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 0dfe2b63c0..05a6084e44 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -285,6 +285,9 @@ For Rules related to the incoming query: .. versionchanged:: 1.3.0 Added the optional parameter ``options``. + .. versionchanged:: 1.6.0 + Added ``name`` to the ``options``. + Add a Rule and Action to the existing rules. :param DNSrule rule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule` @@ -294,6 +297,7 @@ For Rules related to the incoming query: Options: * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. .. function:: clearRules() @@ -324,6 +328,9 @@ For Rules related to the incoming query: .. versionchanged:: 1.3.0 Added the optional parameter ``options``. + .. versionchanged:: 1.6.0 + Added ``name`` to the ``options``. + Return a pair of DNS Rule and DNS Action, to be used with :func:`setRules`. :param Rule rule: A Rule (see `Matching Packets (Selectors)`_) @@ -333,6 +340,7 @@ For Rules related to the incoming query: Options: * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. .. function:: setRules(rules) @@ -366,9 +374,12 @@ For Rules related to the incoming query: .. versionchanged:: 1.3.0 ``id`` can now be an UUID. + .. versionchanged:: 1.6.0 + ``id`` can now be a string representing the name of the rule. + Remove rule ``id``. - :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise + :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise For Rules related to responses: @@ -377,6 +388,9 @@ For Rules related to responses: .. versionchanged:: 1.3.0 Added the optional parameter ``options``. + .. versionchanged:: 1.6.0 + Added ``name`` to the ``options``. + Add a Rule and Action for responses to the existing rules. :param DNSRule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule` @@ -386,6 +400,7 @@ For Rules related to responses: Options: * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. .. function:: mvResponseRule(from, to) @@ -406,9 +421,12 @@ For Rules related to responses: .. versionchanged:: 1.3.0 ``id`` can now be an UUID. + .. versionchanged:: 1.6.0 + ``id`` can now be a string representing the name of the rule. + Remove response rule ``id``. - :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise + :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise .. function:: showResponseRules([options]) @@ -440,6 +458,9 @@ Functions for manipulating Cache Hit Response Rules: .. versionchanged:: 1.3.0 Added the optional parameter ``options``. + .. versionchanged:: 1.6.0 + Added ``name`` to the ``options``. + Add a Rule and ResponseAction for Cache Hits to the existing rules. :param DNSRule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule` @@ -449,6 +470,7 @@ Functions for manipulating Cache Hit Response Rules: Options: * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. .. function:: mvCacheHitResponseRule(from, to) @@ -473,7 +495,10 @@ Functions for manipulating Cache Hit Response Rules: .. versionchanged:: 1.3.0 ``id`` can now be an UUID. - :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise + .. versionchanged:: 1.6.0 + ``id`` can now be a string representing the name of the rule. + + :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise .. function:: showCacheHitResponseRules([options]) @@ -506,10 +531,19 @@ Functions for manipulating Self-Answered Response Rules: .. versionadded:: 1.3.0 + .. versionchanged:: 1.6.0 + Added ``name`` to the ``options``. + Add a Rule and Action for Self-Answered queries to the existing rules. :param DNSRule: A DNSRule, e.g. an :func:`AllRule` or a compounded bunch of rules using e.g. :func:`AndRule` :param action: The action to take + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. .. function:: mvSelfAnsweredResponseRule(from, to) @@ -531,9 +565,12 @@ Functions for manipulating Self-Answered Response Rules: .. versionadded:: 1.3.0 + .. versionchanged:: 1.6.0 + ``id`` can now be a string representing the name of the rule. + Remove self answered response rule ``id``. - :param int id: The UUID of the rule to remove if ``id`` is an UUID, its position otherwise + :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise .. function:: showSelfAnsweredResponseRules([options])