Compare commits

..

19 Commits

Author SHA1 Message Date
Lorenzo Fontana
38dbf28057 chore: comment out again rules validation for now
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:50:47 +01:00
Lorenzo Fontana
3239c16391 update(userspace/libhawk): further explaination for rules provider
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:52 +01:00
Lorenzo Fontana
549a4c3041 update(userspace/libhawk): watch rules transactional pattern documentation
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:52 +01:00
Lorenzo Fontana
6e7256569c update(userspace): transactional interface for engine updates
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:52 +01:00
Lorenzo Fontana
c1805281ac update: rules provider configuration
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:52 +01:00
Lorenzo Fontana
90c890bc2a update: extension loading mechanism
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:51 +01:00
Lorenzo Fontana
24e2d175f0 update: library management code
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:51 +01:00
Lorenzo Fontana
4ccbd9d194 update: initial library loader
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:51 +01:00
Lorenzo Fontana
1957bc75b7 update(userspace/engine): copy constructor for falco_engine
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:51 +01:00
Lorenzo Fontana
f8c10f6c27 update(userspace): atomic falco engine delete previous instance
Co-Authored-By: Leonardo Grasso <me@leonardograsso.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:51 +01:00
Lorenzo Fontana
b90164bac1 update(userspace): engine atomic and express lifecycle with a namespace
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:49 +01:00
Lorenzo Fontana
538e7286bc new(userspace): libhawk lifecycle destruction management
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:32 +01:00
Lorenzo Fontana
344b8c002c new(userspace): initial lifecycle implementation
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:32 +01:00
Lorenzo Fontana
f87e6f1871 chore: restore the classic flags for a future refactor for the new
functionalities

Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:49:31 +01:00
Lorenzo Fontana
cd71f62f04 update(userspace): pointer to pointer in hawk_engine rules_cb
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2021-01-21 10:48:29 +01:00
Leonardo Di Donato
458f7ccd3b new(userspace): use conditional_variable to signal when we have a
ready-to-use (with rules) falco engine

Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2021-01-21 10:47:35 +01:00
Leonardo Di Donato
305cb62162 new(userspace/falco): destroy rules watcher when needed
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2021-01-21 10:45:59 +01:00
Leonardo Di Donato
1a5a55a002 new(userspace): make hawk_watch_rules aware of the engine
Co-authored-by: Lorenzo Fontana <lo@linux.com>
Signed-off-by: Leonardo Di Donato <leodidonato@gmail.com>
2021-01-21 10:45:57 +01:00
Lorenzo Fontana
387908d075 new(userspace): initial draft for libhawk
Co-Authored-By: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Lorenzo Fontana <fontanalorenz@gmail.com>
2021-01-21 10:38:58 +01:00
23 changed files with 882 additions and 388 deletions

View File

@@ -26,6 +26,5 @@ This is a list of production adopters of Falco (in alphabetical order):
* [Sumo Logic](https://www.sumologic.com/) - Sumo Logic provides a SaaS based log aggregation service that provides dashboards and applications to easily identify and analyze problems in your application and infrastructure. Sumo Logic provides native integrations for many CNCF projects, such as Falco, that allows end users to easily collect Falco events and analyze Falco events on DecSecOps focused dashboards.
* [Swissblock Technologies](https://swissblock.net/) At Swissblock we connect the dots by combining cutting-edge algorithmic trading strategies with in-depth market analysis. We route all Falco events to our control systems, both monitoring and logging. Being able to deeply analyse alerts, we can understand what is running on our Kubernetes clusters and check against security policies, specifically defined for each workload. A set of alarms notifies us in case of critical events, letting us react fast. In the near future we plan to build a little application to route Kubernetes internal events directly to Falco, fully leveraging Falco PodSecurityPolicies analyses.
* [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call probe. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended Falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-defined infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc.
* [Sysdig](https://www.sysdig.com/) Sysdig originally created Falco in 2016 to detect unexpected or suspicious activity using a rules engine on top of the data that comes from the sysdig kernel system call probe. Sysdig provides tooling to help with vulnerability management, compliance, detection, incident response and forensics in Cloud-native environments. Sysdig Secure has extended Falco to include: a rule library, the ability to update macros, lists & rules via the user interface and API, automated tuning of rules, and rule creation based on profiling known system behavior. On top of the basic Falco rules, Sysdig Secure implements the concept of a "Security policy" that can comprise several rules which are evaluated for a user-defined infrastructure scope like Kubernetes namespaces, OpenShift clusters, deployment workload, cloud regions etc.

View File

@@ -226,6 +226,7 @@ set(FALCO_ABSOLUTE_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}")
set(FALCO_BIN_DIR bin)
add_subdirectory(scripts)
add_subdirectory(userspace/libhawk)
add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
add_subdirectory(tests)

View File

@@ -1,16 +1,17 @@
# Falco Dockerfiles
This directory contains various ways to package Falco as a container and related tools.
This directory contains various ways to package Falco as a container and related tools.
## Currently Supported Images
| Name | Directory | Description |
|---|---|---|
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Falco (DEB built from git tag or from the master) with all the building toolchain. |
| [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. |
| [falcosecurity/falco-no-driver:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver), [falcosecurity/falco-no-driver:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver),[falcosecurity/falco-no-driver:master](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver) | docker/no-driver | Falco (TGZ built from git tag or from the master) without the building toolchain. |
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/getting-started/source/) for more details on building from source. Used to build Falco (CI). |
| [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). |
| [falcosecurity/falco:latest](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco), [falcosecurity/falco:master](https://hub.docker.com/repository/docker/falcosecurity/falco) | docker/falco | Falco (DEB built from git tag or from the master) with all the building toolchain. |
| [falcosecurity/falco-driver-loader:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader), [falcosecurity/falco-driver-loader:master](https://hub.docker.com/repository/docker/falcosecurity/falco-driver-loader) | docker/driver-loader | `falco-driver-loader` as entrypoint with the building toolchain. |
| [falcosecurity/falco-no-driver:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver), [falcosecurity/falco-no-driver:_tag_](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver),[falcosecurity/falco-no-driver:master](https://hub.docker.com/repository/docker/falcosecurity/falco-no-driver) | docker/no-driver | Falco (TGZ built from git tag or from the master) without the building toolchain. |
| [falcosecurity/falco-builder:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-builder) | docker/builder | The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/source/) for more details on building from source. Used to build Falco (CI). |
| [falcosecurity/falco-tester:latest](https://hub.docker.com/repository/docker/falcosecurity/falco-tester) | docker/tester | Container image for running the Falco test suite. Used to run Falco integration tests (CI). |
| _to not be published_ | docker/local | Built on-the-fly and used by falco-tester. |
> Note: `falco-builder`, `falco-tester` (and the `docker/local` image that it's built on the fly) are not integrated into the release process because they are development and CI tools that need to be manually pushed only when updated.

View File

@@ -28,10 +28,7 @@
# The files will be read in the order presented here, so make sure if
# you have overrides they appear in later files.
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/k8s_audit_rules.yaml
- /etc/falco/rules.d
- /tmp/falco
# If true, the times displayed in log messages and output messages
# will be in ISO 8601. By default, times are displayed in the local
@@ -218,3 +215,14 @@ grpc:
# Make sure to have a consumer for them or leave this disabled.
grpc_output:
enabled: false
# todo(fntlnz): provide a default implementation
# so that users can avoid to input this configuration
# if they don't need to change the default Falco behavior
#extensions:
# - myextension.so
# Rules provider
# Specify a non-default provider.
# Default value is "internal"
rules_provider: internal

View File

@@ -1,167 +0,0 @@
# OSS Libraries Donation Plan
## Summary
Sysdig Inc. intends to donate **libsinsp**, **libscap**, the **kernel module driver** and the **eBPF driver sources** by moving them to the Falco project.
This means that some parts of the [draios/sysdig](https://github.com/draios/sysdig) repository will be moved to a new GitHub repository called [falcosecurity/libs](https://github.com/falcosecurity/libs).
This plan aims to describe and clarify the terms and goals to get the donation done.
## Motivation
There are two main OSS projects using the libraries and drivers that we are aware of:
- [sysdig](https://github.com/draios/sysdig) the command line tool
- [Falco](https:/github.com/falcosecurity/falco), the CNCF project.
Since the Falco project is a heavy user of the libraries, a lot more than the sysdig cli tool, Sysdig (the company) decided to donate the libraries and the driver to the Falco community.
Sysdig (the command line tool) will continue to use the libraries now provided by the Falco community underneath.
This change is win-win for both parties because of the following reasons:
- The Falco community owns the source code of the three most important parts of the software it distributes.
- Right now it is "only" an engine on top of the libraries. This **donation** helps in making the scope of the Falco project broader. Having the majority of the source code under an **open governance** in the same organization gives the Falco project more contribution opportunities, helps it in **evolving independently** and makes the whole Falco community a strong owner of the processes and decision making regarding those crucial parts.
- Given the previous point, Sysdig (the command line tool) will benefit from the now **extended contributors base**
- Sysdig (the company) can now focus on the user experience and user space features
- **Contributions** to the libraries and drivers will be **easier** to spread across the Falco community
- By being donated, with their own **release process**, **release artifacts**, and **documentation**, the libraries can now live on their own and possibly be used directly in other projects by becoming fundamental pieces for their success.
## Goals
There are many sub-projects and each of them interacts in a different way in this donation.
Let's see the goals per sub-project.
### libsinsp
1. Extract libsinsp from `draios/sysdig/userspace/libsinsp` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
4. Adapt the CMake and build files
5. Install [poiana](https://github.com/poiana) and its workflows on it
6. Define the `OWNERS`
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
7. When possible, migrate issues and PRs to the new repository
8. Distribute the `libsinsp.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
9. Distribute the `libsinsp.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
10. Creation of the CI scripts using the Falco CI and Falco Infra
11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository
12. Artifacts will be pushed for every tag (release) and for every master merge (development release)
13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already
14. This project will go already "Official support" once the donation is completed
15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
16. Every other additional change will need to have its own process with a proposal
17. Implement the release process as described above
18. Propose a change to Falco repository to use the artifacts produced by the libsinsp release process for the build
19. Document the API
### libscap
1. Extract libscap from `draios/sysdig/userspace/libscap` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
2. The migration comes first, then we can do additional PRs for the points below so that we do only one thing at a time and keep the history linear
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
4. Adapt the CMake and build files
5. Install [poiana](https://github.com/poiana) and its workflows on it
6. Define the `OWNERS`
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
7. When possible, migrate issues and PRs to the new repository
8. Distribute the `libscap.so` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
9. Distribute the `libscap.a` library and headers as an artifact (rpm, deb, tar.gz) following the falcosecurity current process
10. Creation of the CI scripts using the Falco CI and Falco Infra
11. The CI scripts will need to publish the artifacts in the current falcosecurity artifacts repository
12. Artifacts will be pushed for every tag (release) and for every master merge (development release)
13. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already
14. This project will go already "Official support" once the donation is completed
15. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
16. Every other additional change will need to have its own process with a proposal
17. Implement the release process as described above
18. Propose a change to Falco repository to use the artifacts produced by the libscap release process for the build
19. Document the API
### Drivers: Kernel module and eBPF probe
1. Extract them from `draios/sysdig/driver` (keeping the commit history) into [falcosecurity/libs](https://github.com/falcosecurity/libs)
2. The migration comes first, then we can do additional PRs for the point below so that we do only one thing at a time and keep the history linear
3. Keep the same code, refactorings will need to be done in subsequent PRs and approved separately
4. Adapt the Makefiles and build files
5. Install [poiana](https://github.com/poiana) and its workflows on it
6. Define the `OWNERS`
- Owners are chosen from the current major contributors (considering the past two years) to this project, given their availability, commitment is key
7. When possible, migrate issues and PRs to the new repository
8. Falco follows a [multi-stage model for adopting new projects](https://github.com/falcosecurity/evolution#falco-project-evolution), in this case we will do an exception since the library is foundational for Falco and it has a very good track record already. We are just changing maintenance ownership
9. Contributing, Code of Conduct, Governance, Security, and Support will be the same as the rest of the organization, find them [here](https://github.com/falcosecurity/.github)
10. Every other additional change will need to have its own process with a proposal
11. The Falco community already ships driver artifacts using [driverkit](https://github.com/falcosecurity/driverkit) and the [test-infra repository](https://github.com/falcosecurity/test-infra)
- Adapt the place from which [driverkit](https://github.com/falcosecurity/driverkit) grabs the drivers source
12. This project will go already "Official support" once the migration is completed.
### Falco
1. Adapt the CMake files to point to the new homes for libscap, libsinsp and the drivers
2. When distributing the deb and rpm, libscap and libsinsp will need to be install dependencies and not anymore compiled into Falco
### Driverkit
1. Change the source location for the drivers to point to the new driver repository
### pdig
1. The project will need to be adapted to use libscap and libsinsp and the fillers from their new location

View File

@@ -50,8 +50,7 @@
vertical_pod_autoscaler_users,
cluster-autoscaler,
"system:addon-manager",
"cloud-controller-manager",
"eks:node-manager"
"cloud-controller-manager"
]
- rule: Disallowed K8s User

View File

@@ -1277,10 +1277,3 @@ trace_files: !mux
trace_file: trace_files/cat_write.scap
stdout_contains: "2016-08-04T16:17:57.882054739\\+0000: Warning An open was seen"
stderr_contains: "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d\\+0000"
unknown_source:
detect: True
detect_level: WARNING
rules_file:
- rules/unknown_source.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -1,31 +0,0 @@
#
# 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.
#
- macro: Macro with unknown source
condition: some other unknown filter
source: unknown-source
- rule: Rule with unknown source
condition: some unknown filter
output: some unknown output
priority: INFO
source: unknown-source
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and proc.name=cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -45,6 +45,7 @@ nlohmann::json::json_pointer falco_engine::k8s_audit_time = "/stageTimestamp"_js
falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
: m_rules(NULL), m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_alternate_lua_dir(alternate_lua_dir),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
{
@@ -68,6 +69,35 @@ falco_engine::falco_engine(bool seed_rng, const std::string& alternate_lua_dir)
m_json_factory = make_shared<json_event_filter_factory>();
}
falco_engine::falco_engine(const falco_engine &orig_engine)
: m_rules(NULL), m_next_ruleset_id(0),
m_min_priority(falco_common::PRIORITY_DEBUG),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
{
luaopen_lpeg(m_ls);
luaopen_yaml(m_ls);
m_alternate_lua_dir = orig_engine.m_alternate_lua_dir;
falco_common::init(m_lua_main_filename.c_str(), m_alternate_lua_dir.c_str());
falco_rules::init(m_ls);
m_sinsp_rules.reset(new falco_sinsp_ruleset());
m_k8s_audit_rules.reset(new falco_ruleset());
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
// Create this now so we can potentially list filters and exit
m_json_factory = make_shared<json_event_filter_factory>();
set_inspector(orig_engine.m_inspector);
std::string extra = orig_engine.m_extra;
set_extra(extra, orig_engine.m_replace_container_info);
set_min_priority(orig_engine.m_min_priority);
set_sampling_multiplier(orig_engine.m_sampling_multiplier);
set_sampling_ratio(orig_engine.m_sampling_ratio);
}
falco_engine::~falco_engine()
{
if (m_rules)

View File

@@ -47,7 +47,8 @@ limitations under the License.
class falco_engine : public falco_common
{
public:
falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
explicit falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR);
falco_engine(const falco_engine &orig_engine);
virtual ~falco_engine();
// A given engine has a version which identifies the fields
@@ -264,6 +265,7 @@ private:
std::unique_ptr<falco_ruleset> m_k8s_audit_rules;
void populate_rule_result(unique_ptr<struct rule_result> &res, gen_event *ev);
std::string m_alternate_lua_dir;
//
// Here's how the sampling ratio and multiplier influence

View File

@@ -436,11 +436,6 @@ function load_rules_doc(rules_mgr, doc, load_state)
v['source'] = "syscall"
end
-- Ignore macros with unknown sources
if (v['source'] ~= "syscall" and v['source'] ~= "k8s_audit") then
goto next_object
end
if state.macros_by_name[v['macro']] == nil then
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
end
@@ -527,11 +522,6 @@ function load_rules_doc(rules_mgr, doc, load_state)
v['source'] = "syscall"
end
-- Ignore rules with unknown sources
if (v['source'] ~= "syscall" and v['source'] ~= "k8s_audit") then
goto next_object
end
-- Add an empty exceptions property to the rule if not
-- defined, but add a warning about defining one
if v['exceptions'] == nil then
@@ -678,8 +668,6 @@ function load_rules_doc(rules_mgr, doc, load_state)
arr = build_error_with_context(context, "Unknown top level object: "..table.tostring(v))
warnings[#warnings + 1] = arr[1]
end
::next_object::
end
return true, {}, warnings

View File

@@ -30,6 +30,7 @@ set(
set(
FALCO_INCLUDE_DIRECTORIES
"${LIBHAWK_INCLUDE_DIRECTORY}"
"${PROJECT_SOURCE_DIR}/userspace/engine"
"${PROJECT_BINARY_DIR}/userspace/falco"
"${PROJECT_BINARY_DIR}/driver/src"
@@ -52,6 +53,7 @@ set(
set(
FALCO_LIBRARIES
falco_engine
libhawk
sinsp
"${LIBYAML_LIB}"
"${YAMLCPP_LIB}"
@@ -61,6 +63,8 @@ if(USE_BUNDLED_DEPS)
list(APPEND FALCO_DEPENDENCIES yamlcpp)
endif()
if(NOT MINIMAL_BUILD)
list(
APPEND FALCO_SOURCES
@@ -124,12 +128,14 @@ target_include_directories(
)
if(NOT MINIMAL_BUILD)
add_custom_command(
TARGET falco
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
)
# todo(fntlnz): restore this before merge, after the command for compare is refactored
# to work with the new way the engine is passed around
# add_custom_command(
# TARGET falco
# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
# COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
# )
else()
message(STATUS "Skipping engine fields checksum when building the minimal Falco.")
endif()

View File

@@ -137,6 +137,11 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
m_outputs.push_back(http_output);
}
// extension related configuration
m_config->get_sequence<list<string>>(m_extensions_filenames , string("extensions"));
m_rules_provider = m_config->get_scalar<string>("rules_provider", "internal");
// gRPC related configuration
m_grpc_enabled = m_config->get_scalar<bool>("grpc", "enabled", false);
m_grpc_bind_address = m_config->get_scalar<string>("grpc", "bind_address", "0.0.0.0:5060");
m_grpc_threadiness = m_config->get_scalar<uint32_t>("grpc", "threadiness", 0);

View File

@@ -222,6 +222,9 @@ public:
double m_syscall_evt_drop_rate;
double m_syscall_evt_drop_max_burst;
std::list<std::string> m_extensions_filenames;
std::string m_rules_provider;
// Only used for testing
bool m_syscall_evt_simulate_drops;

View File

@@ -30,14 +30,16 @@ limitations under the License.
#include <sys/stat.h>
#include <unistd.h>
#include <getopt.h>
#include <condition_variable>
#include <sinsp.h>
#include "logger.h"
#include "utils.h"
#include "chisel.h"
#include "fields_info.h"
#include "lifecycle.h"
#include "library.h"
#include "event_drops.h"
#include "configuration.h"
#include "falco_engine.h"
@@ -49,13 +51,24 @@ limitations under the License.
#endif
#include "banned.h" // This raises a compilation error when certain functions are used
typedef function<void(sinsp* inspector)> open_t;
typedef function<void(sinsp *inspector)> open_t;
bool g_terminate = false;
bool g_reopen_outputs = false;
bool g_restart = false;
bool g_daemonized = false;
// g_engine is the current loaded Falco engine
std::atomic<falco_engine *> g_engine;
// g_engine_transaction is the Falco engine that is
// being modified under a transaction started by a libhawk plugin
// This engine might become the current g_engine if the transaction is committed
std::atomic<falco_engine *> g_engine_transaction;
// g_engine_blueprint is the engine we use as a template to create new engines
falco_engine *g_engine_blueprint;
//
// Helper functions
//
@@ -174,7 +187,6 @@ static void usage()
"\n"
);
}
static void display_fatal_err(const string &msg)
{
falco_logger::log(LOG_ERR, msg);
@@ -183,7 +195,7 @@ static void display_fatal_err(const string &msg)
* If stderr logging is not enabled, also log to stderr. When
* daemonized this will simply write to /dev/null.
*/
if (! falco_logger::log_stderr)
if(!falco_logger::log_stderr)
{
std::cerr << msg;
}
@@ -235,20 +247,19 @@ static std::string read_file(std::string filename)
//
// Event processing loop
//
uint64_t do_inspect(falco_engine *engine,
falco_outputs *outputs,
sinsp* inspector,
falco_configuration &config,
syscall_evt_drop_mgr &sdropmgr,
uint64_t duration_to_tot_ns,
string &stats_filename,
uint64_t stats_interval,
bool all_events,
int &result)
uint64_t do_inspect(falco_outputs *outputs,
sinsp *inspector,
falco_configuration &config,
syscall_evt_drop_mgr &sdropmgr,
uint64_t duration_to_tot_ns,
string &stats_filename,
uint64_t stats_interval,
bool all_events,
int &result)
{
uint64_t num_evts = 0;
int32_t rc;
sinsp_evt* ev;
sinsp_evt *ev;
StatsFileWriter writer;
uint64_t duration_start = 0;
@@ -259,19 +270,27 @@ uint64_t do_inspect(falco_engine *engine,
config.m_syscall_evt_drop_max_burst,
config.m_syscall_evt_simulate_drops);
if (stats_filename != "")
if(stats_filename != "")
{
string errstr;
if (!writer.init(inspector, stats_filename, stats_interval, errstr))
if(!writer.init(inspector, stats_filename, stats_interval, errstr))
{
throw falco_exception(errstr);
}
}
//
// Loop through the events
//
falco_engine *current_engine = g_engine.exchange(nullptr);
// If we didn't get a set of rules yet from the rules plugin, we load
// an engine with an empty ruleset to let Falco do the processing without blocking
// the driver.
if(current_engine == nullptr)
{
current_engine = new falco_engine((const falco_engine)*g_engine_blueprint);
current_engine->load_rules("", false, false);
}
while(1)
{
@@ -290,7 +309,7 @@ uint64_t do_inspect(falco_engine *engine,
falco_logger::log(LOG_INFO, "SIGINT received, exiting...\n");
break;
}
else if (g_restart)
else if(g_restart)
{
falco_logger::log(LOG_INFO, "SIGHUP received, restarting...\n");
break;
@@ -313,10 +332,11 @@ uint64_t do_inspect(falco_engine *engine,
throw sinsp_exception(inspector->getlasterr().c_str());
}
if (duration_start == 0)
if(duration_start == 0)
{
duration_start = ev->get_ts();
} else if(duration_to_tot_ns > 0)
}
else if(duration_to_tot_ns > 0)
{
if(ev->get_ts() - duration_start >= duration_to_tot_ns)
{
@@ -335,12 +355,14 @@ uint64_t do_inspect(falco_engine *engine,
continue;
}
// As the inspector has no filter at its level, all
// events are returned here. Pass them to the falco
// engine, which will match the event against the set
// of rules. If a match is found, pass the event to
// the outputs.
unique_ptr<falco_engine::rule_result> res = engine->process_sinsp_event(ev);
auto engine_replacement = g_engine.exchange(nullptr);
if(engine_replacement != nullptr)
{
delete current_engine;
current_engine = engine_replacement;
falco_logger::log(LOG_DEBUG, "falco_engine replacement found and swapped");
}
unique_ptr<falco_engine::rule_result> res = current_engine->process_sinsp_event(ev);
if(res)
{
outputs->handle_event(res->evt, res->rule, res->source, res->priority_num, res->format);
@@ -354,9 +376,9 @@ uint64_t do_inspect(falco_engine *engine,
static void print_all_ignored_events(sinsp *inspector)
{
sinsp_evttables* einfo = inspector->get_event_info_tables();
const struct ppm_event_info* etable = einfo->m_event_info;
const struct ppm_syscall_desc* stable = einfo->m_syscall_info_table;
sinsp_evttables *einfo = inspector->get_event_info_tables();
const struct ppm_event_info *etable = einfo->m_event_info;
const struct ppm_syscall_desc *stable = einfo->m_syscall_info_table;
std::set<string> ignored_event_names;
for(uint32_t j = 0; j < PPM_EVENT_MAX; j++)
@@ -410,15 +432,67 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on
}
}
static void rules_insert_cb(char *rules_content)
{
try
{
auto engine = g_engine_transaction.load();
if(engine == nullptr)
{
// todo: inform the caller about this error, maybe stderr and return code?
falco_logger::log(LOG_ERR, std::string("can't insert rules, no transaction in progress"));
return;
}
engine->load_rules(rules_content, false, true);
g_engine_transaction.store(engine);
}
catch(const falco_exception &e)
{
// todo: inform the caller about this error, maybe stderr and return code?
falco_logger::log(LOG_WARNING, std::string("rules load failed: ") + e.what());
return;
}
}
static void rules_begin_cb()
{
if(g_engine_transaction.load() != nullptr)
{
// todo: inform the caller about this error, maybe stderr and return code?
falco_logger::log(LOG_ERR, std::string("a transaction is already in progress"));
return;
}
auto engine_replacement = new falco_engine((const falco_engine)*g_engine_blueprint);
g_engine_transaction.store(engine_replacement);
}
static void rules_commit_cb()
{
auto engine = g_engine_transaction.load();
if(engine == nullptr)
{
// todo: inform the caller about this error, maybe stderr and return code?
falco_logger::log(LOG_ERR, std::string("can't commit rules, no transaction in progress"));
return;
}
delete g_engine.exchange(g_engine_transaction.load());
g_engine_transaction.store(nullptr);
}
static void rules_rollback_cb()
{
g_engine_transaction.store(nullptr);
}
//
// ARGUMENT PARSING AND PROGRAM SETUP
//
int falco_init(int argc, char **argv)
{
int result = EXIT_SUCCESS;
sinsp* inspector = NULL;
sinsp *inspector = NULL;
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
falco_engine *engine = NULL;
std::thread watchrules_thread;
falco_outputs *outputs = NULL;
syscall_evt_drop_mgr sdropmgr;
int op;
@@ -439,9 +513,9 @@ int falco_init(int argc, char **argv)
bool names_only = false;
bool all_events = false;
#ifndef MINIMAL_BUILD
string* k8s_api = 0;
string* k8s_api_cert = 0;
string* mesos_api = 0;
string *k8s_api = 0;
string *k8s_api_cert = 0;
string *mesos_api = 0;
#endif
string output_format = "";
uint32_t snaplen = 0;
@@ -466,7 +540,7 @@ int falco_init(int argc, char **argv)
bool compress = false;
bool buffered_outputs = true;
bool buffered_cmdline = false;
std::map<string,uint64_t> required_engine_versions;
std::map<string, uint64_t> required_engine_versions;
// Used for stats
double duration;
@@ -518,8 +592,8 @@ int falco_init(int argc, char **argv)
// Parse the args
//
while((op = getopt_long(argc, argv,
"hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
long_options, &long_index)) != -1)
"hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
long_options, &long_index)) != -1)
{
switch(op)
{
@@ -654,18 +728,18 @@ int falco_init(int argc, char **argv)
printf("Driver version: %s\n", DRIVER_VERSION);
return EXIT_SUCCESS;
}
else if (string(long_options[long_index].name) == "cri")
else if(string(long_options[long_index].name) == "cri")
{
if(optarg != NULL)
{
cri_socket_path = optarg;
}
}
else if (string(long_options[long_index].name) == "disable-cri-async")
else if(string(long_options[long_index].name) == "disable-cri-async")
{
cri_async = false;
cri_async = false;
}
else if (string(long_options[long_index].name) == "list")
else if(string(long_options[long_index].name) == "list")
{
list_flds = true;
if(optarg != NULL)
@@ -673,27 +747,28 @@ int falco_init(int argc, char **argv)
list_flds_source = optarg;
}
}
else if (string(long_options[long_index].name) == "stats-interval")
else if(string(long_options[long_index].name) == "stats-interval")
{
stats_interval = atoi(optarg);
}
else if (string(long_options[long_index].name) == "support")
else if(string(long_options[long_index].name) == "support")
{
print_support = true;
}
else if (string(long_options[long_index].name) == "disable-source")
else if(string(long_options[long_index].name) == "disable-source")
{
if(optarg != NULL)
{
disable_sources.insert(optarg);
}
}
else if (string(long_options[long_index].name)== "alternate-lua-dir")
else if(string(long_options[long_index].name) == "alternate-lua-dir")
{
if(optarg != NULL)
{
alternate_lua_dir = optarg;
if (alternate_lua_dir.back() != '/') {
if(alternate_lua_dir.back() != '/')
{
alternate_lua_dir += '/';
}
}
@@ -703,7 +778,6 @@ int falco_init(int argc, char **argv)
default:
break;
}
}
inspector = new sinsp();
@@ -733,13 +807,14 @@ int falco_init(int argc, char **argv)
return EXIT_SUCCESS;
}
engine = new falco_engine(true, alternate_lua_dir);
engine->set_inspector(inspector);
engine->set_extra(output_format, replace_container_info);
auto initial_engine = new falco_engine(true, alternate_lua_dir);
initial_engine->set_inspector(inspector);
initial_engine->set_extra(output_format, replace_container_info);
g_engine_blueprint = initial_engine;
if(list_flds)
{
list_source_fields(engine, verbose, names_only, list_flds_source);
// list_source_fields(engine, verbose, names_only, list_flds_source);
return EXIT_SUCCESS;
}
@@ -757,21 +832,23 @@ int falco_init(int argc, char **argv)
}
disable_syscall = disable_sources.count("syscall") > 0;
disable_k8s_audit = disable_sources.count("k8s_audit") > 0;
if (disable_syscall && disable_k8s_audit) {
if(disable_syscall && disable_k8s_audit)
{
throw std::invalid_argument("The event source \"syscall\" and \"k8s_audit\" can not be disabled together");
}
}
// Some combinations of arguments are not allowed.
if (daemon && pidfilename == "") {
if(daemon && pidfilename == "")
{
throw std::invalid_argument("If -d is provided, a pid file must also be provided");
}
ifstream conf_stream;
if (conf_filename.size())
if(conf_filename.size())
{
conf_stream.open(conf_filename);
if (!conf_stream.is_open())
if(!conf_stream.is_open())
{
throw std::runtime_error("Could not find configuration file at " + conf_filename);
}
@@ -779,14 +856,14 @@ int falco_init(int argc, char **argv)
else
{
conf_stream.open(FALCO_SOURCE_CONF_FILE);
if (conf_stream.is_open())
if(conf_stream.is_open())
{
conf_filename = FALCO_SOURCE_CONF_FILE;
}
else
{
conf_stream.open(FALCO_INSTALL_CONF_FILE);
if (conf_stream.is_open())
if(conf_stream.is_open())
{
conf_filename = FALCO_INSTALL_CONF_FILE;
}
@@ -797,33 +874,35 @@ int falco_init(int argc, char **argv)
}
}
if(validate_rules_filenames.size() > 0)
{
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
for(auto file : validate_rules_filenames)
{
falco_logger::log(LOG_INFO, " " + file + "\n");
}
for(auto file : validate_rules_filenames)
{
// Only include the prefix if there is more than one file
std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
try {
engine->load_rules_file(file, verbose, all_events);
}
catch(falco_exception &e)
{
printf("%s%s", prefix.c_str(), e.what());
throw;
}
printf("%sOk\n", prefix.c_str());
}
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;
}
// validate the rules files and exit
// if(validate_rules_filenames.size() > 0)
// {
// falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
// for(auto file : validate_rules_filenames)
// {
// falco_logger::log(LOG_INFO, " " + file + "\n");
// }
// for(auto file : validate_rules_filenames)
// {
// // Only include the prefix if there is more than one file
// std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
// try
// {
// engine->load_rules_file(file, verbose, all_events);
// }
// catch(falco_exception &e)
// {
// printf("%s%s", prefix.c_str(), e.what());
// throw;
// }
// printf("%sOk\n", prefix.c_str());
// }
// falco_logger::log(LOG_INFO, "Ok\n");
// goto exit;
// }
falco_configuration config;
if (conf_filename.size())
if(conf_filename.size())
{
config.init(conf_filename, cmdline_options);
falco_logger::set_time_format_iso_8601(config.m_time_format_iso_8601);
@@ -837,12 +916,20 @@ int falco_init(int argc, char **argv)
throw std::runtime_error("Could not find configuration file at " + conf_filename);
}
if (rules_filenames.size())
for(auto extension : config.m_extensions_filenames)
{
auto lib = new libhawk::library(extension);
lib->load();
}
libhawk::lifecycle::start();
if(rules_filenames.size())
{
config.m_rules_filenames = rules_filenames;
}
engine->set_min_priority(config.m_min_priority);
g_engine_blueprint->set_min_priority(config.m_min_priority);
if(buffered_cmdline)
{
@@ -851,42 +938,35 @@ int falco_init(int argc, char **argv)
if(config.m_rules_filenames.size() == 0)
{
throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
// throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
}
falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
for (auto filename : config.m_rules_filenames)
for(auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
}
for (auto filename : config.m_rules_filenames)
for(auto filename : config.m_rules_filenames)
{
falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
uint64_t required_engine_version;
try {
engine->load_rules_file(filename, verbose, all_events, required_engine_version);
}
catch(falco_exception &e)
{
std::string prefix = "Could not load rules file " + filename + ": ";
throw falco_exception(prefix + e.what());
}
// engine->load_rules_file(filename, verbose, all_events, required_engine_version);
required_engine_versions[filename] = required_engine_version;
}
// You can't both disable and enable rules
if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0) {
enabled_rule_tags.size() > 0)
{
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
}
for (auto substring : disabled_rule_substrings)
for(auto substring : disabled_rule_substrings)
{
falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
engine->enable_rule(substring, false);
// engine->enable_rule(substring, false);
}
if(disabled_rule_tags.size() > 0)
@@ -895,7 +975,7 @@ int falco_init(int argc, char **argv)
{
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(disabled_rule_tags, false);
// engine->enable_rule_by_tag(disabled_rule_tags, false);
}
if(enabled_rule_tags.size() > 0)
@@ -903,14 +983,87 @@ int falco_init(int argc, char **argv)
// Since we only want to enable specific
// rules, first disable all rules.
engine->enable_rule(all_rules, false);
// engine->enable_rule(all_rules, false);
for(auto tag : enabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(enabled_rule_tags, true);
// engine->enable_rule_by_tag(enabled_rule_tags, true);
}
watchrules_thread = std::thread([&] {
libhawk::lifecycle::watch_rules(
(hawk_rules_begin_cb)rules_begin_cb,
(hawk_rules_insert_cb)rules_insert_cb,
(hawk_rules_commit_cb)rules_commit_cb,
(hawk_rules_rollback_cb)rules_rollback_cb,
config.m_rules_provider);
});
falco_logger::log(LOG_INFO, "DOPO\n");
// if(config.m_rules_filenames.size() == 0)
// {
// throw std::invalid_argument("You must specify at least one rules file/directory via -r or a rules_file entry in falco.yaml");
// }
// falco_logger::log(LOG_DEBUG, "Configured rules filenames:\n");
// for (auto filename : config.m_rules_filenames)
// {
// falco_logger::log(LOG_DEBUG, string(" ") + filename + "\n");
// }
// for (auto filename : config.m_rules_filenames)
// {
// falco_logger::log(LOG_INFO, "Loading rules from file " + filename + ":\n");
// uint64_t required_engine_version;
// try {
// engine->load_rules_file(filename, verbose, all_events, required_engine_version);
// }
// catch(falco_exception &e)
// {
// std::string prefix = "Could not load rules file " + filename + ": ";
// throw falco_exception(prefix + e.what());
// }
// required_engine_versions[filename] = required_engine_version;
// }
// // You can't both disable and enable rules
// if((disabled_rule_substrings.size() + disabled_rule_tags.size() > 0) &&
// enabled_rule_tags.size() > 0) {
// throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
// }
// for (auto substring : disabled_rule_substrings)
// {
// falco_logger::log(LOG_INFO, "Disabling rules matching substring: " + substring + "\n");
// engine->enable_rule(substring, false);
// }
// if(disabled_rule_tags.size() > 0)
// {
// for(auto tag : disabled_rule_tags)
// {
// falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
// }
// engine->enable_rule_by_tag(disabled_rule_tags, false);
// }
// if(enabled_rule_tags.size() > 0)
// {
// // Since we only want to enable specific
// // rules, first disable all rules.
// engine->enable_rule(all_rules, false);
// for(auto tag : enabled_rule_tags)
// {
// falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
// }
// engine->enable_rule_by_tag(enabled_rule_tags, true);
// }
if(print_support)
{
nlohmann::json support;
@@ -956,7 +1109,7 @@ int falco_init(int argc, char **argv)
// read hostname
string hostname;
if(char* env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
if(char *env_hostname = getenv("FALCO_GRPC_HOSTNAME"))
{
hostname = env_hostname;
}
@@ -976,15 +1129,15 @@ int falco_init(int argc, char **argv)
inspector->set_drop_event_flags(EF_DROP_SIMPLE_CONS);
}
if (describe_all_rules)
if(describe_all_rules)
{
engine->describe_rule(NULL);
// engine->describe_rule(NULL);
goto exit;
}
if (describe_rule != "")
if(describe_rule != "")
{
engine->describe_rule(&describe_rule);
// engine->describe_rule(&describe_rule);
goto exit;
}
@@ -1020,21 +1173,25 @@ int falco_init(int argc, char **argv)
// If daemonizing, do it here so any init errors will
// be returned in the foreground process.
if (daemon && !g_daemonized) {
if(daemon && !g_daemonized)
{
pid_t pid, sid;
pid = fork();
if (pid < 0) {
if(pid < 0)
{
// error
falco_logger::log(LOG_ERR, "Could not fork. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
} else if (pid > 0) {
}
else if(pid > 0)
{
// parent. Write child pid to pidfile and exit
std::ofstream pidfile;
pidfile.open(pidfilename);
if (!pidfile.good())
if(!pidfile.good())
{
falco_logger::log(LOG_ERR, "Could not write pid to pid file " + pidfilename + ". Exiting.\n");
result = EXIT_FAILURE;
@@ -1048,7 +1205,8 @@ int falco_init(int argc, char **argv)
// Become own process group.
sid = setsid();
if (sid < 0) {
if(sid < 0)
{
falco_logger::log(LOG_ERR, "Could not set session id. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
@@ -1058,7 +1216,8 @@ int falco_init(int argc, char **argv)
umask(027);
// Change working directory to '/'
if ((chdir("/")) < 0) {
if((chdir("/")) < 0)
{
falco_logger::log(LOG_ERR, "Could not change working directory to '/'. Exiting.\n");
result = EXIT_FAILURE;
goto exit;
@@ -1094,14 +1253,15 @@ int falco_init(int argc, char **argv)
{
// Try to open the trace file as a sysdig
// capture file first.
try {
try
{
inspector->open(trace_filename);
falco_logger::log(LOG_INFO, "Reading system call events from file: " + trace_filename + "\n");
}
catch(sinsp_exception &e)
{
falco_logger::log(LOG_DEBUG, "Could not read trace file \"" + trace_filename + "\": " + string(e.what()));
trace_is_scap=false;
trace_is_scap = false;
}
if(!trace_is_scap)
@@ -1112,7 +1272,8 @@ int falco_init(int argc, char **argv)
result = EXIT_FAILURE;
goto exit;
#else
try {
try
{
string line;
nlohmann::json j;
@@ -1124,13 +1285,13 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_INFO, "Reading k8s audit events from file: " + trace_filename + "\n");
}
catch (nlohmann::json::parse_error& e)
catch(nlohmann::json::parse_error &e)
{
fprintf(stderr, "Trace filename %s not recognized as system call events or k8s audit events\n", trace_filename.c_str());
result = EXIT_FAILURE;
goto exit;
}
catch (exception &e)
catch(exception &e)
{
fprintf(stderr, "Could not open trace filename %s for reading: %s\n", trace_filename.c_str(), e.what());
result = EXIT_FAILURE;
@@ -1141,8 +1302,7 @@ int falco_init(int argc, char **argv)
}
else
{
open_t open_cb = [&userspace](sinsp* inspector)
{
open_t open_cb = [&userspace](sinsp *inspector) {
if(userspace)
{
// open_udig() is the underlying method used in the capture code to parse userspace events from the kernel.
@@ -1154,19 +1314,22 @@ int falco_init(int argc, char **argv)
}
inspector->open();
};
open_t open_nodriver_cb = [](sinsp* inspector) {
open_t open_nodriver_cb = [](sinsp *inspector) {
inspector->open_nodriver();
};
open_t open_f;
// Default mode: both event sources enabled
if (!disable_syscall && !disable_k8s_audit) {
if(!disable_syscall && !disable_k8s_audit)
{
open_f = open_cb;
}
if (disable_syscall) {
if(disable_syscall)
{
open_f = open_nodriver_cb;
}
if (disable_k8s_audit) {
if(disable_k8s_audit)
{
open_f = open_cb;
}
@@ -1177,7 +1340,7 @@ int falco_init(int argc, char **argv)
catch(sinsp_exception &e)
{
// If syscall input source is enabled and not through userspace instrumentation
if (!disable_syscall && !userspace)
if(!disable_syscall && !userspace)
{
// Try to insert the Falco kernel module
if(system("modprobe " PROBE_NAME " > /dev/null 2> /dev/null"))
@@ -1215,7 +1378,7 @@ int falco_init(int argc, char **argv)
{
if(!k8s_api_cert)
{
if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
if(char *k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
{
k8s_api_cert = new string(k8s_cert_env);
}
@@ -1224,13 +1387,13 @@ int falco_init(int argc, char **argv)
k8s_api = 0;
k8s_api_cert = 0;
}
else if(char* k8s_api_env = getenv("FALCO_K8S_API"))
else if(char *k8s_api_env = getenv("FALCO_K8S_API"))
{
if(k8s_api_env != NULL)
{
if(!k8s_api_cert)
{
if(char* k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
if(char *k8s_cert_env = getenv("FALCO_K8S_API_CERT"))
{
k8s_api_cert = new string(k8s_cert_env);
}
@@ -1254,7 +1417,7 @@ int falco_init(int argc, char **argv)
{
inspector->init_mesos_client(mesos_api, verbose);
}
else if(char* mesos_api_env = getenv("FALCO_MESOS_API"))
else if(char *mesos_api_env = getenv("FALCO_MESOS_API"))
{
if(mesos_api_env != NULL)
{
@@ -1267,10 +1430,10 @@ int falco_init(int argc, char **argv)
if(trace_filename.empty() && config.m_webserver_enabled && !disable_k8s_audit)
{
std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : "");
falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n");
webserver.init(&config, engine, outputs);
webserver.start();
// std::string ssl_option = (config.m_webserver_ssl_enabled ? " (SSL)" : "");
// falco_logger::log(LOG_INFO, "Starting internal webserver, listening on port " + to_string(config.m_webserver_listen_port) + ssl_option + "\n");
// webserver.init(&config, engine, outputs);
// webserver.start();
}
// gRPC server
@@ -1285,8 +1448,7 @@ int falco_init(int argc, char **argv)
config.m_grpc_private_key,
config.m_grpc_cert_chain,
config.m_grpc_root_certs,
config.m_log_level
);
config.m_log_level);
grpc_server_thread = std::thread([&grpc_server] {
grpc_server.run();
});
@@ -1296,21 +1458,20 @@ int falco_init(int argc, char **argv)
if(!trace_filename.empty() && !trace_is_scap)
{
#ifndef MINIMAL_BUILD
read_k8s_audit_trace_file(engine,
outputs,
trace_filename);
// read_k8s_audit_trace_file(engine,
// outputs,
// trace_filename);
#endif
}
else
{
uint64_t num_evts;
num_evts = do_inspect(engine,
outputs,
num_evts = do_inspect(outputs,
inspector,
config,
sdropmgr,
uint64_t(duration_to_tot*ONE_SECOND_IN_NS),
uint64_t(duration_to_tot * ONE_SECOND_IN_NS),
stats_filename,
stats_interval,
all_events,
@@ -1331,20 +1492,25 @@ int falco_init(int argc, char **argv)
num_evts,
num_evts / duration);
}
}
// Honor -M also when using a trace file.
// Since inspection stops as soon as all events have been consumed
// just await the given duration is reached, if needed.
if(!trace_filename.empty() && duration_to_tot>0)
if(!trace_filename.empty() && duration_to_tot > 0)
{
std::this_thread::sleep_for(std::chrono::seconds(duration_to_tot));
}
inspector->close();
engine->print_stats();
// engine->print_stats();
sdropmgr.print_stats();
libhawk::lifecycle::stop();
if(watchrules_thread.joinable())
{
watchrules_thread.join();
}
#ifndef MINIMAL_BUILD
webserver.stop();
if(grpc_server_thread.joinable())
@@ -1357,7 +1523,11 @@ int falco_init(int argc, char **argv)
catch(exception &e)
{
display_fatal_err("Runtime error: " + string(e.what()) + ". Exiting.\n");
libhawk::lifecycle::stop();
if(watchrules_thread.joinable())
{
watchrules_thread.join();
}
result = EXIT_FAILURE;
#ifndef MINIMAL_BUILD
@@ -1373,9 +1543,7 @@ int falco_init(int argc, char **argv)
exit:
delete inspector;
delete engine;
delete outputs;
return result;
}

View File

@@ -0,0 +1,37 @@
#
# Copyright (C) 2020 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(CheckSymbolExists)
set(
LIBHAWK_SOURCES
lifecycle.cpp
library.cpp
)
set(
LIBHAWK_PUBLIC_INCLUDES
hawk.h
)
set(LIBHAWK_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
add_library(libhawk STATIC ${LIBHAWK_SOURCES})
target_link_options(libhawk PUBLIC "LINKER:--export-dynamic-symbol=plugin_registry")
#todo: we want to provide a default version of the libhawk plugin functions
# we need to manage the situation where the user only provides parts of it and not others
install(
FILES ${LIBHAWK_PUBLIC_INCLUDES}
${PROJECT_BINARY_DIR}/userspace/libhawk/libhawk_export.h
DESTINATION "${FALCO_SHARE_DIR}"
)

143
userspace/libhawk/README.md Normal file
View File

@@ -0,0 +1,143 @@
# Libhawk
Libhawk is a plugin system that can be used to enrich Falco
functionalities via external, user-defined libraries.
## Glossary:
- library: a bundle (e.g: an ELF shared library) containing one or more plugins
- plugin: an hawk plugin. Libraries can register one or more plugins using the `HAWK_REGISTER_PLUGIN` macro
- plugin function: a specific function inside the plugin definition of each plugin. `hawk_init`, `hawk_destroy`
- extension: it's the user facing term to define a library that contains one or more plugin.
## Plugin definitions and lifecycle
Plugins are all loaded when Falco starts.
Falco provides a default plugin for the main functionalities.
### hawk_init
On start, the `hawk_init` function of every plugin is called.
You can use that function to create any resource you might need
for your plugin's lifecycle.
### hawk_destroy
When Falco is stopped, the `hawk_destroy` function gets called.
Implementors have the last chance to free any resources here.
### hawk_watch_rules
`hawk_watch_rules` implements a transactional interface for updating rules.
Its signature takes four arguments, one for each state of the transaction.
An implementation looks like this
```C
void hawk_watch_rules(hawk_rules_begin_cb begin_cb,
hawk_rules_insert_cb insert_cb,
hawk_rules_commit_cb commit_cb,
hawk_rules_rollback_cb rollback_cb)
{
printf("starting rules transaction\n");
begin_cb(); // start the rules loading transaction
printf("insert rules\n");
insert_cb(""); // todo: pass the rules as a string here, this is empty
insert_cb(""); // you can do this as many times you want
commit_cb(); // commit rules
printf("rules committed");
}
```
As you can see, we have a `begin_cb` that is telling the Falco engine to start the transactiont o load rules.
Then we have an `insert_cb` which takes Falco rules as a yaml string, it can be called as many times you want.
Finally we can either commit the transaction with `commit_cb` or we can rollback it with `rollback_cb`.
**Important note**: `hawk_watch_rules` gets called in a thread by Falco.
This means that it is not blocking and executing in parallel with the rest of Falco.
Practically, you can implement things like a for loop to update rules **live** from a database or an external resource.
After you load the extension, you will need to change the `rules_provider` configuration in `falco.yaml` to the
name you gave to the extension you are writing if you want to use the watch rules implementation you just wrote.
<a name="extension-loading"></a>
## Extension Loading
To tell falco to load a library containing one or more plugins
you have to add the path to the shared object into the `extensions`
configuration in `falco.yaml`:
The path can be either absolute, relative or specified into the `ldconfig` search path.
See `/etc/ld.so.conf` for reference.
examples:
```
extensions:
- ./mylocalextension.so
- myextension.so
- /usr/share/falco/extensions/kubernetes.so
```
TODO: when shipping Falco with this feature, we probably want to ship a ld config file to allow dynamic
loading from `/usr/share/falco/extensions` for example.
## Plugin configuration
TODO
This can be explained once this feature is developed.
## Plugin example
A plugin can define one or more definitions.
Here's an example of plugin that is registered and defines
`hawk_init`, `hawk_destroy` and `hawk_watch_rules`
```c
#include "hawk.h"
#include <stdio.h>
void hawk_init() { printf("hawk_example init!\n"); }
void hawk_destroy() { printf("hawk example destroy\n"); }
// note: this function gets called in a thread.
// this means that it is non blocking for the rest of falco.
// You can start your own lifecycle here to fetch rules from
// the outside and begin/commit as many transactions you want in a loop.
void hawk_watch_rules(hawk_rules_begin_cb begin_cb,
hawk_rules_insert_cb insert_cb,
hawk_rules_commit_cb commit_cb,
hawk_rules_rollback_cb rollback_cb)
{
printf("starting rules transaction\n");
begin_cb(); // start the rules loading transaction
printf("insert rules\n");
insert_cb(""); // todo: pass the rules as a string here, this is empty
insert_cb(""); // you can do this as many times you want
commit_cb(); // commit rules
printf("rules committed");
}
hawk_plugin_definition plugin_definition = {
.hawk_init = &hawk_init,
.hawk_destroy = &hawk_destroy,
.hawk_watch_rules = &hawk_watch_rules,
};
HAWK_REGISTER_PLUGIN(hawk_example_c, plugin_definition)
```
To compile the plugin, save it in a file `plugin.c` and then:
```bash
FALCO=/source/falco
gcc -o libhawk.so -fPIC -shared -I$FALCO/userspace/libhawk plugin.c
```
Remember to change the `FALCO` variable to point to where you have the Falco sources.
This should produce shared object called `libhawk.so`, you can now use this library to load the plugin in Falco.
See the [Extension loading](#extension-loading) section.

View File

@@ -0,0 +1,44 @@
#pragma once
#include <stdexcept>
#include <stdexcept>
#include <string>
namespace libhawk
{
class hawk_exception : public std::runtime_error
{
public:
hawk_exception(const std::string& message):
std::runtime_error(message) {}
};
class hawk_plugin_exception : public hawk_exception
{
public:
hawk_plugin_exception(const std::string& plugin_name, const std::string& message):
hawk_exception("plugin: " + plugin_name + ", error: " + message) {}
};
class hawk_library_exception : public hawk_exception
{
public:
hawk_library_exception(const std::string& message):
hawk_exception(message) {}
};
class hawk_library_load_exception : public hawk_library_exception
{
public:
hawk_library_load_exception(const std::string&library_name, const std::string&message):
hawk_library_exception("library loading error, library: " + library_name + " error: " + message) {}
};
class hawk_library_unload_exception : public hawk_library_exception
{
public:
hawk_library_unload_exception(const std::string&library_name, const std::string&message):
hawk_library_exception("library unloading error, library: " + library_name + " error: " + message) {}
};
} // namespace libhawk

43
userspace/libhawk/hawk.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef HAWK_H
#define HAWK_H
// TODO(fntlnz): decide what to do with versioning here
#define HAWK_VERSION_CODE 0x000001
#define HAWK_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z))
#define HAWK_AT_LEAST_VERSION(x, y, z) \
(HAWK_VERSION_CODE >= HAWK_VERSION_BITS(x, y, z))
// Rules update follows a transactional pattern
// - begin the transaction with `hawk_rules_begin_cb`
// - add rules as many times you want with `hawk_rules_insert_cb`
// - commit the rules with `hawk_rules_commit_cb`
// - if anything went wrong, you can rollback with hawk_rules_rollback_cb
typedef void (*hawk_rules_begin_cb)();
typedef void (*hawk_rules_insert_cb)(char* rules_content);
typedef void (*hawk_rules_commit_cb)();
typedef void (*hawk_rules_rollback_cb)();
typedef struct
{
void (*hawk_init)(void);
void (*hawk_destroy)(void);
void (*hawk_watch_rules)(hawk_rules_begin_cb, hawk_rules_insert_cb, hawk_rules_commit_cb, hawk_rules_rollback_cb);
} hawk_plugin_definition;
typedef void(register_plugin_cb)(const char*, hawk_plugin_definition);
typedef struct
{
register_plugin_cb* register_plugin;
} hawk_plugin_registry;
extern hawk_plugin_registry plugin_registry;
#define HAWK_REGISTER_PLUGIN(name, definition) \
void name##_hawk_plugin_init(void) __attribute__((constructor)); \
void name##_hawk_plugin_init(void) \
{ \
plugin_registry.register_plugin(#name, definition); \
}
#endif //HAWK_H

View File

@@ -0,0 +1,59 @@
/*
Copyright (C) 2020 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 "library.h"
#include "exception.h"
#include <dlfcn.h>
libhawk::library::library(const std::string &filename):
m_library_filename(filename){};
bool libhawk::library::load()
{
library_handle handler = nullptr;
handler = dlopen(m_library_filename.c_str(), RTLD_LAZY);
if(!handler)
{
std::string errmsg(dlerror());
throw hawk_library_load_exception(m_library_filename, errmsg);
}
m_library_handle.store(handler);
return (handler != nullptr);
}
bool libhawk::library::unload()
{
if(!m_library_handle.load())
{
return false;
}
library_handle handler = m_library_handle.load();
if(!dlclose(handler))
{
std::string errmsg(dlerror());
throw hawk_library_unload_exception(m_library_filename, errmsg);
return false;
}
m_library_handle.store(nullptr);
return true;
}
bool libhawk::library::is_loaded() const
{
return m_library_handle && m_library_handle.load();
}

View File

@@ -0,0 +1,38 @@
/*
Copyright (C) 2020 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.
*/
#pragma once
#include <string>
#include <atomic>
namespace libhawk
{
class library
{
public:
using library_handle = void *;
library(const std::string &filename);
bool load();
bool unload();
bool is_loaded() const;
~library();
private:
const std::string m_library_filename;
std::atomic<library_handle> m_library_handle;
};
}; // namespace libhawk

View File

@@ -0,0 +1,86 @@
/*
Copyright (C) 2020 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 "lifecycle.h"
#include "exception.h"
std::map<std::string, hawk_plugin_definition> *libhawk::g_plugins;
void libhawk_register_plugin(const char *name, hawk_plugin_definition def)
{
if(libhawk::g_plugins == nullptr)
{
libhawk::g_plugins = new std::map<std::string, hawk_plugin_definition>();
}
auto name_str = std::string(name);
auto plugin = libhawk::g_plugins->find(name_str);
if(plugin != libhawk::g_plugins->end())
{
throw libhawk::hawk_exception("cannot register an already registered plugin: " + name_str);
}
libhawk::g_plugins->insert(std::make_pair(name_str, def));
};
hawk_plugin_registry plugin_registry = {
.register_plugin = &libhawk_register_plugin,
};
void libhawk::lifecycle::start()
{
if(g_plugins == nullptr)
{
throw hawk_exception("no libhawk plugins registered");
}
for(const auto &plugin : *g_plugins)
{
if(plugin.second.hawk_init != nullptr)
{
plugin.second.hawk_init();
}
}
}
void libhawk::lifecycle::stop()
{
for(const auto &plugin : *g_plugins)
{
if(plugin.second.hawk_destroy != nullptr)
{
plugin.second.hawk_destroy();
}
}
}
void libhawk::lifecycle::watch_rules(
hawk_rules_begin_cb begin_cb,
hawk_rules_insert_cb insert_cb,
hawk_rules_commit_cb commit_cb,
hawk_rules_rollback_cb rollback_cb,
const std::string &plugin_name)
{
auto plugin = g_plugins->find(plugin_name);
if(plugin == g_plugins->end())
{
throw hawk_plugin_exception(plugin_name, "cannot watch_rules on a non existing plugin");
}
if(plugin->second.hawk_watch_rules == nullptr)
{
throw hawk_plugin_exception(plugin_name, "plugin does not implement hawk_watch_rules");
}
plugin->second.hawk_watch_rules(begin_cb, insert_cb, commit_cb, rollback_cb);
}

View File

@@ -0,0 +1,39 @@
/*
Copyright (C) 2020 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.
*/
#pragma once
#include <map>
#include <string>
#include <vector>
#include "hawk.h"
namespace libhawk
{
extern std::map<std::string, hawk_plugin_definition>* g_plugins;
namespace lifecycle
{
void start();
void stop();
void watch_rules(hawk_rules_begin_cb begin_cb,
hawk_rules_insert_cb insert_cb,
hawk_rules_commit_cb commit_cb,
hawk_rules_rollback_cb rollback_cb,
const std::string& plugin_name);
} // namespace lifecycle
} // namespace libhawk