mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 03:32:09 +00:00
Signed-off-by: Federico Di Pierro <nierro92@gmail.com> Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
271 lines
6.0 KiB
C++
271 lines
6.0 KiB
C++
/*
|
|
Copyright (C) 2019 The Falco Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "falco_common.h"
|
|
#include "webserver.h"
|
|
#include "json_evt.h"
|
|
#include "banned.h" // This raises a compilation error when certain functions are used
|
|
|
|
using json = nlohmann::json;
|
|
using namespace std;
|
|
|
|
string k8s_audit_handler::m_k8s_audit_event_source = "k8s_audit";
|
|
|
|
k8s_audit_handler::k8s_audit_handler(falco_engine *engine, falco_outputs *outputs):
|
|
m_engine(engine), m_outputs(outputs)
|
|
{
|
|
}
|
|
|
|
k8s_audit_handler::~k8s_audit_handler()
|
|
{
|
|
}
|
|
|
|
bool k8s_healthz_handler::handleGet(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
const std::string status_body = "{\"status\": \"ok\"}";
|
|
mg_send_http_ok(conn, "application/json", status_body.size());
|
|
mg_printf(conn, "%s", status_body.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool k8s_audit_handler::accept_data(falco_engine *engine,
|
|
falco_outputs *outputs,
|
|
std::string &data,
|
|
std::string &errstr)
|
|
{
|
|
std::list<json_event> jevts;
|
|
json j;
|
|
|
|
try
|
|
{
|
|
j = json::parse(data);
|
|
}
|
|
catch(json::parse_error &e)
|
|
{
|
|
errstr = string("Could not parse data: ") + e.what();
|
|
return false;
|
|
}
|
|
catch(json::out_of_range &e)
|
|
{
|
|
errstr = string("Could not parse data: ") + e.what();
|
|
return false;
|
|
}
|
|
|
|
bool ok;
|
|
try
|
|
{
|
|
ok = falco_k8s_audit::parse_k8s_audit_json(j, jevts);
|
|
}
|
|
catch(json::type_error &e)
|
|
{
|
|
ok = false;
|
|
}
|
|
if(!ok)
|
|
{
|
|
errstr = string("Data not recognized as a k8s audit event");
|
|
return false;
|
|
}
|
|
|
|
for(auto &jev : jevts)
|
|
{
|
|
std::unique_ptr<falco_engine::rule_result> res;
|
|
|
|
try
|
|
{
|
|
res = engine->process_event(m_k8s_audit_event_source, &jev);
|
|
}
|
|
catch(...)
|
|
{
|
|
errstr = string("unkown error processing audit event");
|
|
fprintf(stderr, "%s\n", errstr.c_str());
|
|
return false;
|
|
}
|
|
|
|
if(res)
|
|
{
|
|
try
|
|
{
|
|
outputs->handle_event(res->evt, res->rule,
|
|
res->source, res->priority_num,
|
|
res->format, res->tags);
|
|
}
|
|
catch(falco_exception &e)
|
|
{
|
|
errstr = string("Internal error handling output: ") + e.what();
|
|
fprintf(stderr, "%s\n", errstr.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool k8s_audit_handler::accept_uploaded_data(std::string &post_data, std::string &errstr)
|
|
{
|
|
return k8s_audit_handler::accept_data(m_engine, m_outputs, post_data, errstr);
|
|
}
|
|
|
|
bool k8s_audit_handler::handleGet(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
mg_send_http_error(conn, 405, "GET method not allowed");
|
|
|
|
return true;
|
|
}
|
|
|
|
// The version in CivetServer.cpp has valgrind compliants due to
|
|
// unguarded initialization of c++ string from buffer.
|
|
static void get_post_data(struct mg_connection *conn, std::string &postdata)
|
|
{
|
|
mg_lock_connection(conn);
|
|
char buf[2048];
|
|
int r = mg_read(conn, buf, sizeof(buf));
|
|
while(r > 0)
|
|
{
|
|
postdata.append(buf, r);
|
|
r = mg_read(conn, buf, sizeof(buf));
|
|
}
|
|
mg_unlock_connection(conn);
|
|
}
|
|
|
|
bool k8s_audit_handler::handlePost(CivetServer *server, struct mg_connection *conn)
|
|
{
|
|
// Ensure that the content-type is application/json
|
|
const char *ct = server->getHeader(conn, string("Content-Type"));
|
|
|
|
// content type *must* start with application/json
|
|
if(ct == NULL || strncmp(ct, "application/json", strlen("application/json")) != 0)
|
|
{
|
|
mg_send_http_error(conn, 400, "Wrong Content Type");
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string post_data;
|
|
get_post_data(conn, post_data);
|
|
std::string errstr;
|
|
|
|
if(!accept_uploaded_data(post_data, errstr))
|
|
{
|
|
errstr = "Bad Request: " + errstr;
|
|
mg_send_http_error(conn, 400, "%s", errstr.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
const std::string ok_body = "<html><body>Ok</body></html>";
|
|
mg_send_http_ok(conn, "text/html", ok_body.size());
|
|
mg_printf(conn, "%s", ok_body.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
falco_webserver::falco_webserver():
|
|
m_config(NULL)
|
|
{
|
|
}
|
|
|
|
falco_webserver::~falco_webserver()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
void falco_webserver::init(falco_configuration *config,
|
|
falco_engine *engine,
|
|
falco_outputs *outputs)
|
|
{
|
|
m_config = config;
|
|
m_engine = engine;
|
|
m_outputs = outputs;
|
|
}
|
|
|
|
template<typename T, typename... Args>
|
|
std::unique_ptr<T> make_unique(Args &&... args)
|
|
{
|
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
}
|
|
|
|
void falco_webserver::start()
|
|
{
|
|
if(m_server)
|
|
{
|
|
stop();
|
|
}
|
|
|
|
if(!m_config)
|
|
{
|
|
throw falco_exception("No config provided to webserver");
|
|
}
|
|
|
|
if(!m_engine)
|
|
{
|
|
throw falco_exception("No engine provided to webserver");
|
|
}
|
|
|
|
if(!m_outputs)
|
|
{
|
|
throw falco_exception("No outputs provided to webserver");
|
|
}
|
|
|
|
std::vector<std::string> cpp_options = {
|
|
"num_threads", to_string(1)};
|
|
|
|
if(m_config->m_webserver_ssl_enabled)
|
|
{
|
|
cpp_options.push_back("listening_ports");
|
|
cpp_options.push_back(to_string(m_config->m_webserver_listen_port) + "s");
|
|
cpp_options.push_back("ssl_certificate");
|
|
cpp_options.push_back(m_config->m_webserver_ssl_certificate);
|
|
}
|
|
else
|
|
{
|
|
cpp_options.push_back("listening_ports");
|
|
cpp_options.push_back(to_string(m_config->m_webserver_listen_port));
|
|
}
|
|
|
|
try
|
|
{
|
|
m_server = make_unique<CivetServer>(cpp_options);
|
|
}
|
|
catch(CivetException &e)
|
|
{
|
|
throw falco_exception(std::string("Could not create embedded webserver: ") + e.what());
|
|
}
|
|
if(!m_server->getContext())
|
|
{
|
|
throw falco_exception("Could not create embedded webserver");
|
|
}
|
|
|
|
m_k8s_audit_handler = make_unique<k8s_audit_handler>(m_engine, m_outputs);
|
|
m_server->addHandler(m_config->m_webserver_k8s_audit_endpoint, *m_k8s_audit_handler);
|
|
m_k8s_healthz_handler = make_unique<k8s_healthz_handler>();
|
|
m_server->addHandler(m_config->m_webserver_k8s_healthz_endpoint, *m_k8s_healthz_handler);
|
|
}
|
|
|
|
void falco_webserver::stop()
|
|
{
|
|
if(m_server)
|
|
{
|
|
m_server = NULL;
|
|
m_k8s_audit_handler = NULL;
|
|
m_k8s_healthz_handler = NULL;
|
|
}
|
|
}
|