]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Switch Webserver and console to the new configuration
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 11 Jun 2024 10:41:24 +0000 (12:41 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 15 Jul 2024 09:47:54 +0000 (11:47 +0200)
pdns/dnsdistdist/dnsdist-configuration.hh
pdns/dnsdistdist/dnsdist-console.cc
pdns/dnsdistdist/dnsdist-console.hh
pdns/dnsdistdist/dnsdist-lua-web.cc
pdns/dnsdistdist/dnsdist-lua.cc
pdns/dnsdistdist/dnsdist-lua.hh
pdns/dnsdistdist/dnsdist-metrics.cc
pdns/dnsdistdist/dnsdist-web.cc
pdns/dnsdistdist/dnsdist-web.hh
pdns/dnsdistdist/dnsdist.cc
pdns/dnsdistdist/test-dnsdist-lua-ffi.cc

index 9889d40b9a94e1d982c87c222b0da570a86daca5..91a41f208232fa05955a578fd924afe44753bbdd 100644 (file)
 
 #include <functional>
 #include <map>
+#include <memory>
+#include <optional>
 #include <string>
 
+#include "credentials.hh"
 #include "dnsdist-query-count.hh"
 #include "dnsdist-rule-chains.hh"
 #include "iputils.hh"
@@ -156,8 +159,6 @@ struct Configuration
 {
   std::set<std::string> d_capabilitiesToRetain;
   std::vector<uint32_t> d_tcpFastOpenKey;
-  ComboAddress d_consoleServerAddress{"127.0.0.1:5199"};
-  std::string d_consoleKey;
 #ifdef __linux__
   // On Linux this gives us 128k pending queries (default is 8192 queries),
   // which should be enough to deal with huge spikes
@@ -193,23 +194,21 @@ struct Configuration
    a RCU-like mechanism */
 struct RuntimeConfiguration
 {
-  // ca tient pas la route: meilleure option: stocker un type plus opaque dans la configuration (dnsdist::rules::RuleChains) et
-  // laisser le soin a dnsdist::rules de le gerer
-  /*  std::vector<rules::RuleAction> d_cacheMissRuleActions;
-  std::vector<rules::ResponseRuleAction> d_respruleactions;
-  std::vector<rules::ResponseRuleAction> d_cachehitrespruleactions;
-  std::vector<rules::ResponseRuleAction> d_selfansweredrespruleactions;
-  std::vector<rules::ResponseRuleAction> d_cacheInsertedRespRuleActions;
-  std::vector<rules::ResponseRuleAction> d_XFRRespRuleActions;
-  */
   rules::RuleChains d_ruleChains;
   servers_t d_backends;
   std::map<std::string, std::shared_ptr<ServerPool>> d_pools;
+  std::shared_ptr<const CredentialsHolder> d_webPassword;
+  std::shared_ptr<const CredentialsHolder> d_webAPIKey;
+  std::optional<std::unordered_map<std::string, std::string>> d_webCustomHeaders;
   std::shared_ptr<ServerPolicy> d_lbPolicy;
   NetmaskGroup d_ACL;
   NetmaskGroup d_proxyProtocolACL;
   NetmaskGroup d_consoleACL;
+  NetmaskGroup d_webServerACL;
+  std::optional<ComboAddress> d_webServerAddress{std::nullopt};
   dnsdist::QueryCount::Configuration d_queryCountConfig;
+  ComboAddress d_consoleServerAddress{"127.0.0.1:5199"};
+  std::string d_consoleKey;
   std::string d_secPollSuffix{"secpoll.powerdns.com."};
   std::string d_apiConfigDirectory;
   uint64_t d_dynBlocksPurgeInterval{60};
@@ -231,6 +230,9 @@ struct RuntimeConfiguration
   uint16_t d_tlsSessionCacheSessionValidity{600};
   uint16_t d_tlsSessionCacheMaxSessionsPerBackend{20};
   DNSAction::Action d_dynBlockAction{DNSAction::Action::Drop};
+  bool d_apiRequiresAuthentication{true};
+  bool d_dashboardRequiresAuthentication{true};
+  bool d_statsRequireAuthentication{true};
   bool d_truncateTC{false};
   bool d_fixupCase{false};
   bool d_queryCountEnabled{false};
index d4b1b7b552ec88b0755b78f80a4e303ea31a2928..f5ed5c705f202d0073b34687924fa56e0cba0ac8 100644 (file)
@@ -178,7 +178,7 @@ static bool putMsgLen32(int fileDesc, uint32_t len)
 
 static ConsoleCommandResult sendMessageToServer(int fileDesc, const std::string& line, dnsdist::crypto::authenticated::Nonce& readingNonce, dnsdist::crypto::authenticated::Nonce& writingNonce, const bool outputEmptyLine)
 {
-  const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+  const auto& consoleKey = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey;
   string msg = dnsdist::crypto::authenticated::encryptSym(line, consoleKey, writingNonce);
   const auto msgLen = msg.length();
   if (msgLen > std::numeric_limits<uint32_t>::max()) {
@@ -225,8 +225,8 @@ namespace dnsdist::console
 {
 void doClient(const std::string& command)
 {
-  const auto consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
-  const auto server = dnsdist::configuration::getImmutableConfiguration().d_consoleServerAddress;
+  const auto consoleKey = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey;
+  const auto server = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleServerAddress;
   if (!dnsdist::crypto::authenticated::isValidKey(consoleKey)) {
     cerr << "The currently configured console key is not valid, please configure a valid key using the setKey() directive" << endl;
     return;
@@ -932,7 +932,7 @@ static void controlClientThread(ConsoleConnection&& conn)
 
     setTCPNoDelay(conn.getFD());
 
-    const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+    const auto consoleKey = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey;
     dnsdist::crypto::authenticated::Nonce theirs;
     dnsdist::crypto::authenticated::Nonce ours;
     dnsdist::crypto::authenticated::Nonce readingNonce;
@@ -1065,11 +1065,11 @@ static void controlClientThread(ConsoleConnection&& conn)
   }
 }
 
-// NOLINTNEXTLINE(performance-unnecessary-value-param): this is thread
-void controlThread(std::shared_ptr<Socket>&& acceptFD, ComboAddress local)
+void controlThread(Socket&& acceptFD)
 {
   try {
     setThreadName("dnsdist/control");
+    const ComboAddress local = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleServerAddress;
     s_connManager.setMaxConcurrentConnections(dnsdist::configuration::getImmutableConfiguration().d_consoleMaxConcurrentConnections);
 
     ComboAddress client;
@@ -1081,8 +1081,8 @@ void controlThread(std::shared_ptr<Socket>&& acceptFD, ComboAddress local)
     int sock{-1};
     infolog("Accepting control connections on %s", local.toStringWithPort());
 
-    while ((sock = SAccept(acceptFD->getHandle(), client)) >= 0) {
-      const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+    while ((sock = SAccept(acceptFD.getHandle(), client)) >= 0) {
+      const auto& consoleKey = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey;
       FDWrapper socket(sock);
       if (!dnsdist::crypto::authenticated::isValidKey(consoleKey)) {
         vinfolog("Control connection from %s dropped because we don't have a valid key configured, please configure one using setKey()", client.toStringWithPort());
index e59e58b99d74d0702285ed4b8a811466180807ce..ef27bc0a1be408cbfed0700c2d228236bf87d9d2 100644 (file)
@@ -32,7 +32,7 @@ namespace dnsdist::console
 const std::vector<std::pair<timeval, std::string>>& getConfigurationDelta();
 void doClient(const std::string& command);
 void doConsole();
-void controlThread(std::shared_ptr<Socket>&& acceptFD, ComboAddress local);
+void controlThread(Socket&& acceptFD);
 void clearHistory();
 
 #ifndef DISABLE_COMPLETION
index 35cf0d0f09630224bd17a2d17f932982640c17a8..cd7e47f91ed78d0d9715528e502498caa4fb19c2 100644 (file)
 #include "dnsdist-lua.hh"
 #include "dnsdist-web.hh"
 
+namespace dnsdist::webserver
+{
 void registerWebHandler(const std::string& endpoint, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)> handler, bool isLua);
+}
 
 void setupLuaWeb(LuaContext& luaCtx)
 {
 #ifndef DISABLE_LUA_WEB_HANDLERS
   luaCtx.writeFunction("registerWebHandler", [](const std::string& path, std::function<void(const YaHTTP::Request*, YaHTTP::Response*)> handler) {
     /* LuaWrapper does a copy for objects passed by reference, so we pass a pointer */
-    registerWebHandler(path, [handler](const YaHTTP::Request& req, YaHTTP::Response& resp) { handler(&req, &resp); }, true);
+    dnsdist::webserver::registerWebHandler(path, [handler](const YaHTTP::Request& req, YaHTTP::Response& resp) { handler(&req, &resp); }, true);
   });
 
   luaCtx.registerMember<std::string(YaHTTP::Request::*)>("path", [](const YaHTTP::Request& req) -> std::string { return req.url.path; }, [](YaHTTP::Request& req, const std::string& path) { (void) path; });
index 99d4d708fd8aeae7826617e14337733a41aed925..c0da1259e2374b9d6e3f959d1df10abce0ac6aca 100644 (file)
@@ -82,7 +82,7 @@
 
 using std::thread;
 
-static boost::optional<std::vector<std::function<void(void)>>> g_launchWork = boost::none;
+static std::optional<std::vector<std::function<void(void)>>> s_launchWork{std::nullopt};
 
 static boost::tribool s_noLuaSideEffect;
 
@@ -699,8 +699,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
                          }
 
                          if (ret->connected) {
-                           if (g_launchWork) {
-                             g_launchWork->push_back([ret]() {
+                           if (s_launchWork) {
+                             s_launchWork->push_back([ret]() {
                                ret->start();
                              });
                            }
@@ -1292,29 +1292,26 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       return;
     }
 
-    try {
-      int sock = SSocket(local.sin4.sin_family, SOCK_STREAM, 0);
-      SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-      SBind(sock, local);
-      SListen(sock, 5);
-      auto launch = [sock, local]() {
-        thread thr(dnsdistWebserverThread, sock, local);
+    dnsdist::configuration::updateRuntimeConfiguration([local](dnsdist::configuration::RuntimeConfiguration& config) {
+      config.d_webServerAddress = local;
+    });
+
+    if (dnsdist::configuration::isConfigurationDone()) {
+      try {
+        auto sock = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
+        sock.bind(local, true);
+        sock.listen(5);
+        thread thr(dnsdist::webserver::WebserverThread, std::move(sock));
         thr.detach();
-      };
-      if (g_launchWork) {
-        g_launchWork->push_back(launch);
       }
-      else {
-        launch();
+      catch (const std::exception& e) {
+        g_outputBuffer = "Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
+        errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
       }
     }
-    catch (const std::exception& e) {
-      g_outputBuffer = "Unable to bind to webserver socket on " + local.toStringWithPort() + ": " + e.what();
-      errlog("Unable to bind to webserver socket on %s: %s", local.toStringWithPort(), e.what());
-    }
   });
 
-  typedef LuaAssociativeTable<boost::variant<bool, std::string, LuaAssociativeTable<std::string>>> webserveropts_t;
+  using webserveropts_t = LuaAssociativeTable<boost::variant<bool, std::string, LuaAssociativeTable<std::string>>>;
 
   luaCtx.writeFunction("setWebserverConfig", [](boost::optional<webserveropts_t> vars) {
     setLuaSideEffect();
@@ -1323,64 +1320,65 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       return;
     }
 
-    bool hashPlaintextCredentials = false;
-    getOptionalValue<bool>(vars, "hashPlaintextCredentials", hashPlaintextCredentials);
-
-    std::string password;
-    std::string apiKey;
-    std::string acl;
-    LuaAssociativeTable<std::string> headers;
-    bool statsRequireAuthentication{true};
-    bool apiRequiresAuthentication{true};
-    bool dashboardRequiresAuthentication{true};
-    int maxConcurrentConnections = 0;
-
-    if (getOptionalValue<std::string>(vars, "password", password) > 0) {
-      auto holder = make_unique<CredentialsHolder>(std::move(password), hashPlaintextCredentials);
-      if (!holder->wasHashed() && holder->isHashingAvailable()) {
-        infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+    dnsdist::configuration::updateRuntimeConfiguration([&vars](dnsdist::configuration::RuntimeConfiguration& config) {
+      std::string password;
+      std::string apiKey;
+      std::string acl;
+      LuaAssociativeTable<std::string> headers;
+      bool statsRequireAuthentication{true};
+      bool apiRequiresAuthentication{true};
+      bool dashboardRequiresAuthentication{true};
+      bool hashPlaintextCredentials = false;
+      getOptionalValue<bool>(vars, "hashPlaintextCredentials", hashPlaintextCredentials);
+
+      if (getOptionalValue<std::string>(vars, "password", password) > 0) {
+        auto holder = std::make_shared<CredentialsHolder>(std::move(password), hashPlaintextCredentials);
+        if (!holder->wasHashed() && holder->isHashingAvailable()) {
+          infolog("Passing a plain-text password via the 'password' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+        }
+        config.d_webPassword = std::move(holder);
       }
 
-      setWebserverPassword(std::move(holder));
-    }
-
-    if (getOptionalValue<std::string>(vars, "apiKey", apiKey) > 0) {
-      auto holder = make_unique<CredentialsHolder>(std::move(apiKey), hashPlaintextCredentials);
-      if (!holder->wasHashed() && holder->isHashingAvailable()) {
-        infolog("Passing a plain-text API key via the 'apiKey' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+      if (getOptionalValue<std::string>(vars, "apiKey", apiKey) > 0) {
+        auto holder = std::make_shared<CredentialsHolder>(std::move(apiKey), hashPlaintextCredentials);
+        if (!holder->wasHashed() && holder->isHashingAvailable()) {
+          infolog("Passing a plain-text API key via the 'apiKey' parameter to 'setWebserverConfig()' is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+        }
+        config.d_webAPIKey = std::move(holder);
       }
 
-      setWebserverAPIKey(std::move(holder));
-    }
-
-    if (getOptionalValue<std::string>(vars, "acl", acl) > 0) {
-      setWebserverACL(acl);
-    }
+      if (getOptionalValue<std::string>(vars, "acl", acl) > 0) {
+        NetmaskGroup ACLnmg;
+        ACLnmg.toMasks(acl);
+        config.d_webServerACL = std::move(ACLnmg);
+      }
 
-    if (getOptionalValue<decltype(headers)>(vars, "customHeaders", headers) > 0) {
-      setWebserverCustomHeaders(headers);
-    }
+      if (getOptionalValue<decltype(headers)>(vars, "customHeaders", headers) > 0) {
+        config.d_webCustomHeaders = std::move(headers);
+      }
 
-    if (getOptionalValue<bool>(vars, "statsRequireAuthentication", statsRequireAuthentication) > 0) {
-      setWebserverStatsRequireAuthentication(statsRequireAuthentication);
-    }
+      if (getOptionalValue<bool>(vars, "statsRequireAuthentication", statsRequireAuthentication) > 0) {
+        config.d_statsRequireAuthentication = statsRequireAuthentication;
+      }
 
-    if (getOptionalValue<bool>(vars, "apiRequiresAuthentication", apiRequiresAuthentication) > 0) {
-      setWebserverAPIRequiresAuthentication(apiRequiresAuthentication);
-    }
+      if (getOptionalValue<bool>(vars, "apiRequiresAuthentication", apiRequiresAuthentication) > 0) {
+        config.d_apiRequiresAuthentication = apiRequiresAuthentication;
+      }
 
-    if (getOptionalValue<bool>(vars, "dashboardRequiresAuthentication", dashboardRequiresAuthentication) > 0) {
-      setWebserverDashboardRequiresAuthentication(dashboardRequiresAuthentication);
-    }
+      if (getOptionalValue<bool>(vars, "dashboardRequiresAuthentication", dashboardRequiresAuthentication) > 0) {
+        config.d_dashboardRequiresAuthentication = dashboardRequiresAuthentication;
+      }
+    });
 
+    int maxConcurrentConnections = 0;
     if (getOptionalIntegerValue("setWebserverConfig", vars, "maxConcurrentConnections", maxConcurrentConnections) > 0) {
-      setWebserverMaxConcurrentConnections(maxConcurrentConnections);
+      dnsdist::webserver::setMaxConcurrentConnections(maxConcurrentConnections);
     }
   });
 
   luaCtx.writeFunction("showWebserverConfig", []() {
     setLuaNoSideEffect();
-    return getWebserverConfig();
+    return dnsdist::webserver::getConfig();
   });
 
   luaCtx.writeFunction("hashPassword", [](const std::string& password, boost::optional<uint64_t> workFactor) {
@@ -1395,40 +1393,32 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     ComboAddress local(str, 5199);
 
     if (client || configCheck) {
-      dnsdist::configuration::updateImmutableConfiguration([&local](dnsdist::configuration::Configuration& config) {
-        config.d_consoleServerAddress = local;
-      });
       return;
     }
 
-    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
+    dnsdist::configuration::updateRuntimeConfiguration([local](dnsdist::configuration::RuntimeConfiguration& config) {
+      config.d_consoleServerAddress = local;
       config.d_consoleEnabled = true;
     });
 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
-    if (dnsdist::configuration::isConfigurationDone() && dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) {
+    if (dnsdist::configuration::isConfigurationDone() && dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) {
       warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
     }
 #endif
 
-    try {
-      auto sock = std::make_shared<Socket>(local.sin4.sin_family, SOCK_STREAM, 0);
-      sock->bind(local, true);
-      sock->listen(5);
-      auto launch = [sock = std::move(sock), local]() {
-        std::thread consoleControlThread(dnsdist::console::controlThread, std::move(sock), local);
+    if (dnsdist::configuration::isConfigurationDone()) {
+      try {
+        auto sock = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
+        sock.bind(local, true);
+        sock.listen(5);
+        std::thread consoleControlThread(dnsdist::console::controlThread, std::move(sock));
         consoleControlThread.detach();
-      };
-      if (g_launchWork) {
-        g_launchWork->emplace_back(std::move(launch));
       }
-      else {
-        launch();
+      catch (const std::exception& exp) {
+        g_outputBuffer = "Unable to bind to control socket on " + local.toStringWithPort() + ": " + exp.what();
+        errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), exp.what());
       }
     }
-    catch (std::exception& e) {
-      g_outputBuffer = "Unable to bind to control socket on " + local.toStringWithPort() + ": " + e.what();
-      errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), e.what());
-    }
   });
 
   luaCtx.writeFunction("addConsoleACL", [](const std::string& netmask) {
@@ -1516,7 +1506,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
   });
 
   luaCtx.writeFunction("setKey", [](const std::string& key) {
-    if (!dnsdist::configuration::isConfigurationDone() && !dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
+    if (!dnsdist::configuration::isConfigurationDone() && !dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) { // this makes sure the commandline -k key prevails over dnsdist.conf
       return; // but later setKeys() trump the -k value again
     }
 #if !defined(HAVE_LIBSODIUM) && !defined(HAVE_LIBCRYPTO)
@@ -1531,7 +1521,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
       return;
     }
 
-    dnsdist::configuration::updateImmutableConfiguration([&newKey](dnsdist::configuration::Configuration& config) {
+    dnsdist::configuration::updateRuntimeConfiguration([&newKey](dnsdist::configuration::RuntimeConfiguration& config) {
       config.d_consoleKey = std::move(newKey);
     });
   });
@@ -1544,7 +1534,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     setLuaNoSideEffect();
 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
     try {
-      const auto& consoleKey = dnsdist::configuration::getImmutableConfiguration().d_consoleKey;
+      const auto& consoleKey = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey;
       string testmsg;
 
       if (optTestMsg) {
@@ -3381,7 +3371,7 @@ vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool
 {
   // this needs to exist only during the parsing of the configuration
   // and cannot be captured by lambdas
-  g_launchWork = std::vector<std::function<void(void)>>();
+  s_launchWork = std::vector<std::function<void(void)>>();
 
   setupLuaActions(luaCtx);
   setupLuaConfig(luaCtx, client, configCheck);
@@ -3418,7 +3408,8 @@ vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool
 
   luaCtx.executeCode(ifs);
 
-  auto ret = *g_launchWork;
-  g_launchWork = boost::none;
+  auto ret = std::move(*s_launchWork);
+  s_launchWork = std::nullopt;
+
   return ret;
 }
index 2944b2bf8a8df936fe5d792eb1657b34c265a232..475d0570c7470772d18c3c82fc7075c00a92a601 100644 (file)
@@ -170,7 +170,7 @@ std::shared_ptr<DNSRule> makeRule(const luadnsrule_t& var, const std::string& ca
 void parseRuleParams(boost::optional<luaruleparams_t>& params, boost::uuids::uuid& uuid, std::string& name, uint64_t& creationOrder);
 void checkParameterBound(const std::string& parameter, uint64_t value, size_t max = std::numeric_limits<uint16_t>::max());
 
-vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config);
+std::vector<std::function<void(void)>> setupLua(LuaContext& luaCtx, bool client, bool configCheck, const std::string& config);
 void setupLuaActions(LuaContext& luaCtx);
 void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck);
 void setupLuaBindingsDNSCrypt(LuaContext& luaCtx, bool client);
index 3c8986d0568c058cd59222bb3b4489b4c5b601dd..4c647db017be3531f30485fac3b4236c80d0381e 100644 (file)
@@ -178,7 +178,7 @@ std::optional<std::string> declareCustomMetric(const std::string& name, const st
     if (itp.second) {
       g_stats.entries.write_lock()->emplace_back(Stats::EntryPair{name, &(*customCounters)[name].d_value});
       dnsdist::prometheus::PrometheusMetricDefinition def{name, type, description, finalCustomName};
-      addMetricDefinition(def);
+      dnsdist::webserver::addMetricDefinition(def);
     }
   }
   else if (type == "gauge") {
@@ -187,7 +187,7 @@ std::optional<std::string> declareCustomMetric(const std::string& name, const st
     if (itp.second) {
       g_stats.entries.write_lock()->emplace_back(Stats::EntryPair{name, &(*customGauges)[name].d_value});
       dnsdist::prometheus::PrometheusMetricDefinition def{name, type, description, finalCustomName};
-      addMetricDefinition(def);
+      dnsdist::webserver::addMetricDefinition(def);
     }
   }
   else {
index 787c225bca3097c26989435cd54e8c86ce21b76d..3fbbd1180fcc916bce7dfedbc78be32f0576a7a9 100644 (file)
 #include "threadname.hh"
 #include "sstuff.hh"
 
-struct WebserverConfig
-{
-  WebserverConfig()
-  {
-    acl.toMasks("127.0.0.1, ::1");
-  }
-
-  NetmaskGroup acl;
-  std::unique_ptr<CredentialsHolder> password;
-  std::unique_ptr<CredentialsHolder> apiKey;
-  boost::optional<std::unordered_map<std::string, std::string>> customHeaders;
-  bool apiRequiresAuthentication{true};
-  bool dashboardRequiresAuthentication{true};
-  bool statsRequireAuthentication{true};
-};
-
-LockGuarded<WebserverConfig> g_webserverConfig;
-
-static ConcurrentConnectionManager s_connManager(100);
-
-std::string getWebserverConfig()
-{
-  ostringstream out;
-
-  {
-    auto config = g_webserverConfig.lock();
-    out << "Current web server configuration:" << endl;
-    out << "ACL: " << config->acl.toString() << endl;
-    out << "Custom headers: ";
-    if (config->customHeaders) {
-      out << endl;
-      for (const auto& header : *config->customHeaders) {
-        out << " - " << header.first << ": " << header.second << endl;
-      }
-    }
-    else {
-      out << "None" << endl;
-    }
-    out << "API requires authentication: " << (config->apiRequiresAuthentication ? "yes" : "no") << endl;
-    out << "Dashboard requires authentication: " << (config->dashboardRequiresAuthentication ? "yes" : "no") << endl;
-    out << "Statistics require authentication: " << (config->statsRequireAuthentication ? "yes" : "no") << endl;
-    out << "Password: " << (config->password ? "set" : "unset") << endl;
-    out << "API key: " << (config->apiKey ? "set" : "unset") << endl;
-  }
-  {
-    const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
-    out << "API writable: " << (config.d_apiReadWrite ? "yes" : "no") << endl;
-    out << "API configuration directory: " << config.d_apiConfigDirectory << endl;
-    out << "Maximum concurrent connections: " << s_connManager.getMaxConcurrentConnections() << endl;
-  }
-
-  return out.str();
-}
-
-class WebClientConnection
-{
-public:
-  WebClientConnection(const ComboAddress& client, int socketDesc) :
-    d_client(client), d_socket(socketDesc)
-  {
-    if (!s_connManager.registerConnection()) {
-      throw std::runtime_error("Too many concurrent web client connections");
-    }
-  }
-  WebClientConnection(WebClientConnection&& rhs) noexcept :
-    d_client(rhs.d_client), d_socket(std::move(rhs.d_socket))
-  {
-  }
-  WebClientConnection(const WebClientConnection&) = delete;
-  WebClientConnection& operator=(const WebClientConnection&) = delete;
-  WebClientConnection& operator=(WebClientConnection&& rhs) noexcept
-  {
-    d_client = rhs.d_client;
-    d_socket = std::move(rhs.d_socket);
-    return *this;
-  }
-
-  ~WebClientConnection()
-  {
-    if (d_socket.getHandle() != -1) {
-      s_connManager.releaseConnection();
-    }
-  }
-
-  [[nodiscard]] const Socket& getSocket() const
-  {
-    return d_socket;
-  }
-
-  [[nodiscard]] const ComboAddress& getClient() const
-  {
-    return d_client;
-  }
-
-private:
-  ComboAddress d_client;
-  Socket d_socket;
-};
-
 #ifndef DISABLE_PROMETHEUS
 static MetricDefinitionStorage s_metricDefinitions;
 
@@ -228,6 +129,86 @@ std::map<std::string, MetricDefinition> MetricDefinitionStorage::metrics{
 };
 #endif /* DISABLE_PROMETHEUS */
 
+namespace dnsdist::webserver
+{
+static ConcurrentConnectionManager s_connManager(100);
+
+std::string getConfig()
+{
+  ostringstream out;
+
+  {
+    const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
+    out << "Current web server configuration:" << endl;
+    out << "ACL: " << config.d_webServerACL.toString() << endl;
+    out << "Custom headers: ";
+    if (config.d_webCustomHeaders) {
+      out << endl;
+      for (const auto& header : *config.d_webCustomHeaders) {
+        out << " - " << header.first << ": " << header.second << endl;
+      }
+    }
+    else {
+      out << "None" << endl;
+    }
+    out << "API requires authentication: " << (config.d_apiRequiresAuthentication ? "yes" : "no") << endl;
+    out << "Dashboard requires authentication: " << (config.d_dashboardRequiresAuthentication ? "yes" : "no") << endl;
+    out << "Statistics require authentication: " << (config.d_statsRequireAuthentication ? "yes" : "no") << endl;
+    out << "Password: " << (config.d_webPassword ? "set" : "unset") << endl;
+    out << "API key: " << (config.d_webAPIKey ? "set" : "unset") << endl;
+    out << "API writable: " << (config.d_apiReadWrite ? "yes" : "no") << endl;
+    out << "API configuration directory: " << config.d_apiConfigDirectory << endl;
+    out << "Maximum concurrent connections: " << s_connManager.getMaxConcurrentConnections() << endl;
+  }
+
+  return out.str();
+}
+
+class WebClientConnection
+{
+public:
+  WebClientConnection(const ComboAddress& client, int socketDesc) :
+    d_client(client), d_socket(socketDesc)
+  {
+    if (!s_connManager.registerConnection()) {
+      throw std::runtime_error("Too many concurrent web client connections");
+    }
+  }
+  WebClientConnection(WebClientConnection&& rhs) noexcept :
+    d_client(rhs.d_client), d_socket(std::move(rhs.d_socket))
+  {
+  }
+  WebClientConnection(const WebClientConnection&) = delete;
+  WebClientConnection& operator=(const WebClientConnection&) = delete;
+  WebClientConnection& operator=(WebClientConnection&& rhs) noexcept
+  {
+    d_client = rhs.d_client;
+    d_socket = std::move(rhs.d_socket);
+    return *this;
+  }
+
+  ~WebClientConnection()
+  {
+    if (d_socket.getHandle() != -1) {
+      s_connManager.releaseConnection();
+    }
+  }
+
+  [[nodiscard]] const Socket& getSocket() const
+  {
+    return d_socket;
+  }
+
+  [[nodiscard]] const ComboAddress& getClient() const
+  {
+    return d_client;
+  }
+
+private:
+  ComboAddress d_client;
+  Socket d_socket;
+};
+
 bool addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def)
 {
 #ifndef DISABLE_PROMETHEUS
@@ -280,7 +261,7 @@ static void apiSaveACL(const NetmaskGroup& nmg)
 }
 #endif /* DISABLE_WEB_CONFIG */
 
-static bool checkAPIKey(const YaHTTP::Request& req, const std::unique_ptr<CredentialsHolder>& apiKey)
+static bool checkAPIKey(const YaHTTP::Request& req, const std::shared_ptr<const CredentialsHolder>& apiKey)
 {
   if (!apiKey) {
     return false;
@@ -294,7 +275,7 @@ static bool checkAPIKey(const YaHTTP::Request& req, const std::unique_ptr<Creden
   return false;
 }
 
-static bool checkWebPassword(const YaHTTP::Request& req, const std::unique_ptr<CredentialsHolder>& password, bool dashboardRequiresAuthentication)
+static bool checkWebPassword(const YaHTTP::Request& req, const std::shared_ptr<const CredentialsHolder>& password, bool dashboardRequiresAuthentication)
 {
   if (!dashboardRequiresAuthentication) {
     return true;
@@ -341,26 +322,26 @@ static bool isAStatsRequest(const YaHTTP::Request& req)
 
 static bool handleAuthorization(const YaHTTP::Request& req)
 {
-  auto config = g_webserverConfig.lock();
+  const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
 
   if (isAStatsRequest(req)) {
-    if (config->statsRequireAuthentication) {
+    if (config.d_statsRequireAuthentication) {
       /* Access to the stats is allowed for both API and Web users */
-      return checkAPIKey(req, config->apiKey) || checkWebPassword(req, config->password, config->dashboardRequiresAuthentication);
+      return checkAPIKey(req, config.d_webAPIKey) || checkWebPassword(req, config.d_webPassword, config.d_dashboardRequiresAuthentication);
     }
     return true;
   }
 
   if (isAnAPIRequest(req)) {
     /* Access to the API requires a valid API key */
-    if (!config->apiRequiresAuthentication || checkAPIKey(req, config->apiKey)) {
+    if (!config.d_apiRequiresAuthentication || checkAPIKey(req, config.d_webAPIKey)) {
       return true;
     }
 
-    return isAnAPIRequestAllowedWithWebAuth(req) && checkWebPassword(req, config->password, config->dashboardRequiresAuthentication);
+    return isAnAPIRequestAllowedWithWebAuth(req) && checkWebPassword(req, config.d_webPassword, config.d_dashboardRequiresAuthentication);
   }
 
-  return checkWebPassword(req, config->password, config->dashboardRequiresAuthentication);
+  return checkWebPassword(req, config.d_webPassword, config.d_dashboardRequiresAuthentication);
 }
 
 static bool isMethodAllowed(const YaHTTP::Request& req)
@@ -385,7 +366,8 @@ static bool isMethodAllowed(const YaHTTP::Request& req)
 
 static bool isClientAllowedByACL(const ComboAddress& remote)
 {
-  return g_webserverConfig.lock()->acl.match(remote);
+  const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
+  return config.d_webServerACL.match(remote);
 }
 
 static void handleCORS(const YaHTTP::Request& req, YaHTTP::Response& resp)
@@ -411,7 +393,7 @@ static void handleCORS(const YaHTTP::Request& req, YaHTTP::Response& resp)
   }
 }
 
-static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional<std::unordered_map<std::string, std::string>>& customHeaders)
+static void addSecurityHeaders(YaHTTP::Response& resp, const std::optional<std::unordered_map<std::string, std::string>>& customHeaders)
 {
   static const std::vector<std::pair<std::string, std::string>> headers = {
     {"X-Content-Type-Options", "nosniff"},
@@ -432,7 +414,7 @@ static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional<std
   }
 }
 
-static void addCustomHeaders(YaHTTP::Response& resp, const boost::optional<std::unordered_map<std::string, std::string>>& customHeaders)
+static void addCustomHeaders(YaHTTP::Response& resp, const std::optional<std::unordered_map<std::string, std::string>>& customHeaders)
 {
   if (!customHeaders) {
     return;
@@ -1453,7 +1435,7 @@ static void handleConfigDump(const YaHTTP::Request& req, YaHTTP::Response& resp)
   std::vector<std::pair<std::string, configentry_t>> configEntries{
     {"acl", runtimeConfiguration.d_ACL.toString()},
     {"allow-empty-response", runtimeConfiguration.d_allowEmptyResponse},
-    {"control-socket", immutableConfig.d_consoleServerAddress.toStringWithPort()},
+    {"control-socket", runtimeConfiguration.d_consoleServerAddress.toStringWithPort()},
     {"ecs-override", runtimeConfiguration.d_ecsOverride},
     {"ecs-source-prefix-v4", static_cast<double>(runtimeConfiguration.d_ECSSourcePrefixV4)},
     {"ecs-source-prefix-v6", static_cast<double>(runtimeConfiguration.d_ECSSourcePrefixV6)},
@@ -1858,10 +1840,9 @@ static void connectionThread(WebClientConnection&& conn)
     resp.version = req.version;
 
     {
-      auto config = g_webserverConfig.lock();
-
-      addCustomHeaders(resp, config->customHeaders);
-      addSecurityHeaders(resp, config->customHeaders);
+      const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
+      addCustomHeaders(resp, config.d_webCustomHeaders);
+      addSecurityHeaders(resp, config.d_webCustomHeaders);
     }
     /* indicate that the connection will be closed after completion of the response */
     resp.headers["Connection"] = "close";
@@ -1926,64 +1907,20 @@ static void connectionThread(WebClientConnection&& conn)
   }
 }
 
-void setWebserverAPIKey(std::unique_ptr<CredentialsHolder>&& apiKey)
-{
-  auto config = g_webserverConfig.lock();
-
-  if (apiKey) {
-    config->apiKey = std::move(apiKey);
-  }
-  else {
-    config->apiKey.reset();
-  }
-}
-
-void setWebserverPassword(std::unique_ptr<CredentialsHolder>&& password)
-{
-  g_webserverConfig.lock()->password = std::move(password);
-}
-
-void setWebserverACL(const std::string& acl)
-{
-  NetmaskGroup newACL;
-  newACL.toMasks(acl);
-
-  g_webserverConfig.lock()->acl = std::move(newACL);
-}
-
-void setWebserverCustomHeaders(const boost::optional<std::unordered_map<std::string, std::string>>& customHeaders)
-{
-  g_webserverConfig.lock()->customHeaders = customHeaders;
-}
-
-void setWebserverStatsRequireAuthentication(bool require)
-{
-  g_webserverConfig.lock()->statsRequireAuthentication = require;
-}
-
-void setWebserverAPIRequiresAuthentication(bool require)
-{
-  g_webserverConfig.lock()->apiRequiresAuthentication = require;
-}
-
-void setWebserverDashboardRequiresAuthentication(bool require)
-{
-  g_webserverConfig.lock()->dashboardRequiresAuthentication = require;
-}
-
-void setWebserverMaxConcurrentConnections(size_t max)
+void setMaxConcurrentConnections(size_t max)
 {
   s_connManager.setMaxConcurrentConnections(max);
 }
 
-void dnsdistWebserverThread(int sock, const ComboAddress& local)
+void WebserverThread(Socket sock)
 {
   setThreadName("dnsdist/webserv");
+  const auto local = *dnsdist::configuration::getCurrentRuntimeConfiguration().d_webServerAddress;
   infolog("Webserver launched on %s", local.toStringWithPort());
 
   {
-    auto config = g_webserverConfig.lock();
-    if (!config->password && config->dashboardRequiresAuthentication) {
+    const auto& config = dnsdist::configuration::getCurrentRuntimeConfiguration();
+    if (!config.d_webPassword && config.d_dashboardRequiresAuthentication) {
       warnlog("Webserver launched on %s without a password set!", local.toStringWithPort());
     }
   }
@@ -1991,7 +1928,7 @@ void dnsdistWebserverThread(int sock, const ComboAddress& local)
   for (;;) {
     try {
       ComboAddress remote(local);
-      int fileDesc = SAccept(sock, remote);
+      int fileDesc = SAccept(sock.getHandle(), remote);
 
       if (!isClientAllowedByACL(remote)) {
         vinfolog("Connection to webserver from client %s is not allowed, closing", remote.toStringWithPort());
@@ -2010,3 +1947,4 @@ void dnsdistWebserverThread(int sock, const ComboAddress& local)
     }
   }
 }
+}
index d707e464c52e71461473aa541460b377e09a89cd..1c76a194f81387517605b9904893d311f1930d29 100644 (file)
@@ -3,20 +3,12 @@
 #include "credentials.hh"
 #include "dnsdist-prometheus.hh"
 
-void setWebserverAPIKey(std::unique_ptr<CredentialsHolder>&& apiKey);
-void setWebserverPassword(std::unique_ptr<CredentialsHolder>&& password);
-void setWebserverACL(const std::string& acl);
-void setWebserverCustomHeaders(const boost::optional<std::unordered_map<std::string, std::string> >& customHeaders);
-void setWebserverAPIRequiresAuthentication(bool);
-void setWebserverDashboardRequiresAuthentication(bool);
-void setWebserverStatsRequireAuthentication(bool);
-void setWebserverMaxConcurrentConnections(size_t);
-
-void dnsdistWebserverThread(int sock, const ComboAddress& local);
-
+namespace dnsdist::webserver
+{
+void WebserverThread(Socket sock);
+void setMaxConcurrentConnections(size_t max);
 void registerBuiltInWebHandlers();
 void clearWebHandlers();
-
-std::string getWebserverConfig();
-
+std::string getConfig();
 bool addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def);
+}
index c2e1e4e192fc501c0edddabd89c16af96deea39a..3eb1360d63ca4dbb81c5af0f0cf2535e63232901 100644 (file)
@@ -2995,7 +2995,7 @@ static void parseParameters(int argc, char** argv, CommandLineParameters& cmdLin
         // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
         exit(EXIT_FAILURE);
       }
-      dnsdist::configuration::updateImmutableConfiguration([&consoleKey](dnsdist::configuration::Configuration& config) {
+      dnsdist::configuration::updateRuntimeConfiguration([&consoleKey](dnsdist::configuration::RuntimeConfiguration& config) {
         config.d_consoleKey = std::move(consoleKey);
       });
     }
@@ -3246,6 +3246,44 @@ static void startFrontends()
 }
 }
 
+struct ListeningSockets
+{
+  Socket d_consoleSocket{-1};
+  Socket d_webServerSocket{-1};
+};
+
+static ListeningSockets initListeningSockets()
+{
+  ListeningSockets result;
+  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
+
+  if (currentConfig.d_consoleEnabled) {
+    const auto& local = currentConfig.d_consoleServerAddress;
+    try {
+      result.d_consoleSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
+      result.d_consoleSocket.bind(local, true);
+      result.d_consoleSocket.listen(5);
+    }
+    catch (const std::exception& exp) {
+      errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), exp.what());
+    }
+  }
+
+  if (currentConfig.d_webServerAddress) {
+    const auto& local = *currentConfig.d_webServerAddress;
+    try {
+      result.d_webServerSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
+      result.d_webServerSocket.bind(local, true);
+      result.d_webServerSocket.listen(5);
+    }
+    catch (const std::exception& exp) {
+      errlog("Unable to bind to web server socket on %s: %s", local.toStringWithPort(), exp.what());
+    }
+  }
+
+  return result;
+}
+
 int main(int argc, char** argv)
 {
   try {
@@ -3297,7 +3335,7 @@ int main(int argc, char** argv)
     if (cmdLine.beClient || !cmdLine.command.empty()) {
       setupLua(*(g_lua.lock()), true, false, cmdLine.config);
       if (clientAddress != ComboAddress()) {
-        dnsdist::configuration::updateImmutableConfiguration([&clientAddress](dnsdist::configuration::Configuration& config) {
+        dnsdist::configuration::updateRuntimeConfiguration([&clientAddress](dnsdist::configuration::RuntimeConfiguration& config) {
           config.d_consoleServerAddress = clientAddress;
         });
       }
@@ -3316,15 +3354,13 @@ int main(int argc, char** argv)
           acl.addMask(addr);
         }
       }
-    });
-
-    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
       for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
         config.d_consoleACL.addMask(mask);
       }
+      config.d_webServerACL.toMasks("127.0.0.1, ::1");
     });
 
-    registerBuiltInWebHandlers();
+    dnsdist::webserver::registerBuiltInWebHandlers();
 
     if (cmdLine.checkConfig) {
       setupLua(*(g_lua.lock()), false, true, cmdLine.config);
@@ -3406,8 +3442,10 @@ int main(int argc, char** argv)
       infolog("Console ACL allowing connections from: %s", acls.c_str());
     }
 
+    auto listeningSockets = initListeningSockets();
+
 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
-    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getImmutableConfiguration().d_consoleKey.empty()) {
+    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) {
       warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
     }
 #endif
@@ -3434,6 +3472,15 @@ int main(int argc, char** argv)
     initDoHWorkers();
 #endif
 
+    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled) {
+      std::thread consoleControlThread(dnsdist::console::controlThread, std::move(listeningSockets.d_consoleSocket));
+      consoleControlThread.detach();
+    }
+    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_webServerAddress) {
+      std::thread webServerThread(dnsdist::webserver::WebserverThread, std::move(listeningSockets.d_webServerSocket));
+      webServerThread.detach();
+    }
+
     for (auto& todoItem : todo) {
       todoItem();
     }
index 03c6a75759ccc9c8048b8ba70f778b1290943db6..41fd39d46afaa9ca60ef5c359e8da29c30d56f47 100644 (file)
@@ -33,7 +33,7 @@
 #include "dnsparser.hh"
 #include "dnswriter.hh"
 
-bool addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def)
+bool dnsdist::webserver::addMetricDefinition(const dnsdist::prometheus::PrometheusMetricDefinition& def)
 {
   return true;
 }