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
* 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
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;
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)
}
}
-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());
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");
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(;;) {
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) {
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);