]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Add a limit of the number of concurrent connections to auth webserver. 17148/head
authorMiod Vallat <miod.vallat@powerdns.com>
Thu, 16 Apr 2026 06:49:10 +0000 (08:49 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Thu, 16 Apr 2026 07:12:12 +0000 (09:12 +0200)
Signed-off-by: Miod Vallat <miod.vallat@powerdns.com>
docs/http-api/index.rst
docs/settings.rst
pdns/Makefile.am
pdns/auth-main.cc
pdns/ixfrdist-web.cc
pdns/recursordist/ws-recursor.hh
pdns/webserver.cc
pdns/webserver.hh
pdns/ws-auth.cc

index 3325f3459210addf7deca6ae7fdaf762c4e9c03b..a1a490cf88bb0b8f173cfae50f0d9d914342a4d9 100644 (file)
@@ -22,6 +22,7 @@ The following webserver related configuration items are available:
 * :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver (not relevant if :ref:`setting-webserver-address` is set to a UNIX domain socket).
 * :ref:`setting-webserver-max-bodysize`: Maximum request/response body size in megabytes
 * :ref:`setting-webserver-connection-timeout`: Request/response timeout in seconds
+* :ref:`setting-webserver-max-concurrent-connections`: Maximum number of allowed concurrent connections to the web server.
 
 .. warning::
 
index 85e7c9312311dad797f6228e637a17562cd87101..ac6a5a1d865beb0d44d8555bce0df6cd92f9a3ff 100644 (file)
@@ -2176,6 +2176,17 @@ The value between the hooks is a UUID that is generated for each request. This c
 
 Maximum request/response body size in megabytes.
 
+.. _setting-webserver-max-concurrent-connections:
+
+``webserver-max-concurrent-connections``
+----------------------------------------
+.. versionadded:: 5.1.0
+
+-  Integer
+-  Default: 100
+
+Maximum number of allowed concurrent connections to the web server.
+
 .. _setting-webserver-connection-timeout:
 
 ``webserver-connection-timeout``
index bd1db64ff66c71c8499e58e3d6f5c67e1538a486..c2bb7002c2f6b5b6c50d338c17972b63140c9549 100644 (file)
@@ -211,6 +211,7 @@ pdns_server_SOURCES = \
        circular_buffer.hh \
        comment.hh \
        communicator.cc communicator.hh \
+       connection-management.hh \
        coverage.cc coverage.hh \
        credentials.cc credentials.hh \
        dbdnsseckeeper.cc \
@@ -730,6 +731,7 @@ ixfrdist_SOURCES = \
        axfr-retriever.cc \
        base32.cc \
        base64.cc base64.hh \
+       connection-management.hh \
        credentials.cc credentials.hh \
        dns.cc \
        dns_random.hh \
index 6da017a84e542bc7eada993976fd7d6e702e06dc..92218ab0241ace1dd1488c22aad1990f5b8d8bb2 100644 (file)
@@ -249,6 +249,7 @@ static void declareArguments()
   ::arg().set("webserver-allow-from", "Webserver/API access is only allowed from these subnets") = "127.0.0.1,::1";
   ::arg().set("webserver-loglevel", "Amount of logging in the webserver (none, normal, detailed)") = "normal";
   ::arg().set("webserver-max-bodysize", "Webserver/API maximum request/response body size in megabytes") = "2";
+  ::arg().set("webserver-max-concurrent-connections", "Webserver/API maximum concurrent connections allowed") = "100";
   ::arg().set("webserver-connection-timeout", "Webserver/API request/response timeout in seconds") = "5";
   ::arg().setSwitch("webserver-hash-plaintext-credentials", "Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime") = "no";
 
index efbde189b568ab1dae444c3c61f24de57fb11fb9..ec517057465835e035fde530621debec058ea9be 100644 (file)
@@ -26,7 +26,7 @@
 #include "ixfrdist-stats.hh"
 
 IXFRDistWebServer::IXFRDistWebServer(const ComboAddress& listenAddress, const NetmaskGroup& acl, const string& loglevel) :
-  d_ws(std::make_unique<WebServer>(listenAddress.toString(), listenAddress.getPort()))
+  d_ws(std::make_unique<WebServer>(nullptr, listenAddress.toString(), listenAddress.getPort()))
 {
   d_ws->setACL(acl);
   d_ws->setLogLevel(loglevel);
index b622273789c49092c5addb5c4502f76c5a5c4f97..cd99173f7d252517a1fadde424188971e3038b67 100644 (file)
@@ -57,7 +57,7 @@ class AsyncWebServer : public WebServer
 {
 public:
   AsyncWebServer(FDMultiplexer* fdm, const string& listenaddress, int port) :
-    WebServer(listenaddress, port), d_fdm(fdm) {};
+    WebServer(nullptr, listenaddress, port), d_fdm(fdm) {};
   void go();
 
 private:
index 652b86e61966e1e0ffdf3e5b38e87e48fe121f64..a2585a49c64f452a06a05c3daadd072afb6dda53 100644 (file)
@@ -220,9 +220,16 @@ void WebServer::registerWebHandler(const string& url, const HandlerFunction& han
   registerBareHandler(url, func, method);
 }
 
-static void* WebServerConnectionThreadStart(const WebServer* webServer, const std::shared_ptr<Socket>& client)
+static void* WebServerConnectionThreadStart(WebServer* webServer, const std::shared_ptr<Socket>& client)
 {
   setThreadName("rec/webhndlr");
+
+  if (!webServer->registerConnection()) {
+    SLOG(g_log<<Logger::Error<<"Too many concurrent web server connections"<<endl,
+         webServer->d_slog->info(Logr::Error, "Too many concurrent web server connections"));
+    return nullptr;
+  }
+
   const std::string msg = "Exception while serving a connection in main webserver thread";
   try {
     webServer->serveConnection(client);
@@ -239,6 +246,8 @@ static void* WebServerConnectionThreadStart(const WebServer* webServer, const st
     SLOG(g_log<<Logger::Error<<"Unknown exception while serving a connection in main webserver thread"<<endl,
          webServer->d_slog->info(Logr::Error, msg));
   }
+
+  webServer->releaseConnection();
   return nullptr;
 }
 
@@ -504,7 +513,8 @@ bool WebServer::validURL(const YaHTTP::URL& url)
   return isOK;
 }
 
-void WebServer::serveConnection(const std::shared_ptr<Socket>& client) const {
+void WebServer::serveConnection(const std::shared_ptr<Socket>& client) const
+{
   const auto unique = getUniqueID();
   const string logprefix = d_logprefix + to_string(unique) + " ";
 
@@ -587,7 +597,8 @@ void WebServer::serveConnection(const std::shared_ptr<Socket>& client) const {
   }
 }
 
-WebServer::WebServer(string listenaddress, int port) :
+WebServer::WebServer(std::shared_ptr<ConcurrentConnectionManager> ccm, string listenaddress, int port) :
+  d_ccm(ccm),
   d_listenaddress(std::move(listenaddress)),
   d_port(port),
   d_server(nullptr),
index 42fa3aa558662a8c95b21e7eac4317ad05ed5a97..510c90c48cd1a64677631a84ebca9c9221b3331a 100644 (file)
@@ -184,10 +184,12 @@ protected:
   Socket d_server_socket;
 };
 
+#include "connection-management.hh"
+
 class WebServer : public boost::noncopyable
 {
 public:
-  WebServer(string listenaddress, int port);
+  WebServer(std::shared_ptr<ConcurrentConnectionManager> ccm, string listenaddress, int port);
   virtual ~WebServer() = default;
 
   void setSLog(Logr::log_t log)
@@ -237,6 +239,18 @@ public:
   void registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method = "", bool allowPassword=false);
   void registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method = "");
 
+  bool registerConnection() {
+    if (!d_ccm) {
+      return true;
+    }
+    return d_ccm->registerConnection();
+  }
+  void releaseConnection() {
+    if (d_ccm) {
+      d_ccm->releaseConnection();
+    }
+  }
+
   enum class LogLevel : uint8_t {
     None = 0,                // No logs from requests at all
     Normal = 10,             // A "common log format"-like line e.g. '127.0.0.1 "GET /apache_pb.gif HTTP/1.0" 200 2326'
@@ -284,6 +298,7 @@ protected:
   void apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword);
   void webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp);
 
+  std::shared_ptr<ConcurrentConnectionManager> d_ccm;
   string d_listenaddress;
   int d_port;
   std::shared_ptr<Server> d_server;
index f7002ba9f9d5f5eb113aed98885e4ba6c5d5f9e8..b63cd74455fab330239b1373ab493292cd8516cb 100644 (file)
@@ -100,7 +100,7 @@ AuthWebServer::AuthWebServer() :
 
 {
   if (arg().mustDo("webserver") || arg().mustDo("api")) {
-    d_ws = std::make_unique<WebServer>(arg()["webserver-address"], arg().asNum("webserver-port"));
+    d_ws = std::make_unique<WebServer>(std::make_shared<ConcurrentConnectionManager>(arg().asNum("webserver-max-concurrent-connections")), arg()["webserver-address"], arg().asNum("webserver-port"));
     if (g_slogStructured) {
       d_ws->setSLog(g_slog->withName("webserver"));
     }