g_carbon.setState(ours);
});
- g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password) {
+ g_lua.writeFunction("webserver", [client](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey) {
setLuaSideEffect();
if(client)
return;
SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
SBind(sock, local);
SListen(sock, 5);
- auto launch=[sock, local, password]() {
- thread t(dnsdistWebserverThread, sock, local, password);
+ auto launch=[sock, local, password, apiKey]() {
+ thread t(dnsdistWebserverThread, sock, local, password, apiKey ? *apiKey : "");
t.detach();
};
if(g_launchWork)
#include "base64.hh"
-static bool compareAuthorization(YaHTTP::Request& req, const string &expected_password)
+static bool compareAuthorization(YaHTTP::Request& req, const string &expected_password, const string& expectedApiKey)
{
// validate password
YaHTTP::strstr_map_t::iterator header = req.headers.find("authorization");
// this gets rid of terminating zeros
auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), expected_password.c_str())));
}
+ if (!auth_ok && !expectedApiKey.empty()) {
+ /* if this is a request for the API,
+ check if the API key is correct */
+ if (req.url.path=="/jsonstat" ||
+ req.url.path=="/api/v1/servers/localhost") {
+ header = req.headers.find("x-api-key");
+ if (header != req.headers.end()) {
+ auth_ok = (0==strcmp(header->second.c_str(), expectedApiKey.c_str()));
+ }
+ }
+ }
return auth_ok;
}
if (req.method == "OPTIONS") {
/* Pre-flight request */
resp.headers["Access-Control-Allow-Methods"] = "GET";
- resp.headers["Access-Control-Allow-Headers"] = "Authorization";
+ resp.headers["Access-Control-Allow-Headers"] = "Authorization, X-API-Key";
}
resp.headers["Access-Control-Allow-Origin"] = origin->second;
}
}
-static void connectionThread(int sock, ComboAddress remote, string password)
+static void connectionThread(int sock, ComboAddress remote, string password, string apiKey)
{
using namespace json11;
vinfolog("Webserver handling connection from %s", remote.toStringWithPort());
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'";
+ /* no need to send back the API key if any */
+ resp.headers.erase("X-API-Key");
if(req.method == "OPTIONS") {
/* the OPTIONS method should not require auth, otherwise it breaks CORS */
handleCORS(req, resp);
resp.status=200;
}
- else if (!compareAuthorization(req, password)) {
+ else if (!compareAuthorization(req, password, apiKey)) {
YaHTTP::strstr_map_t::iterator header = req.headers.find("authorization");
if (header != req.headers.end())
errlog("HTTP Request \"%s\" from %s: Web Authentication failed", req.url.path, remote.toStringWithPort());
fclose(fp);
}
}
-void dnsdistWebserverThread(int sock, const ComboAddress& local, const std::string& password)
+void dnsdistWebserverThread(int sock, const ComboAddress& local, const std::string& password, const std::string& apiKey)
{
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);
+ std::thread t(connectionThread, fd, remote, password, apiKey);
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);
+void dnsdistWebserverThread(int sock, const ComboAddress& local, const string& password, const string& apiKey);
bool getMsgLen32(int fd, uint32_t* len);
bool putMsgLen32(int fd, uint32_t len);
void* tcpAcceptorThread(void* p);