Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup(userspace/falco)!: improvements to the http output perf. #2602

Merged
merged 2 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
193 changes: 98 additions & 95 deletions userspace/falco/outputs_http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,104 +18,107 @@ 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;
if (!falco::outputs::abstract_output::init(oc, buffered, hostname, json_output, err)) {
return false;
}

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)
{
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
{
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;
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