]> granicus.if.org Git - pdns/commitdiff
dnsdist: Allow the use of custom headers in the web server
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 22 Apr 2016 14:42:36 +0000 (16:42 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 22 Apr 2016 14:46:31 +0000 (16:46 +0200)
pdns/README-dnsdist.md
pdns/dnsdist-lua.cc
pdns/dnsdist-web.cc
pdns/dnsdist.hh

index a004a8ff989af8288b6f838b4a77f6cd5fc05268..fd838d76c7686154430448f32b75b99b0d550603 100644 (file)
@@ -246,6 +246,22 @@ webserver("127.0.0.1:8083", "supersecretpassword", "supersecretAPIkey")
 to the configuration, and point your browser at http://127.0.0.1:8083 and
 log in with any username, and that password. Enjoy!
 
+By default, our web server sends some security-related headers:
+
+ * X-Content-Type-Options: nosniff
+ * X-Frame-Options: deny
+ * X-Permitted-Cross-Domain-Policies: none
+ * X-XSS-Protection: 1; mode=block
+ * Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'
+
+You can override those headers, or add custom headers by using the last parameter to
+`webserver()`. For example, to remove the `X-Frame-Options` header and add a
+`X-Custom` one:
+```
+webserver("127.0.0.1:8080", "supersecret", "apikey", {["X-Frame-Options"]= "", ["X-Custom"]="custom"})
+```
+
+
 Server pools
 ------------
 Now for some cool stuff. Let's say we know we're getting a whole bunch of
@@ -965,7 +981,7 @@ Here are all functions:
  * Practical
     * `shutdown()`: shut down `dnsdist`
     * quit or ^D: exit the console
-    * `webserver(address:port, password [, apiKey])`: launch a webserver with stats on that address with that password
+    * `webserver(address:port, password [, apiKey [, customHeaders ]])`: launch a webserver with stats on that address with that password
  * ACL related:
     * `addACL(netmask)`: add to the ACL set who can use this server
     * `setACL({netmask, netmask})`: replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us
index b42a5699dcebc14c44c1786e973393956dd36b9c..eb2ac6533b5834676b66d1687426152eaa721491 100644 (file)
@@ -1006,7 +1006,7 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
                        g_carbon.setState(ours);
                      });
 
-  g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey) {
+  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) {
       setLuaSideEffect();
       if(client)
        return;
@@ -1016,8 +1016,8 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
        SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
        SBind(sock, local);
        SListen(sock, 5);
-       auto launch=[sock, local, password, apiKey]() {
-         thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "");
+       auto launch=[sock, local, password, apiKey, customHeaders]() {
+         thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "", customHeaders);
          t.detach();
        };
        if(g_launchWork) 
index e2e47806fe89760a5ec6675f88d9610805160e1f..45143573178f3f188d64fd9f8f046258933e2fb9 100644 (file)
@@ -62,7 +62,40 @@ static void handleCORS(YaHTTP::Request& req, YaHTTP::Response& resp)
   }
 }
 
-static void connectionThread(int sock, ComboAddress remote, string password, string apiKey)
+static void addSecurityHeaders(YaHTTP::Response& resp, const boost::optional<std::map<std::string, std::string> >& customHeaders)
+{
+  static const std::vector<std::pair<std::string, std::string> > headers = {
+    { "X-Content-Type-Options", "nosniff" },
+    { "X-Frame-Options", "deny" },
+    { "X-Permitted-Cross-Domain-Policies", "none" },
+    { "X-XSS-Protection", "1; mode=block" },
+    { "Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'" },
+  };
+
+  for (const auto& h : headers) {
+    if (customHeaders) {
+      const auto& custom = customHeaders->find(h.first);
+      if (custom != customHeaders->end()) {
+        continue;
+      }
+    }
+    resp.headers[h.first] = h.second;
+  }
+}
+
+static void addCustomHeaders(YaHTTP::Response& resp, const boost::optional<std::map<std::string, std::string> >& customHeaders)
+{
+  if (!customHeaders)
+    return;
+
+  for (const auto& c : *customHeaders) {
+    if (!c.second.empty()) {
+      resp.headers[c.first] = c.second;
+    }
+  }
+}
+
+static void connectionThread(int sock, ComboAddress remote, string password, string apiKey, const boost::optional<std::map<std::string, std::string> >& customHeaders)
 {
   using namespace json11;
   vinfolog("Webserver handling connection from %s", remote.toStringWithPort());
@@ -89,11 +122,10 @@ static void connectionThread(int sock, ComboAddress remote, string password, str
 
     YaHTTP::Response resp(req);
     const string charset = "; charset=utf-8";
-    resp.headers["X-Content-Type-Options"] = "nosniff";
-    resp.headers["X-Frame-Options"] = "deny";
-    resp.headers["X-Permitted-Cross-Domain-Policies"] = "none";
-    resp.headers["X-XSS-Protection"] = "1; mode=block";
-    resp.headers["Content-Security-Policy"] = "default-src 'self'; style-src 'self' 'unsafe-inline'";
+
+    addCustomHeaders(resp, customHeaders);
+    addSecurityHeaders(resp, customHeaders);
+
     /* no need to send back the API key if any */
     resp.headers.erase("X-API-Key");
 
@@ -372,7 +404,7 @@ static void connectionThread(int sock, ComboAddress remote, string password, str
        fclose(fp);
     }
 }
-void dnsdistWebserverThread(int sock, const ComboAddress& local, const std::string& password, const std::string& apiKey)
+void dnsdistWebserverThread(int sock, const ComboAddress& local, const std::string& password, const std::string& apiKey, const boost::optional<std::map<std::string, std::string> >& customHeaders)
 {
   warnlog("Webserver launched on %s", local.toStringWithPort());
   for(;;) {
@@ -380,7 +412,7 @@ void dnsdistWebserverThread(int sock, const ComboAddress& local, const std::stri
       ComboAddress remote(local);
       int fd = SAccept(sock, remote);
       vinfolog("Got connection from %s", remote.toStringWithPort());
-      std::thread t(connectionThread, fd, remote, password, apiKey);
+      std::thread t(connectionThread, fd, remote, password, apiKey, customHeaders);
       t.detach();
     }
     catch(std::exception& e) {
index a7893b38e57325c65a29c9aa2c8bddde3597d62f..f359dd27b73c8b1a63f864f3078cd7455625e1be 100644 (file)
@@ -511,7 +511,7 @@ std::shared_ptr<DownstreamState> roundrobin(const NumberedServerVector& servers,
 int getEDNSZ(const char* packet, unsigned int len);
 void spoofResponseFromString(DNSQuestion& dq, const string& spoofContent);
 uint16_t getEDNSOptionCode(const char * packet, size_t len);
-void dnsdistWebserverThread(int sock, const ComboAddress& local, const string& password, const string& apiKey);
+void dnsdistWebserverThread(int sock, const ComboAddress& local, const string& password, const string& apiKey, const boost::optional<std::map<std::string, std::string> >&);
 bool getMsgLen32(int fd, uint32_t* len);
 bool putMsgLen32(int fd, uint32_t len);
 void* tcpAcceptorThread(void* p);