]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #8657 from rgacogne/ddist-backend-uuid
authorRemi Gacogne <rgacogne@users.noreply.github.com>
Mon, 20 Jan 2020 09:12:40 +0000 (10:12 +0100)
committerGitHub <noreply@github.com>
Mon, 20 Jan 2020 09:12:40 +0000 (10:12 +0100)
dnsdist: Allow retrieving and deleting a backend via its UUID

1  2 
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/reference/config.rst

diff --combined pdns/dnsdist-console.cc
index 98af2e3fe775950722b54df430322768841adbb3,c30030b84bc6d19096d05be34a299bcc63341592..76b99967f5b55cfcfe98a53fc115a1a1c2d8a445
@@@ -402,7 -402,7 +402,7 @@@ const std::vector<ConsoleKeyword> g_con
    { "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" },
    { "getResponseRing", true, "", "return the current content of the response ring" },
    { "getRespRing", true, "", "return the qname/rcode content of the response ring" },
-   { "getServer", true, "n", "returns server with index n" },
+   { "getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string" },
    { "getServers", true, "", "returns a table with all defined servers" },
    { "getStatisticsCounters", true, "", "returns a map of statistic counters" },
    { "getTLSContext", true, "n", "returns the TLS context with index n" },
    { "KeyValueStoreLookupRule", true, "kvs, lookupKey", "matches queries if the key is found in the specified Key Value store" },
    { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
    { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
 +  { "LogResponseAction", true, "[filename], [append], [buffered]", "Log a line for each response, to the specified file if any, to the console (require verbose) otherwise. The `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
    { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
    { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
    { "MacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" },
    { "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" },
-   { "rmServer", true, "n", "remove server with index n" },
+   { "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"},
    { "setACL", true, "{netmask, netmask}", "replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us" },
    { "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
    { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" },
    { "setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove" },
 +  { "setConsistentHashingBalancingFactor", true, "factor", "Set the balancing factor for bounded-load consistent hashing" },
    { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" },
    { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" },
    { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" },
diff --combined pdns/dnsdist-lua.cc
index 272a92463da25f2e7ededaec98c85aec9b0ec3aa,b51fd65125738b89af15dcd201c860b5f9811f18..79557c6f96caa5098f796d8779aad5953e789488
@@@ -34,7 -34,6 +34,7 @@@
  #include "dnsdist.hh"
  #include "dnsdist-console.hh"
  #include "dnsdist-ecs.hh"
 +#include "dnsdist-healthchecks.hh"
  #include "dnsdist-lua.hh"
  #include "dnsdist-rings.hh"
  #include "dnsdist-secpoll.hh"
@@@ -207,19 -206,15 +207,19 @@@ static void parseTLSConfig(TLSConfig& c
  
  #endif // defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
  
 -void setupLuaConfig(bool client)
 +void setupLuaConfig(bool client, bool configCheck)
  {
    typedef std::unordered_map<std::string, boost::variant<bool, std::string, vector<pair<int, std::string> >, DownstreamState::checkfunc_t > > newserver_t;
    g_lua.writeFunction("inClientStartup", [client]() {
          return client && !g_configurationDone;
    });
  
 +  g_lua.writeFunction("inConfigCheck", [client, configCheck]() {
 +        return !configCheck;
 +  });
 +
    g_lua.writeFunction("newServer",
 -      [client](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
 +                      [client, configCheck](boost::variant<string,newserver_t> pvars, boost::optional<int> qps) {
        setLuaSideEffect();
  
        std::shared_ptr<DownstreamState> ret = std::make_shared<DownstreamState>(ComboAddress());
          }
        }
  
 -      if(client) {
 -        // do not construct DownstreamState now, it would try binding sockets.
 -        return ret;
 -      }
 -      ret=std::make_shared<DownstreamState>(serverAddr, sourceAddr, sourceItf, sourceItfName, numberOfSockets);
 +      // create but don't connect the socket in client or check-config modes
 +      ret=std::make_shared<DownstreamState>(serverAddr, sourceAddr, sourceItf, sourceItfName, numberOfSockets, !(client || configCheck));
  
        if(vars.count("qps")) {
          int qpsVal=std::stoi(boost::get<string>(vars["qps"]));
        } );
  
    g_lua.writeFunction("rmServer",
-                       [](boost::variant<std::shared_ptr<DownstreamState>, int> var)
+                       [](boost::variant<std::shared_ptr<DownstreamState>, int, std::string> var)
                        {
                          setLuaSideEffect();
-                         shared_ptr<DownstreamState> server;
-                         auto* rem = boost::get<shared_ptr<DownstreamState>>(&var);
+                         shared_ptr<DownstreamState> server = nullptr;
                          auto states = g_dstates.getCopy();
-                         if(rem) {
+                         if (auto* rem = boost::get<shared_ptr<DownstreamState>>(&var)) {
                            server = *rem;
                          }
+                         else if (auto str = boost::get<std::string>(&var)) {
+                           const auto uuid = getUniqueID(*str);
+                           for (auto& state : states) {
+                             if (state->id == uuid) {
+                               server = state;
+                             }
+                           }
+                         }
                          else {
                            int idx = boost::get<int>(var);
                            server = states.at(idx);
                          }
+                         if (!server) {
+                           throw std::runtime_error("unable to locate the requested server");
+                         }
                          auto localPools = g_pools.getCopy();
                          for (const string& poolName : server->pools) {
                            removeServerFromPool(localPools, poolName, server);
        return getDownstreamCandidates(g_pools.getCopy(), pool);
      });
  
-   g_lua.writeFunction("getServer", [client](int i) {
-       if (client)
+   g_lua.writeFunction("getServer", [client](boost::variant<unsigned int, std::string> i) {
+       if (client) {
          return std::make_shared<DownstreamState>(ComboAddress());
-       return g_dstates.getCopy().at(i);
+       }
+       auto states = g_dstates.getCopy();
+       if (auto str = boost::get<std::string>(&i)) {
+         const auto uuid = getUniqueID(*str);
+         for (auto& state : states) {
+           if (state->id == uuid) {
+             return state;
+           }
+         }
+       }
+       else if (auto pos = boost::get<unsigned int>(&i)) {
+         return states.at(*pos);
+       }
+       g_outputBuffer = "Error: no rule matched\n";
+       return std::shared_ptr<DownstreamState>(nullptr);
      });
  
    g_lua.writeFunction("carbonServer", [](const std::string& address, boost::optional<string> ourName,
        g_carbon.setState(ours);
    });
  
 -  g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
 +  g_lua.writeFunction("webserver", [client,configCheck](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
        setLuaSideEffect();
        ComboAddress local;
        try {
          throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e.reason);
        }
  
 -      if (client) {
 +      if (client || configCheck) {
          return;
        }
  
        }
      });
  
 -  g_lua.writeFunction("controlSocket", [client](const std::string& str) {
 +  g_lua.writeFunction("controlSocket", [client,configCheck](const std::string& str) {
        setLuaSideEffect();
        ComboAddress local(str, 5199);
  
 -      if(client) {
 +      if(client || configCheck) {
        g_serverControl = local;
        return;
        }
  #endif
      });
  
 -  g_lua.writeFunction("generateDNSCryptProviderKeys", [](const std::string& publicKeyFile, const std::string privateKeyFile) {
 +  g_lua.writeFunction("generateDNSCryptProviderKeys", [client](const std::string& publicKeyFile, const std::string privateKeyFile) {
        setLuaNoSideEffect();
  #ifdef HAVE_DNSCRYPT
 +      if (client) {
 +        return;
 +      }
        unsigned char publicKey[DNSCRYPT_PROVIDER_PUBLIC_KEY_SIZE];
        unsigned char privateKey[DNSCRYPT_PROVIDER_PRIVATE_KEY_SIZE];
        sodium_mlock(privateKey, sizeof(privateKey));
      });
  
  #ifdef HAVE_DNSCRYPT
 -  g_lua.writeFunction("generateDNSCryptCertificate", [](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
 +  g_lua.writeFunction("generateDNSCryptCertificate", [client](const std::string& providerPrivateKeyFile, const std::string& certificateFile, const std::string privateKeyFile, uint32_t serial, time_t begin, time_t end, boost::optional<DNSCryptExchangeVersion> version) {
        setLuaNoSideEffect();
 +      if (client) {
 +        return;
 +      }
        DNSCryptPrivateKey privateKey;
        DNSCryptCert cert;
  
        g_roundrobinFailOnNoServer = fail;
      });
  
 +  g_lua.writeFunction("setConsistentHashingBalancingFactor", [](double factor) {
 +      setLuaSideEffect();
 +      if (factor >= 0) {
 +        g_consistentHashBalancingFactor = factor;
 +      }
 +      else {
 +        errlog("Invalid value passed to setConsistentHashingBalancingFactor()!");
 +        g_outputBuffer="Invalid value passed to setConsistentHashingBalancingFactor()!\n";
 +        return;
 +      }
 +    });
 +
    g_lua.writeFunction("setRingBuffersSize", [](size_t capacity, boost::optional<size_t> numberOfShards) {
        setLuaSideEffect();
        if (g_configurationDone) {
        g_useTCPSinglePipe = flag;
      });
  
 -  g_lua.writeFunction("snmpAgent", [client](bool enableTraps, boost::optional<std::string> masterSocket) {
 -      if(client)
 +  g_lua.writeFunction("snmpAgent", [client,configCheck](bool enableTraps, boost::optional<std::string> masterSocket) {
 +      if(client || configCheck)
          return;
  #ifdef HAVE_NET_SNMP
        if (g_configurationDone) {
  
            if (vars->count("provider")) {
              frontend->d_provider = boost::get<const string>((*vars)["provider"]);
 +            boost::algorithm::to_lower(frontend->d_provider);
            }
  
            parseTLSConfig(frontend->d_tlsConfig, "addTLSLocal", vars);
              vinfolog("Loading TLS provider '%s'", frontend->d_provider);
            }
            else {
 +#ifdef HAVE_LIBSSL
              vinfolog("Loading default TLS provider 'openssl'");
 +#else
 +            vinfolog("Loading default TLS provider 'gnutls'");
 +#endif
            }
            // only works pre-startup, so no sync necessary
            auto cs = std::unique_ptr<ClientState>(new ClientState(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus));
      g_lua.writeFunction("setAllowEmptyResponse", [](bool allow) { g_allowEmptyResponse=allow; });
  
  #if defined(HAVE_LIBSSL) && defined(HAVE_OCSP_BASIC_SIGN)
 -    g_lua.writeFunction("generateOCSPResponse", [](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
 -      return libssl_generate_ocsp_response(certFile, caCert, caKey, outFile, ndays, nmin);
 +    g_lua.writeFunction("generateOCSPResponse", [client](const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) {
 +      if (client) {
 +        return;
 +      }
 +
 +      libssl_generate_ocsp_response(certFile, caCert, caKey, outFile, ndays, nmin);
      });
  #endif /* HAVE_LIBSSL && HAVE_OCSP_BASIC_SIGN*/
  }
  
 -vector<std::function<void(void)>> setupLua(bool client, const std::string& config)
 +vector<std::function<void(void)>> setupLua(bool client, bool configCheck, const std::string& config)
  {
    g_launchWork= new vector<std::function<void(void)>>();
  
    setupLuaActions();
 -  setupLuaConfig(client);
 +  setupLuaConfig(client, configCheck);
    setupLuaBindings(client);
    setupLuaBindingsDNSCrypt();
    setupLuaBindingsDNSQuestion();
    setupLuaBindingsKVS(client);
    setupLuaBindingsPacketCache();
 -  setupLuaBindingsProtoBuf(client);
 +  setupLuaBindingsProtoBuf(client, configCheck);
    setupLuaInspection();
    setupLuaRules();
    setupLuaVars();
index 59ae79dc6dda6318afaf5d69a1ded149c35649e6,14a56106e48fdbd8456386273ccc1c82db5312b3..9aa47383f8fecc464b9e16ffa6098a35cba6e4cd
@@@ -227,12 -227,6 +227,12 @@@ Control Socket, Console and Webserve
  
    Returns true while the console client is parsing the configuration.
  
 +.. function:: inConfigCheck()
 +
 +  .. versionadded:: 1.5.0
 +
 +  Returns true while the configuration is being checked, ie when run with ``--check-config``.
 +
  .. function:: makeKey()
  
    Generate and print an encryption key.
@@@ -434,9 -428,12 +434,12 @@@ Server
  
  .. function:: getServer(index) -> Server
  
+   .. versionchanged:: 1.5.0
+     ``index`` might be an UUID.
    Get a :class:`Server`
  
-   :param int index: The number of the server (as seen in :func:`showServers`).
+   :param int or str index: The number of the server (as seen in :func:`showServers`) or its UUID as a string.
    :returns:  The :class:`Server` object or nil
  
  .. function:: getServers()
    Returns a table with all defined servers.
  
  .. function:: rmServer(index)
+               rmServer(uuid)
                rmServer(server)
  
+   .. versionchanged:: 1.5.0
+     ``uuid`` selection added.
    Remove a backend server.
  
-   :param int index: The number of the server (as seen in :func:`showServers`).
+   :param int or str index: The number of the server (as seen in :func:`showServers`), its UUID as a string, or a server object.
    :param Server server: A :class:`Server` object as returned by e.g. :func:`getServer`.
  
  Server Functions
@@@ -1026,21 -1027,6 +1033,21 @@@ faster than the existing rules
      :param int action: The action to take when the dynamic block matches, see :ref:`here <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
      :param int warningRate: If set to a non-zero value, the rate above which a warning message will be issued and a no-op block inserted
  
 +  .. method:: DynBlockRulesGroup:setRCodeRatio(rcode, ratio, seconds, reason, blockingTime, minimumNumberOfResponses [, action [, warningRate]])
 +
 +    .. versionadded:: 1.5.0
 +
 +    Adds a rate-limiting rule for the ratio of responses of code ``rcode`` over the total number of responses for a given client.
 +
 +    :param int rcode: The response code
 +    :param int ratio: Ratio of responses per second of the given rcode over the total number of responses for this client to exceed
 +    :param int seconds: Number of seconds the ratio has been exceeded
 +    :param string reason: The message to show next to the blocks
 +    :param int blockingTime: The number of seconds this block to expire
 +    :param int minimumNumberOfResponses: How many total responses is required for this rule to apply
 +    :param int action: The action to take when the dynamic block matches, see :ref:`here <DNSAction>`. (default to the one set with :func:`setDynBlocksAction`)
 +    :param int warningRatio: If set to a non-zero value, the ratio above which a warning message will be issued and a no-op block inserted
 +
    .. method:: DynBlockRulesGroup:setQTypeRate(qtype, rate, seconds, reason, blockingTime [, action [, warningRate]])
  
      .. versionchanged:: 1.3.3