From 1bd84b9895b5cf4bc88196f6ec698f21510bc89f Mon Sep 17 00:00:00 2001 From: Mark Stemm Date: Mon, 25 Sep 2017 07:08:49 -0700 Subject: [PATCH] Rework config file handling These changes allow for a local rules file that will be preserved across upgrades and allows the main rules file to be overwritten across upgrades. - Move all config/rules files below /etc/falco/ - Add a "local rules" file /etc/falco/falco_rules.local.yaml. The intent is that it contains modifications/deltas to the main rules file /etc/falco/falco_rules.yaml. The main falco_rules.yaml should be treated as immutable. - All config files are flagged so they are not overwritten on upgrade. - Change the handling of the config item "rules_file" in falco.yaml to allow a list of files. By default, this list contains: [/etc/falco/falco_rules.yaml, /etc/falco/falco_rules.local.yaml]. Also change rpm/debian packaging to ensure that the above files are preserved across upgrades: - Use relative paths for share/bin dirs. This ensures that when packaged as rpms they won't be flagged as config files. - In debian packaging, flag /etc/falco/{falco.yaml,falco_rules.yaml,falco_rules.local.yaml} as conffiles. That way they are preserved across upgrades if modified. - In rpm packaging when using cmake, any files installed with an absolute path are automatically flagged as %config. The only files directly installed are now the config files, so that addresses the problem. --- CMakeLists.txt | 8 ++++---- cpack/debian/conffiles | 2 ++ falco.yaml | 14 ++++++++++++-- rules/CMakeLists.txt | 16 +++++++++++++--- rules/falco_rules.local.yaml | 13 +++++++++++++ userspace/falco/configuration.cpp | 15 ++++++++++++++- userspace/falco/configuration.h | 24 ++++++++++++++++++++++++ 7 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 cpack/debian/conffiles create mode 100644 rules/falco_rules.local.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index bc4866e6c61..61d610dcd5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ if(NOT DEFINED FALCO_VERSION) endif() if(NOT DEFINED FALCO_ETC_DIR) - set(FALCO_ETC_DIR "/etc") + set(FALCO_ETC_DIR "/etc/falco") endif() if(NOT CMAKE_BUILD_TYPE) @@ -399,8 +399,8 @@ add_subdirectory("${SYSDIG_DIR}/userspace/libscap" "${PROJECT_BINARY_DIR}/usersp add_subdirectory("${SYSDIG_DIR}/userspace/libsinsp" "${PROJECT_BINARY_DIR}/userspace/libsinsp") set(FALCO_SINSP_LIBRARY sinsp) -set(FALCO_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/falco) -set(FALCO_BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin) +set(FALCO_SHARE_DIR share/falco) +set(FALCO_BIN_DIR bin) add_subdirectory(scripts) add_subdirectory(userspace/engine) add_subdirectory(userspace/falco) @@ -422,7 +422,7 @@ set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig ") set(CPACK_DEBIAN_PACKAGE_SECTION "utils") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.sysdig.org") set(CPACK_DEBIAN_PACKAGE_DEPENDS "dkms (>= 2.1.0.0)") -set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm") +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm;${PROJECT_SOURCE_DIR}/cpack/debian/conffiles") set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org") diff --git a/cpack/debian/conffiles b/cpack/debian/conffiles new file mode 100644 index 00000000000..c4e8595764f --- /dev/null +++ b/cpack/debian/conffiles @@ -0,0 +1,2 @@ +/etc/falco/falco.yaml +/etc/falco/falco_rules.local.yaml diff --git a/falco.yaml b/falco.yaml index d407b75bccc..3bbb9f20ee9 100644 --- a/falco.yaml +++ b/falco.yaml @@ -1,5 +1,15 @@ -# File containing Falco rules, loaded at startup. -rules_file: /etc/falco_rules.yaml +# File(s) containing Falco rules, loaded at startup. +# +# falco_rules.yaml ships with the falco package and is overridden with +# every new software version. falco_rules.local.yaml is only created +# if it doesn't exist. If you want to customize the set of rules, add +# your customizations to falco_rules.local.yaml. +# +# 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 # Whether to output events in json or text json_output: false diff --git a/rules/CMakeLists.txt b/rules/CMakeLists.txt index 94a0ad9cc3d..e15309a0b31 100644 --- a/rules/CMakeLists.txt +++ b/rules/CMakeLists.txt @@ -1,9 +1,10 @@ if(NOT DEFINED FALCO_ETC_DIR) - set(FALCO_ETC_DIR "/etc") + set(FALCO_ETC_DIR "/etc/falco") endif() if(NOT DEFINED FALCO_RULES_DEST_FILENAME) set(FALCO_RULES_DEST_FILENAME "falco_rules.yaml") + set(FALCO_LOCAL_RULES_DEST_FILENAME "falco_rules.local.yaml") endif() if(DEFINED FALCO_COMPONENT) @@ -11,9 +12,18 @@ install(FILES falco_rules.yaml COMPONENT "${FALCO_COMPONENT}" DESTINATION "${FALCO_ETC_DIR}" RENAME "${FALCO_RULES_DEST_FILENAME}") + +install(FILES falco_rules.local.yaml + COMPONENT "${FALCO_COMPONENT}" + DESTINATION "${FALCO_ETC_DIR}" + RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}") else() install(FILES falco_rules.yaml - DESTINATION "${FALCO_ETC_DIR}" - RENAME "${FALCO_RULES_DEST_FILENAME}") + DESTINATION "${FALCO_ETC_DIR}" + RENAME "${FALCO_RULES_DEST_FILENAME}") + +install(FILES falco_rules.local.yaml + DESTINATION "${FALCO_ETC_DIR}" + RENAME "${FALCO_LOCAL_RULES_DEST_FILENAME}") endif() diff --git a/rules/falco_rules.local.yaml b/rules/falco_rules.local.yaml new file mode 100644 index 00000000000..3c8e3bb5aa8 --- /dev/null +++ b/rules/falco_rules.local.yaml @@ -0,0 +1,13 @@ +#################### +# Your custom rules! +#################### + +# Add new rules, like this one +# - rule: The program "sudo" is run in a container +# desc: An event will trigger every time you run sudo in a container +# condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = sudo +# output: "Sudo run in container (user=%user.name %container.info parent=%proc.pname cmdline=%proc.cmdline)" +# priority: ERROR +# tags: [users, container] + +# Or override/append to any rule, macro, or list from the Default Rules diff --git a/userspace/falco/configuration.cpp b/userspace/falco/configuration.cpp index 74d828a0bc2..0f19dcc7610 100644 --- a/userspace/falco/configuration.cpp +++ b/userspace/falco/configuration.cpp @@ -51,7 +51,20 @@ void falco_configuration::init(string conf_filename, list &cmdline_optio init_cmdline_options(cmdline_options); - m_rules_filenames.push_back(m_config->get_scalar("rules_file", "/etc/falco_rules.yaml")); + list rules_files; + + m_config->get_sequence>(rules_files, string("rules_file")); + + for(auto &file : rules_files) + { + // Here, we only include files that exist + struct stat buffer; + if(stat(file.c_str(), &buffer) == 0) + { + m_rules_filenames.push_back(file); + } + } + m_json_output = m_config->get_scalar("json_output", false); falco_outputs::output_config file_output; diff --git a/userspace/falco/configuration.h b/userspace/falco/configuration.h index 42f3b681bb2..e00ccfd9570 100644 --- a/userspace/falco/configuration.h +++ b/userspace/falco/configuration.h @@ -18,6 +18,9 @@ along with falco. If not, see . #pragma once +#include +#include +#include #include #include #include @@ -127,6 +130,27 @@ class yaml_configuration } } + // called with the last variadic arg (where the sequence is expected to be found) + template + void get_sequence(T& ret, const std::string& name) + { + YAML::Node child_node = m_root[name]; + if(child_node.IsDefined()) + { + if(child_node.IsSequence()) + { + for(const YAML::Node& item : child_node) + { + ret.insert(ret.end(), item.as()); + } + } + else if(child_node.IsScalar()) + { + ret.insert(ret.end(), child_node.as()); + } + } + } + private: YAML::Node m_root; };