From 080935977e7cd9ee63e6613bbd4ff0ce98bfa686 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Tue, 3 Dec 2024 16:35:36 +0100 Subject: [PATCH] fix(src,cmake,go-worker): multiple fixes. First of all, podman had a runtime dep on `gpgme` (`libgpgme-dev`). Install it through vcpkg (as static) to avoid linking issues for the library consumer. Second, in go-worker Container struct, omit empty structured fields (slices, maps...), to avoid wasting memory space (plus all the back and forth copies). Finally, fixed some bugs all around the C++ implementation. Signed-off-by: Federico Di Pierro --- .github/workflows/ci.yml | 4 +- CMakeLists.txt | 6 ++- Makefile | 2 + go-worker/pkg/container/engine.go | 10 ++-- src/container_info_json.cpp | 65 ++++++++++++----------- src/container_type.h | 12 ++--- src/extract.cpp | 25 +++------ src/listening.cpp | 3 +- src/parse.cpp | 87 +++++++++++++++---------------- vcpkg.json | 3 +- 10 files changed, 106 insertions(+), 111 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8979652..698be9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,10 +28,10 @@ jobs: fetch-depth: 0 submodules: 'recursive' - # Needed by containerd go package + # Needed by containerd go package - build time dep, no runtime. - name: Install deps run: - sudo apt-get install -y --no-install-recommends libbtrfs-dev libgpgme-dev + sudo apt-get install -y --no-install-recommends libbtrfs-dev - name: Setup Go uses: actions/setup-go@v5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 893c7ed..75a1a2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ include(plugin-sdk-cpp) # vcpkg dependencies find_package(re2 REQUIRED) find_package(spdlog CONFIG REQUIRED) +## gpgme is vcpkg installed but comes with pkg-config +## It is a runtime dep of podman go package. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GPGME REQUIRED gpgme IMPORTED_TARGET) file(GLOB_RECURSE SOURCES src/*.cpp) @@ -47,4 +51,4 @@ target_compile_features(container PUBLIC cxx_std_17) target_include_directories(container PRIVATE ${PLUGIN_SDK_INCLUDE} ${PLUGIN_SDK_DEPS_INCLUDE} ${WORKER_INCLUDE}) # project linked libraries -target_link_libraries(container PRIVATE spdlog::spdlog_header_only re2::re2 ${WORKER_LIB}) +target_link_libraries(container PRIVATE spdlog::spdlog_header_only re2::re2 PkgConfig::GPGME ${WORKER_LIB}) \ No newline at end of file diff --git a/Makefile b/Makefile index 19e63ac..f9b1fe5 100644 --- a/Makefile +++ b/Makefile @@ -18,11 +18,13 @@ OUTPUT := lib$(NAME).so all: $(OUTPUT) +.PHONY: clean clean: rm -rf build $(OUTPUT) make -C go-worker/ clean # This Makefile requires CMake installed on the system +.PHONY: $(OUTPUT) $(OUTPUT): cmake -B build -S . && make -C build/ container -j6 && cp build/$(OUTPUT) $(OUTPUT) diff --git a/go-worker/pkg/container/engine.go b/go-worker/pkg/container/engine.go index c9adc72..700e8c8 100644 --- a/go-worker/pkg/container/engine.go +++ b/go-worker/pkg/container/engine.go @@ -200,21 +200,21 @@ type Container struct { CPUShares int64 `json:"cpu_shares"` CPUSetCPUCount int64 `json:"cpuset_cpu_count"` CreatedTime int64 `json:"created_time"` - Env []string `json:"env"` + Env []string `json:"env,omitempty"` FullID string `json:"full_id"` HostIPC bool `json:"host_ipc"` HostNetwork bool `json:"host_network"` HostPID bool `json:"host_pid"` Ip string `json:"ip"` IsPodSandbox bool `json:"is_pod_sandbox"` - Labels map[string]string `json:"labels"` + Labels map[string]string `json:"labels,omitempty"` MemoryLimit int64 `json:"memory_limit"` SwapLimit int64 `json:"swap_limit"` PodSandboxID string `json:"pod_sandbox_id"` // cri only Privileged bool `json:"privileged"` - PodSandboxLabels map[string]string `json:"pod_sandbox_labels"` // cri only - PortMappings []portMapping `json:"port_mappings"` - Mounts []mount `json:"Mounts"` + PodSandboxLabels map[string]string `json:"pod_sandbox_labels,omitempty"` // cri only + PortMappings []portMapping `json:"port_mappings,omitempty"` + Mounts []mount `json:"Mounts,omitempty"` HealthcheckProbe *probe `json:"Healthcheck,omitempty"` LivenessProbe *probe `json:"LivenessProbe,omitempty"` ReadinessProbe *probe `json:"ReadinessProbe,omitempty"` diff --git a/src/container_info_json.cpp b/src/container_info_json.cpp index d21327a..bce94a9 100644 --- a/src/container_info_json.cpp +++ b/src/container_info_json.cpp @@ -70,46 +70,49 @@ void from_json(const nlohmann::json& j, container_port_mapping& port) { } void from_json(const nlohmann::json& j, std::shared_ptr& cinfo) { + std::shared_ptr info = std::make_shared(); const nlohmann::json& container = j["container"]; - cinfo->m_type = container.value("type", CT_UNKNOWN); - cinfo->m_id = container.value("id", ""); - cinfo->m_name = container.value("name", ""); - cinfo->m_image = container.value("image", ""); - cinfo->m_imagedigest = container.value("imagedigest", ""); - cinfo->m_imageid = container.value("imageid", ""); - cinfo->m_imagerepo = container.value("imagerepo", ""); - cinfo->m_imagetag = container.value("imagetag", ""); - cinfo->m_container_user = container.value("User", ""); - cinfo->m_pod_sandbox_cniresult = container.value("cni_json", ""); - cinfo->m_cpu_period = container.value("cpu_period", 0); - cinfo->m_cpu_quota = container.value("cpu_quota", 0); - cinfo->m_cpu_shares = container.value("cpu_shares", 0); - cinfo->m_cpuset_cpu_count = container.value("cpuset_cpu_count", 0); - cinfo->m_created_time = container.value("created_time", 0); - cinfo->m_env = container.value("env", std::vector{}); - cinfo->m_full_id = container.value("full_id", ""); - cinfo->m_host_ipc = container.value("host_ipc", false); - cinfo->m_host_network = container.value("host_network", false); - cinfo->m_host_pid = container.value("host_pid", false); - cinfo->m_container_ip = container.value("ip", 0); - cinfo->m_is_pod_sandbox = container.value("is_pod_sandbox", false); - cinfo->m_labels = container.value("labels", std::map{}); - cinfo->m_memory_limit = container.value("memory_limit", 0); - cinfo->m_swap_limit = container.value("swap_limit", 0); - cinfo->m_pod_sandbox_id = container.value("pod_sandbox_id", ""); - cinfo->m_privileged = container.value("privileged", false); - cinfo->m_pod_sandbox_labels = container.value("pod_sandbox_labels", std::map{}); - cinfo->m_port_mappings = container.value("port_mappings", std::vector{}); - cinfo->m_mounts = container.value("Mounts", std::vector{}); + info->m_type = container.value("type", CT_UNKNOWN); + info->m_id = container.value("id", ""); + info->m_name = container.value("name", ""); + info->m_image = container.value("image", ""); + info->m_imagedigest = container.value("imagedigest", ""); + info->m_imageid = container.value("imageid", ""); + info->m_imagerepo = container.value("imagerepo", ""); + info->m_imagetag = container.value("imagetag", ""); + info->m_container_user = container.value("User", ""); + info->m_pod_sandbox_cniresult = container.value("cni_json", ""); + info->m_cpu_period = container.value("cpu_period", 0); + info->m_cpu_quota = container.value("cpu_quota", 0); + info->m_cpu_shares = container.value("cpu_shares", 0); + info->m_cpuset_cpu_count = container.value("cpuset_cpu_count", 0); + info->m_created_time = container.value("created_time", 0); + info->m_env = container.value("env", std::vector{}); + info->m_full_id = container.value("full_id", ""); + info->m_host_ipc = container.value("host_ipc", false); + info->m_host_network = container.value("host_network", false); + info->m_host_pid = container.value("host_pid", false); + info->m_container_ip = container.value("ip", ""); + info->m_is_pod_sandbox = container.value("is_pod_sandbox", false); + info->m_labels = container.value("labels", std::map{}); + info->m_memory_limit = container.value("memory_limit", 0); + info->m_swap_limit = container.value("swap_limit", 0); + info->m_pod_sandbox_id = container.value("pod_sandbox_id", ""); + info->m_privileged = container.value("privileged", false); + info->m_pod_sandbox_labels = container.value("pod_sandbox_labels", std::map{}); + info->m_port_mappings = container.value("port_mappings", std::vector{}); + info->m_mounts = container.value("Mounts", std::vector{}); for (int probe_type = container_health_probe::PT_HEALTHCHECK; probe_type <= container_health_probe::PT_READINESS_PROBE; probe_type++) { const auto& probe_name = container_health_probe::probe_type_names[probe_type]; if (container.contains(probe_name)) { container_health_probe probe = container.value(probe_name, container_health_probe()); probe.m_type = container_health_probe::probe_type(probe_type); - cinfo->m_health_probes.push_back(probe); + info->m_health_probes.push_back(probe); } } + + cinfo = info; } void to_json(nlohmann::json& j, const container_mount_info& mount) { diff --git a/src/container_type.h b/src/container_type.h index ae5dcee..99a3ef0 100644 --- a/src/container_type.h +++ b/src/container_type.h @@ -1,18 +1,18 @@ #pragma once enum container_type { - CT_DOCKER = 0, // TODO implement matcher + CT_DOCKER = 0, CT_LXC = 1, CT_LIBVIRT_LXC = 2, CT_MESOS = 3, // deprecated CT_RKT = 4, // deprecated CT_CUSTOM = 5, - CT_CRI = 6, // TODO implement matcher - CT_CONTAINERD = 7, // TODO implement matcher - CT_CRIO = 8, // TODO implement matcher + CT_CRI = 6, + CT_CONTAINERD = 7, + CT_CRIO = 8, CT_BPM = 9, - CT_STATIC = 10, // TODO implement matcher - CT_PODMAN = 11, // TODO implement matcher + CT_STATIC = 10, + CT_PODMAN = 11, // Default value, may be changed if necessary CT_UNKNOWN = 0xffff diff --git a/src/extract.cpp b/src/extract.cpp index 356f441..3796d83 100644 --- a/src/extract.cpp +++ b/src/extract.cpp @@ -334,20 +334,12 @@ static inline void concatenate_container_labels(const std::mapsecond; auto& req = in.get_extract_request(); const auto field_id = req.get_field_id(); - switch(field_id) - { + switch(field_id) { case TYPE_CONTAINER_ID: req.set_value(cinfo->m_id); break; diff --git a/src/listening.cpp b/src/listening.cpp index a5c88db..ee7c6b3 100644 --- a/src/listening.cpp +++ b/src/listening.cpp @@ -14,8 +14,7 @@ bool my_plugin::capture_open(const falcosecurity::capture_listen_input& in) { { std::shared_ptr info = nullptr; auto container_id = compute_container_id_for_thread(e, tr, info); - m_container_id_field.write_value(tw, e, - (const char*)container_id.c_str()); + m_container_id_field.write_value(tw, e, container_id); return true; }); return true; diff --git a/src/parse.cpp b/src/parse.cpp index b556e69..0ee8435 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -49,8 +49,7 @@ bool my_plugin::parse_async_event( falcosecurity::events::asyncevent_e_decoder ad(evt); bool added = std::strcmp(ad.get_name(), ASYNC_EVENT_NAME_ADDED) == 0; bool removed = std::strcmp(ad.get_name(), ASYNC_EVENT_NAME_REMOVED) == 0; - if(!added && !removed) - { + if(!added && !removed) { // We are not interested in parsing async events that are not // generated by our plugin. // This is not an error, it could happen when we have more than one @@ -60,14 +59,12 @@ bool my_plugin::parse_async_event( uint32_t json_charbuf_len = 0; char* json_charbuf_pointer = (char*)ad.get_data(json_charbuf_len); - if(json_charbuf_pointer == nullptr) - { + if(json_charbuf_pointer == nullptr) { m_lasterr = "there is no payload in the async event"; SPDLOG_ERROR(m_lasterr); return false; } - auto json_event = nlohmann::json::parse(std::string(json_charbuf_pointer)); - + auto json_event = nlohmann::json::parse(json_charbuf_pointer); auto cinfo = json_event.get>(); if (added) { m_containers[cinfo->m_id] = cinfo; @@ -138,15 +135,18 @@ std::string my_plugin::compute_container_id_for_thread(const falcosecurity::tabl tr, [&](const falcosecurity::table_entry& e) { + if (!container_id.empty()) { + // Nothing more to do. + // Note: returning false would raise an exception + // instead of stopping the loop :/ + return true; + } // read the "second" field (aka: the cgroup path) // from the current entry of the cgroups table std::string cgroup; m_cgroups_field_second.read_value(tr, e, cgroup); - if(!cgroup.empty()) { - if (m_mgr->match_cgroup(cgroup, container_id, info)) { - return false; // stop iterating - } + m_mgr->match_cgroup(cgroup, container_id, info); } return true; } @@ -224,47 +224,44 @@ void my_plugin::write_thread_category(const falcosecurity::table_entry& thread_e bool my_plugin::parse_new_process_event( const falcosecurity::parse_event_input& in) { // get tid - int64_t thread_id = in.get_event_reader().get_tid(); - if(thread_id <= 0) - { - SPDLOG_INFO("unknown thread id for event num '{}' with type '{}'", - in.get_event_reader().get_num(), - int32_t(in.get_event_reader().get_type())); - return false; - } + auto thread_id = in.get_event_reader().get_tid(); // compute container_id from tid->cgroups auto& tr = in.get_table_reader(); // retrieve the thread entry associated with this thread id - auto thread_entry = m_threads_table.get_entry(tr, thread_id); - - std::shared_ptr info = nullptr; - auto container_id = compute_container_id_for_thread(thread_entry, tr, info); - - // store container_id - auto& tw = in.get_table_writer(); - m_container_id_field.write_value(tw, thread_entry, - (const char*)container_id.c_str()); - - if (info != nullptr) { - // Since the matcher also returned a container_info, - // it means we do not expect to receive any metadata from the go-worker, - // since the engine has no listener SDK. - // Just send the event now. - nlohmann::json j(info); - generate_async_event(j.dump().c_str(), true, ASYNC_HANDLER_DEFAULT); - - // Immediately cache the container metadata - m_containers[info->m_id] = info; - } - - // Write thread category field - if (container_id != HOST_CONTAINER_ID) { - write_thread_category(thread_entry, tr, tw); + try { + auto thread_entry = m_threads_table.get_entry(tr, thread_id); + + std::shared_ptr info = nullptr; + auto container_id = compute_container_id_for_thread(thread_entry, tr, info); + + // store container_id + auto& tw = in.get_table_writer(); + m_container_id_field.write_value(tw, thread_entry, container_id); + + if (info != nullptr) { + // Since the matcher also returned a container_info, + // it means we do not expect to receive any metadata from the go-worker, + // since the engine has no listener SDK. + // Just send the event now. + nlohmann::json j(info); + generate_async_event(j.dump().c_str(), true, ASYNC_HANDLER_DEFAULT); + + // Immediately cache the container metadata + m_containers[info->m_id] = info; + } + + // Write thread category field + if (container_id != HOST_CONTAINER_ID) { + write_thread_category(thread_entry, tr, tw); + } + return true; + } catch (falcosecurity::plugin_exception &e) { + SPDLOG_ERROR("cannot attach container_id to new process event for the thread id '{}': {}", + thread_id, e.what()); + return false; } - - return true; } bool my_plugin::parse_event(const falcosecurity::parse_event_input& in) { diff --git a/vcpkg.json b/vcpkg.json index d2b2c9d..1669a14 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,6 +4,7 @@ "license": "MIT", "dependencies": [ "re2", - "spdlog" + "spdlog", + "gpgme" ] }