diff --git a/.travis.yml b/.travis.yml index 62fcdc6a..8056a87f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,7 +48,7 @@ script: - make package - cp falco*.deb ../docker/local - cd ../docker/local - - docker build -t sysdig/falco:test . + - docker build -t falcosecurity/falco:test . - cd ../.. - sudo test/run_regression_tests.sh $TRAVIS_BRANCH notifications: diff --git a/rules/application_rules.yaml b/rules/application_rules.yaml index 2c03fbf7..6d19a203 100644 --- a/rules/application_rules.yaml +++ b/rules/application_rules.yaml @@ -16,6 +16,8 @@ # limitations under the License. # +- required_engine_version: 2 + ################################################################ # By default all application-related rules are disabled for # performance reasons. Depending on the application(s) you use, diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index 901e9b01..52b5d8ec 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -16,6 +16,16 @@ # limitations under the License. # +# See xxx for details on falco engine and rules versioning. Currently, +# this specific rules file is compatible with engine version 0 +# (e.g. falco releases <= 0.13.1), so we'll keep the +# required_engine_version lines commented out, so maintain +# compatibility with older falco releases. With the first incompatible +# change to this rules file, we'll uncomment this line and set it to +# the falco engine version in use at the time. +# +#- required_engine_version: 2 + # Currently disabled as read/write are ignored syscalls. The nearly # similar open_write/open_read check for files being opened for # reading/writing. diff --git a/rules/k8s_audit_rules.yaml b/rules/k8s_audit_rules.yaml index 6b2b5b67..e92e4c56 100644 --- a/rules/k8s_audit_rules.yaml +++ b/rules/k8s_audit_rules.yaml @@ -1,3 +1,22 @@ +# +# Copyright (C) 2016-2018 Draios Inc dba Sysdig. +# +# This file is part of falco. +# +# 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. +# +- required_engine_version: 2 + # Generally only consider audit events once the response has completed - list: k8s_audit_stages items: ["ResponseComplete"] diff --git a/test/falco_tests.yaml b/test/falco_tests.yaml index 241be62f..ba0f00f0 100644 --- a/test/falco_tests.yaml +++ b/test/falco_tests.yaml @@ -18,7 +18,7 @@ trace_files: !mux docker_package: - package: docker:sysdig/falco:test + package: docker:falcosecurity/falco:test detect: True detect_level: WARNING rules_file: /host/rules/rule_names_with_spaces.yaml @@ -33,7 +33,7 @@ trace_files: !mux # just to see if falco can load the driver. docker_package_local_driver: - package: docker:sysdig/falco:test + package: docker:falcosecurity/falco:test addl_docker_run_args: -v /dev/null:/usr/sbin/dkms copy_local_driver: True detect: False @@ -764,3 +764,10 @@ trace_files: !mux rules_file: - rules/skip_unknown_unspec.yaml trace_file: trace_files/cat_write.scap + + engine_version_mismatch: + exit_status: 1 + stderr_contains: Rules require engine version 9999999, but engine version is + rules_file: + - rules/engine_version_mismatch.yaml + trace_file: trace_files/cat_write.scap \ No newline at end of file diff --git a/test/rules/engine_version_mismatch.yaml b/test/rules/engine_version_mismatch.yaml new file mode 100644 index 00000000..e940bde4 --- /dev/null +++ b/test/rules/engine_version_mismatch.yaml @@ -0,0 +1,34 @@ +# +# Copyright (C) 2016-2018 Draios Inc dba Sysdig. +# +# This file is part of falco. +# +# 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. +# + +- required_engine_version: 9999999 + +- list: cat_binaries + items: [cat] + +- list: cat_capable_binaries + items: [cat_binaries] + +- macro: is_cat + condition: proc.name in (cat_capable_binaries) + +- rule: open_from_cat + desc: A process named cat does an open + condition: evt.type=open and is_cat + output: "An open was seen (command=%proc.cmdline)" + priority: WARNING \ No newline at end of file diff --git a/test/rules/single_rule.yaml b/test/rules/single_rule.yaml index b9302358..ba25d3fe 100644 --- a/test/rules/single_rule.yaml +++ b/test/rules/single_rule.yaml @@ -15,6 +15,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +- required_engine_version: 2 + - list: cat_binaries items: [cat] diff --git a/userspace/engine/falco_engine.cpp b/userspace/engine/falco_engine.cpp index 2622f3f7..eff6e6a3 100644 --- a/userspace/engine/falco_engine.cpp +++ b/userspace/engine/falco_engine.cpp @@ -23,6 +23,7 @@ limitations under the License. #include #include "falco_engine.h" +#include "falco_engine_version.h" #include "config_falco_engine.h" #include "formats.h" @@ -76,6 +77,71 @@ falco_engine::~falco_engine() } } +uint32_t falco_engine::engine_version() +{ + return (uint32_t) FALCO_ENGINE_VERSION; +} + +#define DESCRIPTION_TEXT_START 16 + +#define CONSOLE_LINE_LEN 79 + +void falco_engine::list_fields(bool names_only) +{ + for(auto &chk_field : json_factory().get_fields()) + { + if(!names_only) + { + printf("\n----------------------\n"); + printf("Field Class: %s (%s)\n\n", chk_field.name.c_str(), chk_field.desc.c_str()); + } + + for(auto &field : chk_field.fields) + { + uint32_t l, m; + + printf("%s", field.name.c_str()); + + if(names_only) + { + printf("\n"); + continue; + } + uint32_t namelen = field.name.size(); + + if(namelen >= DESCRIPTION_TEXT_START) + { + printf("\n"); + namelen = 0; + } + + for(l = 0; l < DESCRIPTION_TEXT_START - namelen; l++) + { + printf(" "); + } + + size_t desclen = field.desc.size(); + + for(l = 0; l < desclen; l++) + { + if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0) + { + printf("\n"); + + for(m = 0; m < DESCRIPTION_TEXT_START; m++) + { + printf(" "); + } + } + + printf("%c", field.desc.at(l)); + } + + printf("\n"); + } + } +} + void falco_engine::load_rules(const string &rules_content, bool verbose, bool all_events) { // The engine must have been given an inspector by now. diff --git a/userspace/engine/falco_engine.h b/userspace/engine/falco_engine.h index 93d3c930..17036992 100644 --- a/userspace/engine/falco_engine.h +++ b/userspace/engine/falco_engine.h @@ -53,6 +53,15 @@ public: falco_engine(bool seed_rng=true, const std::string& alternate_lua_dir=FALCO_ENGINE_SOURCE_LUA_DIR); virtual ~falco_engine(); + // A given engine has a version which identifies the fields + // and rules file format it supports. This version will change + // any time the code that handles rules files, expression + // fields, etc, changes. + static uint32_t engine_version(); + + // Print to stdout (using printf) a description of each field supported by this engine. + void list_fields(bool names_only=false); + // // Load rules either directly or from a filename. // diff --git a/userspace/engine/falco_engine_version.h b/userspace/engine/falco_engine_version.h new file mode 100644 index 00000000..7d45d3a6 --- /dev/null +++ b/userspace/engine/falco_engine_version.h @@ -0,0 +1,27 @@ +/* +Copyright (C) 2016-2018 Draios Inc dba Sysdig. + +This file is part of falco. + +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. + +*/ + +// The version of rules/filter fields/etc supported by this falco +// engine. +#define FALCO_ENGINE_VERSION (2) + +// 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 "32a91c003ab34f198dcb4c3100fbfb22bf402ad36549f193afa43d73f1f2eba3" diff --git a/userspace/engine/lua/rule_loader.lua b/userspace/engine/lua/rule_loader.lua index 8c81d62b..1f3f84e3 100644 --- a/userspace/engine/lua/rule_loader.lua +++ b/userspace/engine/lua/rule_loader.lua @@ -215,7 +215,12 @@ function load_rules(sinsp_lua_parser, error ("Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.") end - if (v['macro']) then + if (v['required_engine_version']) then + if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then + error("Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr)) + end + + elseif (v['macro']) then if v['source'] == nil then v['source'] = "syscall" diff --git a/userspace/engine/rules.cpp b/userspace/engine/rules.cpp index 805c5eb9..eb541718 100644 --- a/userspace/engine/rules.cpp +++ b/userspace/engine/rules.cpp @@ -33,6 +33,7 @@ const static struct luaL_reg ll_falco_rules [] = {"add_filter", &falco_rules::add_filter}, {"add_k8s_audit_filter", &falco_rules::add_k8s_audit_filter}, {"enable_rule", &falco_rules::enable_rule}, + {"engine_version", &falco_rules::engine_version}, {NULL,NULL} }; @@ -204,6 +205,21 @@ void falco_rules::enable_rule(string &rule, bool enabled) m_engine->enable_rule(rule, enabled); } +int falco_rules::engine_version(lua_State *ls) +{ + if (! lua_islightuserdata(ls, -1)) + { + lua_pushstring(ls, "Invalid arguments passed to engine_version()"); + lua_error(ls); + } + + falco_rules *rules = (falco_rules *) lua_topointer(ls, -1); + + lua_pushnumber(ls, rules->m_engine->engine_version()); + + return 1; +} + void falco_rules::load_rules(const string &rules_content, bool verbose, bool all_events, string &extra, bool replace_container_info, diff --git a/userspace/engine/rules.h b/userspace/engine/rules.h index 0e2ded36..326936eb 100644 --- a/userspace/engine/rules.h +++ b/userspace/engine/rules.h @@ -49,6 +49,7 @@ class falco_rules static int add_filter(lua_State *ls); static int add_k8s_audit_filter(lua_State *ls); static int enable_rule(lua_State *ls); + static int engine_version(lua_State *ls); private: void clear_filters(); diff --git a/userspace/falco/CMakeLists.txt b/userspace/falco/CMakeLists.txt index cc038bd5..1713f94b 100644 --- a/userspace/falco/CMakeLists.txt +++ b/userspace/falco/CMakeLists.txt @@ -48,6 +48,17 @@ target_link_libraries(falco configure_file(config_falco.h.in config_falco.h) +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" + ) + +# add_custom_target(verify_engine_fields +# DEPENDS verify_engine_fields.sh falco_engine.h) + +# add_dependencies(verify_engine_fields falco) + install(TARGETS falco DESTINATION ${FALCO_BIN_DIR}) install(DIRECTORY lua DESTINATION ${FALCO_SHARE_DIR} diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 178fee0d..8b43937f 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -114,6 +114,7 @@ static void usage() " The API servers can also be specified via the environment variable\n" " FALCO_MESOS_API.\n" " -M Stop collecting after reached.\n" + " -N When used with --list, only print field names.\n" " -o, --option = Set the value of option to . Overrides values in configuration file.\n" " can be a two-part .\n" " -p , --print=\n" @@ -335,59 +336,7 @@ static void print_all_ignored_events(sinsp *inspector) printf("\n"); } -// Must match the value in the zsh tab completion -#define DESCRIPTION_TEXT_START 16 - -#define CONSOLE_LINE_LEN 79 - -static void list_falco_fields(falco_engine *engine) -{ - for(auto &chk_field : engine->json_factory().get_fields()) - { - printf("\n----------------------\n"); - printf("Field Class: %s (%s)\n\n", chk_field.name.c_str(), chk_field.desc.c_str()); - - for(auto &field : chk_field.fields) - { - uint32_t l, m; - - printf("%s", field.name.c_str()); - uint32_t namelen = field.name.size(); - - if(namelen >= DESCRIPTION_TEXT_START) - { - printf("\n"); - namelen = 0; - } - - for(l = 0; l < DESCRIPTION_TEXT_START - namelen; l++) - { - printf(" "); - } - - size_t desclen = field.desc.size(); - - for(l = 0; l < desclen; l++) - { - if(l % (CONSOLE_LINE_LEN - DESCRIPTION_TEXT_START) == 0 && l != 0) - { - printf("\n"); - - for(m = 0; m < DESCRIPTION_TEXT_START; m++) - { - printf(" "); - } - } - - printf("%c", field.desc.at(l)); - } - - printf("\n"); - } - } -} - -static void list_source_fields(falco_engine *engine, bool verbose, std::string &source) +static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source) { if(source.size() > 0 && !(source == "syscall" || source == "k8s_audit")) @@ -396,11 +345,11 @@ static void list_source_fields(falco_engine *engine, bool verbose, std::string & } if(source == "" || source == "syscall") { - list_fields(verbose, false); + list_fields(verbose, false, names_only); } if(source == "" || source == "k8s_audit") { - list_falco_fields(engine); + engine->list_fields(names_only); } } @@ -428,6 +377,7 @@ int falco_init(int argc, char **argv) list validate_rules_filenames; string stats_filename = ""; bool verbose = false; + bool names_only = false; bool all_events = false; string* k8s_api = 0; string* k8s_api_cert = 0; @@ -489,7 +439,7 @@ 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:o:P:p:r:S:s:T:t:UvV:w:", + "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UvV:w:", long_options, &long_index)) != -1) { switch(op) @@ -546,6 +496,9 @@ int falco_init(int argc, char **argv) throw sinsp_exception(string("invalid duration") + optarg); } break; + case 'N': + names_only = true; + break; case 'o': cmdline_options.push_back(optarg); break; @@ -652,7 +605,7 @@ int falco_init(int argc, char **argv) if(list_flds) { - list_source_fields(engine, verbose, list_flds_source); + list_source_fields(engine, verbose, names_only, list_flds_source); return EXIT_SUCCESS; } diff --git a/userspace/falco/verify_engine_fields.sh b/userspace/falco/verify_engine_fields.sh new file mode 100644 index 00000000..578e0f0d --- /dev/null +++ b/userspace/falco/verify_engine_fields.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -euo pipefail + +SOURCE_DIR=$1 +OPENSSL=../../openssl-prefix/src/openssl/target/bin/openssl + +if ! command -v ${OPENSSL} version > /dev/null 2>&1; then + echo "No openssl command at ${OPENSSL}" + exit 1 +fi + +NEW_CHECKSUM=$(./falco --list -N | ${OPENSSL} dgst -sha256 | awk '{print $2}') +CUR_CHECKSUM=$(grep FALCO_FIELDS_CHECKSUM ${SOURCE_DIR}/userspace/engine/falco_engine_version.h | awk '{print $3}' | sed -e 's/"//g') + + +if [ $NEW_CHECKSUM != $CUR_CHECKSUM ]; then + echo "Set of fields supported by falco/sysdig libraries has changed (new checksum $NEW_CHECKSUM != old checksum $CUR_CHECKSUM)." + echo "Update checksum and/or version in falco_engine_version.h." + exit 1 +fi + +exit 0