Skip to content

Commit

Permalink
cleanup(userspace/falco): improvements to the http output perf.
Browse files Browse the repository at this point in the history
Moreover, add option to disable stdout echoing.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
  • Loading branch information
FedeDP authored and poiana committed Aug 31, 2023
1 parent 63ba159 commit acaaa0b
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 101 deletions.
2 changes: 2 additions & 0 deletions falco.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ http_output:
client_cert: "/etc/ssl/certs/client.crt"
# Path to the client key.
client_key: "/etc/ssl/certs/client.key"
# Whether to echo server answers to stdout
echo: false

# [Stable] `program_output`
#
Expand Down
4 changes: 4 additions & 0 deletions userspace/falco/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
insecure = config.get_scalar<bool>("http_output.insecure", false);
http_output.options["insecure"] = insecure? std::string("true") : std::string("false");

bool echo;
echo = config.get_scalar<bool>("http_output.echo", false);
http_output.options["echo"] = echo? std::string("true") : std::string("false");

std::string ca_cert;
ca_cert = config.get_scalar<std::string>("http_output.ca_cert", "");
http_output.options["ca_cert"] = ca_cert;
Expand Down
12 changes: 10 additions & 2 deletions userspace/falco/falco_outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,16 @@ void falco_outputs::add_output(falco::outputs::config oc)
throw falco_exception("Output not supported: " + oc.name);
}

oo->init(oc, m_buffered, m_hostname, m_json_output);
m_outputs.push_back(oo);
std::string init_err;
if (oo->init(oc, m_buffered, m_hostname, m_json_output, init_err))
{
m_outputs.push_back(oo);
}
else
{
falco_logger::log(LOG_ERR, "Failed to init output: " + init_err);
delete(oo);
}
}

void falco_outputs::handle_event(gen_event *evt, std::string &rule, std::string &source,
Expand Down
7 changes: 5 additions & 2 deletions userspace/falco/outputs.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,17 @@ struct message
class abstract_output
{
public:
virtual ~abstract_output() {}
virtual ~abstract_output() = default;

void init(const config& oc, bool buffered, const std::string& hostname, bool json_output)
virtual bool init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err)
{
m_oc = oc;
m_buffered = buffered;
m_hostname = hostname;
m_json_output = json_output;

err = "";
return true;
}

// Return the output's name as per its configuration.
Expand Down
191 changes: 96 additions & 95 deletions userspace/falco/outputs_http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,104 +18,105 @@ limitations under the License.
#include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used

void falco::outputs::output_http::output(const message *msg)
#define CHECK_RES(fn) res = res == CURLE_OK ? fn : res

static size_t noop_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
// We don't want to echo anything. Just return size of bytes ignored
return size * nmemb;
}

bool falco::outputs::output_http::init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err)
{
CURL *curl = NULL;
falco::outputs::abstract_output::init(oc, buffered, hostname, json_output, err);

m_curl = nullptr;
m_http_headers = nullptr;
CURLcode res = CURLE_FAILED_INIT;
struct curl_slist *slist1;
slist1 = NULL;

curl = curl_easy_init();
if(curl)
m_curl = curl_easy_init();
if(!m_curl)
{
if (m_json_output)
{
slist1 = curl_slist_append(slist1, "Content-Type: application/json");
} else {
slist1 = curl_slist_append(slist1, "Content-Type: text/plain");
}
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);

if(res == CURLE_OK)
{
// if the URL is quoted the quotes should be removed to satisfy libcurl expected format
std::string unquotedUrl = m_oc.options["url"];
if (!unquotedUrl.empty() && (
(unquotedUrl.front() == '\"' && unquotedUrl.back() == '\"') ||
(unquotedUrl.front() == '\'' && unquotedUrl.back() == '\'')
))
{
unquotedUrl = libsinsp::filter::unescape_str(unquotedUrl);
}
res = curl_easy_setopt(curl, CURLOPT_URL, unquotedUrl.c_str());
}

if(res == CURLE_OK)
{
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
}

if(res == CURLE_OK)
{
res = curl_easy_setopt(curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str());
}

if(res == CURLE_OK)
{
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
}

if(res == CURLE_OK)
{
if(m_oc.options["insecure"] == std::string("true"))
{
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);

if(res == CURLE_OK)
{
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
}
}

if(res == CURLE_OK)
{
if(m_oc.options["mtls"] == std::string("true"))
{
res = curl_easy_setopt(curl, CURLOPT_SSLCERT, m_oc.options["client_cert"].c_str());

if(res == CURLE_OK)
{
res = curl_easy_setopt(curl, CURLOPT_SSLKEY, m_oc.options["client_key"].c_str());
}
}
}

if(res == CURLE_OK)
{
if (!m_oc.options["ca_cert"].empty())
{
res = curl_easy_setopt(curl, CURLOPT_CAINFO, m_oc.options["ca_cert"].c_str());
}else if(!m_oc.options["ca_bundle"].empty())
{
res = curl_easy_setopt(curl, CURLOPT_CAINFO, m_oc.options["ca_bundle"].c_str());
}else{
res = curl_easy_setopt(curl, CURLOPT_CAPATH, m_oc.options["ca_path"].c_str());
}
}

if(res == CURLE_OK)
{
res = curl_easy_perform(curl);
}

if(res != CURLE_OK)
{
falco_logger::log(LOG_ERR, "libcurl error: " + std::string(curl_easy_strerror(res)));
}
curl_easy_cleanup(curl);
curl = NULL;
curl_slist_free_all(slist1);
slist1 = NULL;
falco_logger::log(LOG_ERR, "libcurl failed to initialize the handle: " + std::string(curl_easy_strerror(res)));
return false;
}
if(m_json_output)
{
m_http_headers = curl_slist_append(m_http_headers, "Content-Type: application/json");
}
else
{
m_http_headers = curl_slist_append(m_http_headers, "Content-Type: text/plain");
}
res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_http_headers);

// if the URL is quoted the quotes should be removed to satisfy libcurl expected format
std::string unquotedUrl = m_oc.options["url"];
if (!unquotedUrl.empty() && (
(unquotedUrl.front() == '\"' && unquotedUrl.back() == '\"') ||
(unquotedUrl.front() == '\'' && unquotedUrl.back() == '\'')
))
{
unquotedUrl = libsinsp::filter::unescape_str(unquotedUrl);
}
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_URL, unquotedUrl.c_str()));

CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_USERAGENT, m_oc.options["user_agent"].c_str()));
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, -1L));

if(m_oc.options["insecure"] == std::string("true"))
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L));
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 0L));
}

if(m_oc.options["mtls"] == std::string("true"))
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLCERT, m_oc.options["client_cert"].c_str()));
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_SSLKEY, m_oc.options["client_key"].c_str()));
}

if (!m_oc.options["ca_cert"].empty())
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_cert"].c_str()));
}
else if(!m_oc.options["ca_bundle"].empty())
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_oc.options["ca_bundle"].c_str()));
}
else
{
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_CAPATH, m_oc.options["ca_path"].c_str()));
}

if(m_oc.options["echo"] == std::string("false"))
{
// If echo==true, libcurl defaults to fwrite to stdout, ie: echoing
CHECK_RES(curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, noop_write_callback));
}

if(res != CURLE_OK)
{
err = "libcurl error: " + std::string(curl_easy_strerror(res));
return false;
}
return true;
}

void falco::outputs::output_http::output(const message *msg)
{
CURLcode res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
CHECK_RES(curl_easy_perform(m_curl));
if(res != CURLE_OK)
{
falco_logger::log(LOG_ERR, "libcurl failed to perform call: " + std::string(curl_easy_strerror(res)));
}
}

void falco::outputs::output_http::cleanup()
{
curl_easy_cleanup(m_curl);
m_curl = nullptr;
curl_slist_free_all(m_http_headers);
m_http_headers = nullptr;
}
8 changes: 7 additions & 1 deletion userspace/falco/outputs_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ namespace outputs

class output_http : public abstract_output
{
void output(const message *msg);
bool init(const config& oc, bool buffered, const std::string& hostname, bool json_output, std::string &err) override;
void output(const message *msg) override;
void cleanup() override;

private:
CURL *m_curl;
struct curl_slist *m_http_headers;
};

} // namespace outputs
Expand Down

0 comments on commit acaaa0b

Please sign in to comment.