Compare commits

...

6 Commits

Author SHA1 Message Date
Mark Stemm
0c3fe8a4e7 This version works
Minimally working version that can link a go program against a so with
the embedded falco engine. Running the program opens the engine for
syscalls and prints any output strings on alert.

It assumes the device already exists and the kernel module is
loaded. Also assumes the lua code is below /user/share--we'll want to
bake that into the shared library.

Lots of memory leaks still, the interface from go to c is still
monolithic, and I had to change the config of openssl crypto and
luajit to compile with -fPIC in order to link into the shared library,
but this version shows its feasible.
2021-08-11 13:16:29 -07:00
Mark Stemm
5787dfa098 Update libs version 2021-08-11 13:15:37 -07:00
Mark Stemm
2434942bdc Example program using embed engine
Not working yet
2021-08-10 16:59:29 -07:00
Mark Stemm
2f7b72d670 Add to cmake 2021-08-10 16:59:06 -07:00
Mark Stemm
50e8da1049 Fix compile errors 2021-08-10 16:57:44 -07:00
Mark Stemm
5c398bd396 Embeddable falco engine experiments
Still WIP/Experimental
2021-08-09 14:51:33 -05:00
8 changed files with 780 additions and 3 deletions

View File

@@ -20,8 +20,8 @@ file(MAKE_DIRECTORY ${FALCOSECURITY_LIBS_CMAKE_WORKING_DIR})
# default below In case you want to test against another falcosecurity/libs version just pass the variable - ie., `cmake
# -DFALCOSECURITY_LIBS_VERSION=dev ..`
if(NOT FALCOSECURITY_LIBS_VERSION)
set(FALCOSECURITY_LIBS_VERSION "17f5df52a7d9ed6bb12d3b1768460def8439936d")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=34a2a466f1e5045591f102de2bc812d9b4f0d5874094cc73b97a7970fb2a3a18")
set(FALCOSECURITY_LIBS_VERSION "new/plugin-system-api-additions")
set(FALCOSECURITY_LIBS_CHECKSUM "SHA256=ba0ea2e22121b8543cb1ebe616090097c4dc3f093db8f0bb5cf2ce5a7e0425a0")
endif()
# cd /path/to/build && cmake /path/to/source

View File

@@ -70,3 +70,5 @@ else()
FILES_MATCHING
PATTERN *.lua)
endif()
add_subdirectory(embeddable)

View File

@@ -0,0 +1,43 @@
#
# Copyright (C) 2021 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.
set(FALCO_ENGINE_EMBEDDABLE_SOURCE_FILES
falco_engine_embeddable.cpp)
set(
FALCO_LIBRARIES
falco_engine
sinsp
"${LIBYAML_LIB}"
"${YAMLCPP_LIB}"
)
add_library(falco_engine_embeddable SHARED ${FALCO_ENGINE_EMBEDDABLE_SOURCE_FILES})
add_dependencies(falco_engine_embeddable falco_engine)
target_include_directories(
falco_engine_embeddable
PUBLIC
"${PROJECT_SOURCE_DIR}/userspace/engine"
"${LUAJIT_INCLUDE}"
"${NJSON_INCLUDE}"
"${TBB_INCLUDE_DIR}"
"${STRING_VIEW_LITE_INCLUDE}"
"${LIBSCAP_INCLUDE_DIRS}"
"${LIBSINSP_INCLUDE_DIRS}"
"${PROJECT_BINARY_DIR}/userspace/engine")
target_link_libraries(falco_engine_embeddable ${FALCO_LIBRARIES})
#add_custom_target(example ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/example)
#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/example COMMAND go build ${CMAKE_CURRENT_SOURCE_DIR}/example.go -o ${CMAKE_CURRENT_BINARY_DIR}/example)

View File

@@ -0,0 +1,102 @@
package main
//#cgo CFLAGS: -I../
//#cgo LDFLAGS: -L/home/mstemm/work/falco-build/userspace/engine/embeddable -lfalco_engine_embeddable -Wl,-rpath=/home/mstemm/work/falco-build/userspace/engine/embeddable
/*
#include "stdio.h"
#include "falco_engine_embeddable.h"
int open_engine(void **engine, void *rules_content)
{
int32_t rc;
*engine = falco_engine_embed_init(&rc);
if (rc != 0)
{
return rc;
}
char *errstr;
rc = falco_engine_embed_load_rules_content(*engine, (const char *) rules_content, &errstr);
if (rc != 0)
{
fprintf(stderr, "%s", errstr);
return rc;
}
rc = falco_engine_embed_open(*engine, &errstr);
if (rc != 0)
{
fprintf(stderr, "%s", errstr);
return rc;
}
return rc;
}
int next_result(void *engine, char **output)
{
int32_t rc;
falco_engine_embed_result *res;
char *errstr;
rc = falco_engine_embed_next_result(engine, &res, &errstr);
if (rc != 0)
{
fprintf(stderr, "NEXT ERROR %s", errstr);
return rc;
}
*output = res->output_str;
return rc;
}
*/
import "C"
import (
"fmt"
"io/ioutil"
"os"
"unsafe"
)
func doMain(rules_filename string) int {
rules_content, err := ioutil.ReadFile(rules_filename)
if err != nil {
fmt.Printf("Could not open rules file %s: %v", rules_filename, err)
return 1
}
var handle unsafe.Pointer
rc := C.open_engine(&handle, C.CBytes(rules_content))
if rc != 0 {
fmt.Printf("Could not open falco engine")
return 1
}
for true {
var output *C.char
rc := C.next_result(handle, &output)
if rc != 0 {
fmt.Printf("Could not get next result")
return 1
}
fmt.Printf("GOT RESULT %s\n", C.GoString(output))
}
return 0
}
func main() {
os.Exit(doMain(os.Args[1]))
}

View File

@@ -0,0 +1,6 @@
module github.com/falcosecurity/falco/embedded/example
go 1.16
require (
)

View File

@@ -0,0 +1,356 @@
/*
Copyright (C) 2021 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 <memory>
#include <atomic>
#include <sinsp.h>
#include <event.h>
#include <falco_engine.h>
#include "falco_engine_embeddable.h"
using namespace std;
class falco_engine_embed_int {
public:
falco_engine_embed_int();
virtual ~falco_engine_embed_int();
bool load_rules_content(const char *rules_content, string &err);
bool is_open();
bool open(string &err);
bool close(string &err);
falco_engine_embed_rc next_result(falco_engine_embed_result **result, string &err);
private:
falco_engine_embed_result *rule_result_to_embed_result(sinsp_evt *ev,
unique_ptr<falco_engine::rule_result> &res);
static void add_output_pair(const string &field, const string &val,
char **&fields, char **&vals,
uint32_t &len);
unique_ptr<sinsp_evt_formatter_cache> m_formatters;
bool m_open;
unique_ptr<sinsp> m_inspector;
unique_ptr<falco_engine> m_falco_engine;
atomic<bool> m_shutdown;
};
falco_engine_embed_int::falco_engine_embed_int()
: m_open(false),
m_shutdown(false)
{
m_inspector.reset(new sinsp());
m_falco_engine.reset(new falco_engine());
m_falco_engine->set_inspector(m_inspector.get());
m_formatters.reset(new sinsp_evt_formatter_cache(m_inspector.get()));
}
falco_engine_embed_int::~falco_engine_embed_int()
{
}
bool falco_engine_embed_int::load_rules_content(const char *rules_content, string &err)
{
bool verbose = false;
bool all_events = true;
try {
m_falco_engine->load_rules(string(rules_content), verbose, all_events);
}
catch(falco_exception &e)
{
err = e.what();
return false;
}
return true;
}
bool falco_engine_embed_int::is_open()
{
return m_open;
}
bool falco_engine_embed_int::open(string &err)
{
try {
m_inspector->open();
}
catch(exception &e)
{
err = e.what();
return false;
}
m_open = true;
return true;
}
bool falco_engine_embed_int::close(string &err)
{
m_shutdown = true;
m_open = false;
return true;
}
falco_engine_embed_rc falco_engine_embed_int::next_result(falco_engine_embed_result **result, string &err)
{
*result = NULL;
while(!m_shutdown)
{
sinsp_evt* ev;
int32_t rc = m_inspector->next(&ev);
if (rc == SCAP_TIMEOUT)
{
continue;
}
else if (rc == SCAP_EOF)
{
break;
}
else if (rc != SCAP_SUCCESS)
{
err = m_inspector->getlasterr();
return FE_EMB_RC_ERROR;
}
if(!ev->simple_consumer_consider())
{
continue;
}
unique_ptr<falco_engine::rule_result> res = m_falco_engine->process_sinsp_event(ev);
if(!res)
{
continue;
}
*result = rule_result_to_embed_result(ev, res);
return FE_EMB_RC_OK;
}
// Can only get here if shut down/eof.
return FE_EMB_RC_EOF;
}
falco_engine_embed_result * falco_engine_embed_int::rule_result_to_embed_result(sinsp_evt *ev,
unique_ptr<falco_engine::rule_result> &res)
{
falco_engine_embed_result *result;
result = (falco_engine_embed_result *) malloc(sizeof(falco_engine_embed_result));
result->rule = strdup(res->rule.c_str());
result->event_source = strdup(res->source.c_str());
result->priority_num = res->priority_num;
// Copy output format string without resolving fields.
result->output_format_str = strdup(res->format.c_str());
// Resolve output format string into resolved output
string output;
m_formatters->tostring(ev, res->format, &output);
result->output_str = strdup(output.c_str());
result->output_fields = NULL;
result->output_values = NULL;
result->num_output_values = 0;
map<string, string> rule_output_fields;
m_formatters->resolve_tokens(ev, res->format, rule_output_fields);
for(auto &pair : rule_output_fields)
{
add_output_pair(pair.first, pair.second,
result->output_fields, result->output_values,
result->num_output_values);
}
// Preceding * makes the formatting permissive (not ending at first empty value)
std::string exformat = "*";
for (const auto& exfield : res->exception_fields)
{
exformat += " %" + exfield;
}
map<string, string> exception_output_fields;
m_formatters->resolve_tokens(ev, exformat, exception_output_fields);
for(auto &pair : exception_output_fields)
{
add_output_pair(pair.first, pair.second,
result->output_fields, result->output_values,
result->num_output_values);
}
return result;
}
void falco_engine_embed_int::add_output_pair(const string &field, const string &val,
char **&fields, char **&vals,
uint32_t &len)
{
len++;
fields = (char **) realloc(fields, len*sizeof(char *));
vals = (char **) realloc(vals, len*sizeof(char *));
fields[len-1] = strdup(field.c_str());
vals[len-1] = strdup(val.c_str());
}
static const char *FALCO_ENGINE_EMBED_VERSION = "1.0.0";
char *falco_engine_embed_get_version()
{
return strdup(FALCO_ENGINE_EMBED_VERSION);
}
void falco_engine_embed_free_result(falco_engine_embed_result *result)
{
free(result->rule);
free(result->event_source);
free(result->output_format_str);
free(result->output_str);
for(int32_t i; i < result->num_output_values; i++)
{
free(result->output_fields[i]);
free(result->output_values[i]);
}
free(result->output_fields);
free(result->output_values);
free(result);
}
falco_engine_embed_t* falco_engine_embed_init(int32_t *rc)
{
falco_engine_embed_int *eengine = new falco_engine_embed_int();
*rc = FE_EMB_RC_OK;
return eengine;
}
int32_t falco_engine_embed_destroy(falco_engine_embed_t *engine, char *errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
if(eengine->is_open())
{
errstr = strdup("Engine is open--must call close() first");
return FE_EMB_RC_ERROR;
}
delete(eengine);
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_load_plugin(falco_engine_embed_t *engine,
const char *path,
const char* init_config,
const char* open_params,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
// XXX/mstemm fill in
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_load_rules_content(falco_engine_embed_t *engine,
const char *rules_content,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
std::string err;
if (!eengine->load_rules_content(rules_content, err))
{
*errstr = strdup(err.c_str());
return FE_EMB_RC_ERROR;
}
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_enable_source(falco_engine_embed_t *engine,
int32_t source,
bool enabled,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
// XXX/mstemm fill in
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_open(falco_engine_embed_t *engine,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
std::string err;
if (!eengine->open(err))
{
*errstr = strdup(err.c_str());
return FE_EMB_RC_ERROR;
}
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_close(falco_engine_embed_t *engine,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
std::string err;
if (!eengine->close(err))
{
*errstr = strdup(err.c_str());
return FE_EMB_RC_ERROR;
}
return FE_EMB_RC_OK;
}
int32_t falco_engine_embed_next_result(falco_engine_embed_t *engine,
falco_engine_embed_result **result,
char **errstr)
{
falco_engine_embed_int *eengine = (falco_engine_embed_int *) engine;
std::string err;
falco_engine_embed_rc rc;
rc = eengine->next_result(result, err);
if(rc == FE_EMB_RC_ERROR)
{
*errstr = strdup(err.c_str());
}
return rc;
}

View File

@@ -0,0 +1,268 @@
/*
Copyright (C) 2021 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.
*/
/*
This header file provides a C-only interface to the falco engine,
suitable for embedding in other programs as a shared library. This
interface handles:
- Loading Rules Content
- Enabling/Disabling syscall/k8s_audit event sources.
- Loading and configuring source/extractor plugins
- Starting/Stopping the event processing loop.
After setup, the main interface involves receiving "results" when
syscall/k8s_audit/plugin events match rules.
This interface does not provide as many features as the c++
falco_engine interface, such as interfaces to list rules, segregate
rules by "ruleset", enabling/disabling specific rules etc.
Output handling (e.g. routing alerts to files, stdout, webhook,
slack, etc) is not covered by this interface. After receiving a
result, a program could use a program like falcosidekick for a rich
set of output handling methods.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* A handle to an embeddable falco engine */
typedef void falco_engine_embed_t;
/* Defined return values from API functions. */
enum falco_engine_embed_rc
{
/* No Error */
FE_EMB_RC_OK = 0,
FE_EMB_RC_ERROR = 1,
FE_EMB_RC_EOF = 2,
};
/* Defined event sources. */
enum falco_engine_embed_evt_source
{
FE_EMB_SRC_NONE = 0,
FE_EMB_SRC_SYSCALL = 1,
FE_EMB_K8S_AUDIT = 2,
FE_EMB_PLUGINS = 3, // This includes any event from any plugin
};
/* Represents a result (e.g. an event matching a falco rule)
When returned by a call to next_result(), the struct, as well as
every allocated char * within the struct, is allocated via a call
to malloc() and must be freed via a call to free().
*/
typedef struct falco_engine_embed_result
{
// The rule that matched the event
char *rule;
// The event source of the event that matched the rule
char *event_source;
// An int containing a falco_common::priority_type value of
// the priority of the matching rule.
int32_t priority_num;
// A copy of the rule's output string, *without* any
// fields (e.g. %proc.name, ...) resolved to values.
char *output_format_str;
// An output string, starting with the rule's output string
// with all fields resolved to values.
char *output_str;
// An allocated array of allocated field names from the output
// string. Additional fields + values may be included in
// addition to those in the output string, to aid in
// debugging. Item i in this array maps to item i in
// output_values.
char **output_fields;
// An allocated array of allocated field values from the
// output string. Additional fields + values may be included in
// addition to those in the output string, to aid in
// debugging. Item i in this array maps to item i in
// output_fields.
char **output_values;
// The length of output_fields/output_values
uint32_t num_output_values;
} falco_engine_embed_result;
/* A utility function to free a falco_engine_embed_result struct and
* its allocated strings returned by a call to next_result() */
void falco_engine_embed_free_result(falco_engine_embed_result *result);
// Interface to interact with an embeddable falco engine.
// NOTE: For all functions below that return a char *, the memory
// pointed to by the char * is allocated using malloc() and should be
// freed by the caller using free().
// Return the embedded engine version.
//
// Return value: a version string, in the following format:
// "<major>.<minor>.<patch>", e.g. "1.2.3".
// This interface is compatible following semver conventions:
// <major> changes for incompatible api changes, <minor> for
// backwards-compatible additions, <patch> for compatible bug
// fixes.
char* falco_engine_embed_get_version();
// Initialize a falco engine.
//
// Arguments:
// - rc: pointer to an integer containing a falco_engine_embed_rc value.
//
// Return value: pointer to the engine state that is passed to
// other API functions.
falco_engine_embed_t* falco_engine_embed_init(int32_t *rc);
// Destroy a falco engine. This frees any resources allocated in
// init(). If open() has been called, close() should be called before
// destroy().
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_destroy(falco_engine_embed_t *engine, char *errstr);
// Load either a falco source or extractor plugin.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - path: a file path pointing to a dynamic library that
// can be dlopen()ed.
// - init_config: a string that will be passed to the plugin's
// init() function.
// - open_params: a string that will be passed to the
// plugin's open() function.
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_load_plugin(falco_engine_embed_t *engine,
const char *path,
const char* init_config,
const char* open_params,
char **errstr);
// Load the provided rules content. These rules are applied on
// top of any previously loaded rules content
// (e.g. appending/overriding rule/macro/list objects as
// specified via "append:" properties)
//
// NOTE: Plugins should be loaded before any rules are loaded.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - rules_content: a null-terminated string containing
// yaml rules content.
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_load_rules_content(falco_engine_embed_t *engine,
const char *rules_content,
char **errstr);
// Enable/disable an event source.
// By default all event sources are enabled. This function
// enables/disables specific event sources.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - source: an int containing a falco_engine_embed_evt_source value.
// - enabled: whether to enable or disable the provided source
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_enable_source(falco_engine_embed_t *engine,
int32_t source,
bool enabled,
char **errstr);
// Open the engine, which starts event processing and matching
// against the loaded set of rules.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_open(falco_engine_embed_t *engine,
char **errstr);
// Close the engine, which stops event processing.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_close(falco_engine_embed_t *engine,
char **errstr);
// Receive the next result (e.g. an event that matched a
// rule). This function blocks until the next result is
// available. close() is called, or an error occurs.
//
// Arguments:
// - engine: returned by a prior succesful call to init().
// - result: a pointer to a falco_engine_embed_result struct
// pointer. On success, a struct will be allocated, and filled in
// with allocated char* values, and the pointer updated to point to
// the allocated struct.
// - errstr: on error, errstr will point to an allocated
// string with additional details on the errror. The string
// must be freed via a call to free().
//
// Return value: an integer containing a falco_engine_embed_rc
// value.
int32_t falco_engine_embed_next_result(falco_engine_embed_t *engine,
falco_engine_embed_result **result,
char **errstr);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -21,4 +21,4 @@ limitations under the License.
// This is the result of running "falco --list -N | sha256sum" and
// represents the fields supported by this version of falco. It's used
// at build time to detect a changed set of fields.
#define FALCO_FIELDS_CHECKSUM "2f324e2e66d4b423f53600e7e0fcf2f0ff72e4a87755c490f2ae8f310aba9386"
#define FALCO_FIELDS_CHECKSUM "8183621f52451d842036eee409e2ed920d9c91bab33e0c4a44e4a871378d103f"