From 002decab17b13c01565ce723fe80d39ed09dd760 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 22 Apr 2016 16:42:36 +0200 Subject: [PATCH] dnsdist: Allow the use of custom headers in the web server --- pdns/README-dnsdist.md | 18 +++++++++++++++- pdns/dnsdist-lua.cc | 6 +++--- pdns/dnsdist-web.cc | 48 +++++++++++++++++++++++++++++++++++------- pdns/dnsdist.hh | 2 +- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/pdns/README-dnsdist.md b/pdns/README-dnsdist.md index a004a8ff9..fd838d76c 100644 --- a/pdns/README-dnsdist.md +++ b/pdns/README-dnsdist.md @@ -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 diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index b42a5699d..eb2ac6533 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -1006,7 +1006,7 @@ vector> 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 apiKey) { + g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional apiKey, const boost::optional > customHeaders) { setLuaSideEffect(); if(client) return; @@ -1016,8 +1016,8 @@ vector> 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) diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index e2e47806f..451435731 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -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 >& customHeaders) +{ + static const std::vector > 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 >& 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 >& 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 >& 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) { diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index a7893b38e..f359dd27b 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -511,7 +511,7 @@ std::shared_ptr 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 >&); bool getMsgLen32(int fd, uint32_t* len); bool putMsgLen32(int fd, uint32_t len); void* tcpAcceptorThread(void* p); -- 2.40.0