Compare commits

..

26 Commits

Author SHA1 Message Date
Mark Stemm
194a017d8f Merge remote-tracking branch 'origin/dev' 2019-06-12 09:14:36 -07:00
Mark Stemm
ff376d312b Merge remote-tracking branch 'origin/dev' 2019-06-07 15:15:48 -07:00
Mark Stemm
807c00b827 Merge remote-tracking branch 'origin/dev' 2019-06-07 15:09:50 -07:00
Mark Stemm
db419459aa Merge remote-tracking branch 'origin/dev' 2019-05-13 13:45:34 -07:00
Mark Stemm
36a095ccb3 Merge remote-tracking branch 'origin/dev' 2019-05-13 13:43:10 -07:00
Xiang Dai
f4b0b3c096 falco.yaml: change default program_output action (#507)
falco-CLA-1.0-signed-off-by: Xiang Dai <764524258@qq.com>1
2019-02-20 09:18:45 -08:00
Xiang Dai
e546555de8 fix k8s install issue (#506)
falco-CLA-1.0-signed-off-by: Xiang Dai <764524258@qq.com>
2019-02-20 09:17:34 -08:00
Xiang Dai
1fb53eefdb Update output (#511)
falco-CLA-1.0-signed-off-by: Xiang Dai <764524258@qq.com>

Signed-off-by: Xiang Dai <764524258@qq.com>
2019-02-11 13:01:49 -08:00
Mark Stemm
44e88f28a4 Merge remote-tracking branch 'origin/dev' 2019-02-10 14:57:00 -08:00
Mark Stemm
62c1a0440d Merge remote-tracking branch 'origin/dev' 2019-02-06 16:38:53 -08:00
Mark Stemm
ddf55d3c8e Merge remote-tracking branch 'origin/dev' 2019-01-17 07:28:18 -08:00
Mark Stemm
bd7a9733fd Merge branch 'dev' 2018-11-09 13:41:29 -08:00
Mark Stemm
ff299c1d43 Merge remote-tracking branch 'origin/dev' 2018-09-11 13:33:56 -07:00
Mark Stemm
5e38f130cc Merge remote-tracking branch 'origin/dev' 2018-09-11 11:02:10 -07:00
Mark Stemm
470710366b Merge remote-tracking branch 'origin/dev' 2018-07-31 12:06:09 -07:00
Mark Stemm
6acb13e6bb Merge branch 'dev' 2018-07-24 17:33:24 -07:00
David Archer
b496116fe3 Don't make driver compilation fail when kernel is compiled with CONFIG_ORC_UNWINDER or CONFIG_STACK_VALIDATION. (#362)
sysdig-CLA-1.0-signed-off-by: David Archer <darcher@gmail.com>
2018-04-30 14:30:39 -07:00
Mark Stemm
2a0911dcfd Merge branch 'dev' 2018-04-24 16:21:18 -07:00
Mark Stemm
94df00e512 Merge branch 'dev' 2018-01-18 09:07:00 -08:00
Mark Stemm
3ee76637f4 Merge branch 'dev' 2018-01-17 20:30:28 -08:00
Mark Stemm
e8aee19f6c Merge remote-tracking branch 'origin/dev', 0.8.1 2017-10-10 10:49:27 -07:00
Mark Stemm
74556e5f6e Merge branch 'dev' 2017-10-09 17:17:12 -07:00
Mark Stemm
809d20c294 Merge pull request #246 from draios/dev
Merging for 0.7.0
2017-05-30 13:30:39 -07:00
Mark Stemm
b0ae29c23a Merge branch 'dev' 2017-05-15 11:12:11 -07:00
Mark Stemm
d1b6b2be87 Merge pull request #229 from draios/dev
Merging for 0.6.0
2017-03-29 16:00:06 -07:00
Mark Stemm
e00181d553 Merge pull request #174 from draios/dev
Merging for 0.5.0
2016-12-22 13:25:32 -08:00
48 changed files with 334 additions and 1458 deletions

View File

@@ -1,16 +0,0 @@
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -8
BreakBeforeBraces: Allman
BreakConstructorInitializers: AfterColon
ColumnLimit: 0
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
DerivePointerAlignment: true
IndentWidth: 8
SortIncludes: false
SpaceAfterTemplateKeyword: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeParens: Never
UseTab: Always

View File

@@ -7,66 +7,48 @@
-->
**What type of PR is this?**
> Uncomment one (or more) `/kind <>` lines:
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind bug
> /kind cleanup
> /kind design
> /kind documentation
> /kind failing-test
> /kind feature
> /kind flaky-test
> If contributing rules or changes to rules, please make sure to also uncomment one of the following line:
> If contributing rules or changes to rules, please make sure to uncomment the appropriate kind
> /kind rule-update
> /kind rule-create
> /kind rule/update
> /kind rule/create
**Any specific area of the project related to this PR?**
> Uncomment one (or more) `/area <>` lines:
> /area engine
> /area rules
> /area deployment
> /area integrations
> /area examples
**What this PR does / why we need it**:
**Which issue(s) this PR fixes**:
<!--
Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
If PR is `kind/failing-tests` or `kind/flaky-test`, please post the related issues/tests in a comment and do not use `Fixes`.
-->
Fixes #
**Special notes for your reviewer**:
**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, prepend the string "action required:".
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required:".
For example, `action required: change the API interface of the rule engine`.
-->
```release-note
```

2
.gitignore vendored
View File

@@ -12,8 +12,6 @@ test/results*.json.*
userspace/falco/lua/re.lua
userspace/falco/lua/lpeg.so
userspace/engine/lua/lyaml
userspace/engine/lua/lyaml.lua
docker/event-generator/event_generator
docker/event-generator/mysqld

View File

@@ -36,7 +36,6 @@ script:
- cd build
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder cmake
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder package
- docker run --user $(id -u):$(id -g) -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=1 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-builder tests
- docker run -v /boot:/boot:ro -v /var/run/docker.sock:/var/run/docker.sock -v /etc/passwd:/etc/passwd:ro -e MAKE_JOBS=4 -v $TRAVIS_BUILD_DIR/..:/source -v $TRAVIS_BUILD_DIR/build:/build falcosecurity/falco-tester
notifications:
webhooks:

View File

@@ -2,26 +2,6 @@
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
## v0.15.3
Released 2019-06-12
## Major Changes
* None.
## Minor Changes
* None.
## Bug Fixes
* Fix kernel module compilation for kernels < 3.11 [[#sysdig/1436](https://github.com/draios/sysdig/pull/1436)]
## Rule Changes
* None.
## v0.15.2
Released 2019-06-12

View File

@@ -75,10 +75,7 @@ endif()
set(CMD_MAKE make)
if(NOT SYSDIG_DIR)
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
endif()
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
# make luaJIT work on OS X
if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000")
@@ -307,7 +304,7 @@ else()
URL "http://s3.amazonaws.com/download.draios.com/dependencies/curl-7.61.0.tar.bz2"
URL_MD5 "31d0a9f48dc796a7db351898a1e5058a"
# END CHANGE for CVE-2017-8816, CVE-2017-8817, CVE-2017-8818, CVE-2018-1000007
CONFIGURE_COMMAND ./configure ${CURL_SSL_OPTION} --disable-shared --enable-optimize --disable-curldebug --disable-rt --enable-http --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-sspi --disable-ntlm-wb --disable-tls-srp --without-winssl --without-darwinssl --without-polarssl --without-cyassl --without-nss --without-axtls --without-ca-path --without-ca-bundle --without-libmetalink --without-librtmp --without-winidn --without-libidn2 --without-libpsl --without-nghttp2 --without-libssh2 --disable-threaded-resolver --without-brotli
CONFIGURE_COMMAND ./configure ${CURL_SSL_OPTION} --disable-shared --enable-optimize --disable-curldebug --disable-rt --enable-http --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-sspi --disable-ntlm-wb --disable-tls-srp --without-winssl --without-darwinssl --without-polarssl --without-cyassl --without-nss --without-axtls --without-ca-path --without-ca-bundle --without-libmetalink --without-librtmp --without-winidn --without-libidn --without-nghttp2 --without-libssh2 --disable-threaded-resolver
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
INSTALL_COMMAND "")
@@ -381,6 +378,7 @@ endif()
# Libyaml
#
option(USE_BUNDLED_LIBYAML "Enable building of the bundled libyaml" ${USE_BUNDLED_DEPS})
if(NOT USE_BUNDLED_LIBYAML)
# Note: to distinguish libyaml.a and yaml.a we specify a full
# file name here, so you'll have to arrange for static
@@ -400,7 +398,6 @@ else()
endif()
set(LIBYAML_SRC "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml/src")
set(LIBYAML_INCLUDE "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml/include")
set(LIBYAML_LIB "${LIBYAML_SRC}/.libs/libyaml.a")
message(STATUS "Using bundled libyaml in '${LIBYAML_SRC}'")
ExternalProject_Add(libyaml
@@ -416,6 +413,7 @@ endif()
# lyaml
#
option(USE_BUNDLED_LYAML "Enable building of the bundled lyaml" ${USE_BUNDLED_DEPS})
if(NOT USE_BUNDLED_LYAML)
# Note: to distinguish libyaml.a and yaml.a we specify a full
# file name here, so you'll have to arrange for static
@@ -437,15 +435,14 @@ else()
if(USE_BUNDLED_LIBYAML)
list(APPEND LYAML_DEPENDENCIES "libyaml")
endif()
ExternalProject_Add(lyaml
DEPENDS ${LYAML_DEPENDENCIES}
URL "http://s3.amazonaws.com/download.draios.com/dependencies/lyaml-release-v6.0.tar.gz"
URL_MD5 "dc3494689a0dce7cf44e7a99c72b1f30"
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./configure --enable-static LIBS=-L${LIBYAML_SRC}/.libs CFLAGS=-I${LIBYAML_INCLUDE} CPPFLAGS=-I${LIBYAML_INCLUDE} LUA_INCLUDE=-I${LUAJIT_INCLUDE} LUA=${LUAJIT_SRC}/luajit
INSTALL_COMMAND sh -c "cp -R ${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/lib/* ${PROJECT_SOURCE_DIR}/userspace/engine/lua")
URL_MD5 "dc3494689a0dce7cf44e7a99c72b1f30"
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./configure --enable-static LIBS=-L../../../libyaml-prefix/src/libyaml/src/.libs CFLAGS=-I../../../libyaml-prefix/src/libyaml/include CPPFLAGS=-I../../../libyaml-prefix/src/libyaml/include LUA_INCLUDE=-I../../../luajit-prefix/src/luajit/src LUA=../../../luajit-prefix/src/luajit/src/luajit
INSTALL_COMMAND sh -c "cp -R ${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/lib/* ${PROJECT_SOURCE_DIR}/userspace/engine/lua")
endif()
option(USE_BUNDLED_TBB "Enable building of the bundled tbb" ${USE_BUNDLED_DEPS})
@@ -589,7 +586,7 @@ else()
URL_MD5 "2fc42c182a0ed1b48ad77397f76bb3bc"
CONFIGURE_COMMAND ""
# TODO what if using system openssl, protobuf or cares?
BUILD_COMMAND sh -c "CFLAGS=-Wno-implicit-fallthrough CXXFLAGS=\"-Wno-ignored-qualifiers -Wno-stringop-truncation\" HAS_SYSTEM_ZLIB=false LDFLAGS=-static PATH=${PROTOC_DIR}:$ENV{PATH} PKG_CONFIG_PATH=${OPENSSL_BUNDLE_DIR}:${PROTOBUF_SRC}:${CARES_SRC} make grpc_cpp_plugin static_cxx static_c"
BUILD_COMMAND HAS_SYSTEM_ZLIB=false LDFLAGS=-static PATH=${PROTOC_DIR}:$ENV{PATH} PKG_CONFIG_PATH=${OPENSSL_BUNDLE_DIR}:${PROTOBUF_SRC}:${CARES_SRC} make grpc_cpp_plugin static_cxx static_c
BUILD_IN_SOURCE 1
BUILD_BYPRODUCTS ${GRPC_LIB} ${GRPCPP_LIB}
# TODO s390x support
@@ -606,17 +603,8 @@ add_subdirectory(test)
add_subdirectory(rules)
add_subdirectory(docker)
# Add path for custom CMake modules used to build dependencies from Sysdig (libscap, libsinsp)
list(APPEND CMAKE_MODULE_PATH
"${SYSDIG_DIR}/cmake/modules")
# Add path for custom CMake modules
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
add_subdirectory("${SYSDIG_DIR}/driver" "${PROJECT_BINARY_DIR}/driver")
include(FindMakedev)
endif()
add_subdirectory("${SYSDIG_DIR}/userspace/libscap" "${PROJECT_BINARY_DIR}/userspace/libscap")
add_subdirectory("${SYSDIG_DIR}/userspace/libsinsp" "${PROJECT_BINARY_DIR}/userspace/libsinsp")
@@ -628,7 +616,6 @@ set(FALCO_BIN_DIR bin)
add_subdirectory(scripts)
add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
add_subdirectory(tests)
set(CPACK_PACKAGE_NAME "${PACKAGE_NAME}")

View File

@@ -5,7 +5,7 @@
#### Latest release
**v0.15.3**
**v0.15.2**
Read the [change log](https://github.com/falcosecurity/falco/blob/dev/CHANGELOG.md)
Dev Branch: [![Build Status](https://travis-ci.com/falcosecurity/falco.svg?branch=dev)](https://travis-ci.com/falcosecurity/falco)<br />

View File

@@ -1,175 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
Catch
-----
This module defines a function to help use the Catch test framework.
The :command:`catch_discover_tests` discovers tests by asking the compiled test
executable to enumerate its tests. This does not require CMake to be re-run
when tests change. However, it may not work in a cross-compiling environment,
and setting test properties is less convenient.
This command is intended to replace use of :command:`add_test` to register
tests, and will create a separate CTest test for each Catch test case. Note
that this is in some cases less efficient, as common set-up and tear-down logic
cannot be shared by multiple test cases executing in the same instance.
However, it provides more fine-grained pass/fail information to CTest, which is
usually considered as more beneficial. By default, the CTest test name is the
same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
.. command:: catch_discover_tests
Automatically add tests with CTest by querying the compiled test executable
for available tests::
catch_discover_tests(target
[TEST_SPEC arg1...]
[EXTRA_ARGS arg1...]
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
[PROPERTIES name1 value1...]
[TEST_LIST var]
)
``catch_discover_tests`` sets up a post-build command on the test executable
that generates the list of tests by parsing the output from running the test
with the ``--list-test-names-only`` argument. This ensures that the full
list of tests is obtained. Since test discovery occurs at build time, it is
not necessary to re-run CMake when the list of tests changes.
However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set
in order to function in a cross-compiling environment.
Additionally, setting properties on tests is somewhat less convenient, since
the tests are not available at CMake time. Additional test properties may be
assigned to the set of tests as a whole using the ``PROPERTIES`` option. If
more fine-grained test control is needed, custom content may be provided
through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES`
directory property. The set of discovered tests is made accessible to such a
script via the ``<target>_TESTS`` variable.
The options are:
``target``
Specifies the Catch executable, which must be a known CMake executable
target. CMake will substitute the location of the built executable when
running the test.
``TEST_SPEC arg1...``
Specifies test cases, wildcarded test cases, tags and tag expressions to
pass to the Catch executable with the ``--list-test-names-only`` argument.
``EXTRA_ARGS arg1...``
Any extra arguments to pass on the command line to each test case.
``WORKING_DIRECTORY dir``
Specifies the directory in which to run the discovered test cases. If this
option is not provided, the current binary directory is used.
``TEST_PREFIX prefix``
Specifies a ``prefix`` to be prepended to the name of each discovered test
case. This can be useful when the same test executable is being used in
multiple calls to ``catch_discover_tests()`` but with different
``TEST_SPEC`` or ``EXTRA_ARGS``.
``TEST_SUFFIX suffix``
Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
``PROPERTIES name1 value1...``
Specifies additional properties to be set on all tests discovered by this
invocation of ``catch_discover_tests``.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
executable is being used in multiple calls to ``catch_discover_tests()``.
Note that this variable is only available in CTest.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES"
${ARGN}
)
if(NOT _WORKING_DIRECTORY)
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}")
string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake")
get_property(crosscompiling_emulator
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
else()
# Add discovered tests as directory TEST_INCLUDE_FILE if possible
get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET)
if (NOT ${test_include_file_set})
set_property(DIRECTORY
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
endif()
endif()
endfunction()
###############################################################################
set(_CATCH_DISCOVER_TESTS_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
)

View File

@@ -1,78 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(script)
set(suite)
set(tests)
function(add_command NAME)
set(_args "")
foreach(_arg ${ARGN})
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else()
set(_args "${_args} ${_arg}")
endif()
endforeach()
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
)
endif()
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
# Catch --list-test-names-only reports the number of tests, so 0 is... surprising
if(${result} EQUAL 0)
message(WARNING
"Test executable '${TEST_EXECUTABLE}' contains no tests!\n"
)
elseif(${result} LESS 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Parse output
foreach(line ${output})
set(test ${line})
# use escape commas to handle properly test cases with commans inside the name
string(REPLACE "," "\\," test_name ${test})
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"${test_name}"
${extra_args}
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
)
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@@ -1,39 +0,0 @@
#
# Copyright (C) 2016-2019 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.
#
include(ExternalProject)
set(CATCH2_INCLUDE ${CMAKE_BINARY_DIR}/catch2-prefix/include)
set(CATCH_EXTERNAL_URL
URL
https://github.com/catchorg/catch2/archive/v2.9.1.tar.gz
URL_HASH
MD5=4980778888fed635bf191d8a86f9f89c)
ExternalProject_Add(
catch2
PREFIX ${CMAKE_BINARY_DIR}/catch2-prefix
${CATCH_EXTERNAL_URL}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND}
-E
copy
${CMAKE_BINARY_DIR}/catch2-prefix/src/catch2/single_include/catch2/catch.hpp
${CATCH2_INCLUDE}/catch.hpp)

View File

@@ -1,39 +0,0 @@
#
# Copyright (C) 2016-2019 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.
#
include(ExternalProject)
set(FAKEIT_INCLUDE ${CMAKE_BINARY_DIR}/fakeit-prefix/include)
set(FAKEIT_EXTERNAL_URL
URL
https://github.com/eranpeer/fakeit/archive/2.0.5.tar.gz
URL_HASH
MD5=d3d21b909cebaea5b780af5500bf384e)
ExternalProject_Add(
fakeit-external
PREFIX ${CMAKE_BINARY_DIR}/fakeit-prefix
${FAKEIT_EXTERNAL_URL}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND
${CMAKE_COMMAND}
-E
copy
${CMAKE_BINARY_DIR}/fakeit-prefix/src/fakeit-external/single_header/catch/fakeit.hpp
${FAKEIT_INCLUDE}/fakeit.hpp)

View File

@@ -166,4 +166,5 @@ program_output:
http_output:
enabled: false
url: http://some.url
url: http://some.url

View File

@@ -29,7 +29,7 @@ service/falco-service created
k8s-using-daemonset$
```
The Daemon Set also relies on a Kubernetes ConfigMap to store the Falco configuration and make the configuration available to the Falco Pods. This allows you to manage custom configuration without rebuilding and redeploying the underlying Pods. In order to create the ConfigMap you'll first need to copy the required configuration from their location in this GitHub repo to the `k8s-with-rbac/falco-config/` directory (please note that you will need to create the /falco-config directory). Any modification of the configuration should be performed on these copies rather than the original files.
The Daemon Set also relies on a Kubernetes ConfigMap to store the Falco configuration and make the configuration available to the Falco Pods. This allows you to manage custom configuration without rebuilding and redeploying the underlying Pods. In order to create the ConfigMap you'll need to first need to copy the required configuration from their location in this GitHub repo to the `k8s-with-rbac/falco-config/` directory (please note that you will need to create the /falco-config directory). Any modification of the configuration should be performed on these copies rather than the original files.
```
k8s-using-daemonset$ mkdir -p k8s-with-rbac/falco-config
@@ -59,7 +59,8 @@ Now that we have the requirements for our Daemon Set in place, we can create our
```
k8s-using-daemonset$ kubectl create -f k8s-with-rbac/falco-daemonset-configmap.yaml
daemonset "falco" created
daemonset.extensions "falco-daemonset" created
k8s-using-daemonset$
```
@@ -97,14 +98,13 @@ In order to test that Falco is working correctly, you can launch a shell in a Po
```
k8s-using-daemonset$ kubectl get pods
NAME READY STATUS RESTARTS AGE
falco-74htl 1/1 Running 0 13h
falco-fqz2m 1/1 Running 0 13h
falco-sgjfx 1/1 Running 0 13h
k8s-using-daemonset$ kubectl exec -it falco-74htl bash
root@falco-74htl:/# exit
k8s-using-daemonset$ kubectl logs falco-74htl
{"output":"17:48:58.590038385: Notice A shell was spawned in a container with an attached terminal (user=root k8s.pod=falco-74htl container=a98c2aa8e670 shell=bash parent=<NA> cmdline=bash terminal=34816)","priority":"Notice","rule":"Terminal shell in container","time":"2017-12-20T17:48:58.590038385Z", "output_fields": {"container.id":"a98c2aa8e670","evt.time":1513792138590038385,"k8s.pod.name":"falco-74htl","proc.cmdline":"bash ","proc.name":"bash","proc.pname":null,"proc.tty":34816,"user.name":"root"}}
NAME READY STATUS RESTARTS AGE
falco-daemonset-b695d 1/1 Running 0 2d
falco-daemonset-n8q2v 1/1 Running 0 2d
k8s-using-daemonset$ kubectl exec -it falco-daemonset-b695d bash
root@falco-daemonset-b695d:/# exit
k8s-using-daemonset$ kubectl logs falco-daemonset-b695d
07:16:09.217866519: Error File below known binary directory renamed/removed (user=root command=event_generator pcmdline=<NA> operation=rename file=<NA> res=0 oldpath=/bin/true newpath=/bin/true.event-generator-sh ) k8s.ns=default k8s.pod=falco-event-generator-deployment-645444689b-j6mth container=0e67aad65846 k8s.ns=default k8s.pod=falco-event-generator-deployment-645444689b-j6mth container=0e67aad65846
k8s-using-daemonset$
```

View File

@@ -59,7 +59,7 @@
- macro: rename
condition: evt.type in (rename, renameat)
- macro: mkdir
condition: evt.type in (mkdir, mkdirat)
condition: evt.type = mkdir
- macro: remove
condition: evt.type in (rmdir, unlink, unlinkat)
@@ -159,7 +159,7 @@
items: [docker, dockerd, exe, docker-compose, docker-entrypoi, docker-runc-cur, docker-current, dockerd-current]
- list: k8s_binaries
items: [hyperkube, skydns, kube2sky, exechealthz, weave-net, loopback, bridge, openshift-sdn, openshift]
items: [hyperkube, skydns, kube2sky, exechealthz, weave-net, loopback, bridge, openshift-sdn]
- list: lxd_binaries
items: [lxd, lxcfs]
@@ -243,7 +243,7 @@
# A canonical set of processes that run other programs with different
# privileges or as a different user.
- list: userexec_binaries
items: [sudo, su, suexec, critical-stack, dzdo]
items: [sudo, su, suexec, critical-stack]
- list: known_setuid_binaries
items: [
@@ -311,17 +311,13 @@
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
(evt.rawres >= 0 or evt.res = EINPROGRESS))
# RFC1918 addresses were assigned for private network usage
- list: rfc_1918_addresses
items: ['"10.0.0.0/8"', '"172.16.0.0/12"', '"192.168.0.0/16"']
- macro: outbound
condition: >
(((evt.type = connect and evt.dir=<) or
(evt.type in (sendto,sendmsg) and evt.dir=< and
fd.l4proto != tcp and fd.connected=false and fd.name_changed=true)) and
(fd.typechar = 4 or fd.typechar = 6) and
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8" and not fd.snet in (rfc_1918_addresses)) and
(fd.ip != "0.0.0.0" and fd.net != "127.0.0.0/8") and
(evt.rawres >= 0 or evt.res = EINPROGRESS))
# Very similar to inbound/outbound, but combines the tests together
@@ -352,7 +348,7 @@
- rule: Disallowed SSH Connection
desc: Detect any new ssh connection to a host other than those in an allowed group of hosts
condition: (inbound_outbound) and ssh_port and not allowed_ssh_hosts
output: Disallowed SSH Connection (command=%proc.cmdline connection=%fd.name user=%user.name container_id=%container.id image=%container.image.repository)
output: Disallowed SSH Connection (command=%proc.cmdline connection=%fd.name user=%user.name)
priority: NOTICE
tags: [network, mitre_remote_service]
@@ -383,7 +379,7 @@
((fd.sip in (allowed_outbound_destination_ipaddrs)) or
(fd.snet in (allowed_outbound_destination_networks)) or
(fd.sip.name in (allowed_outbound_destination_domains)))
output: Disallowed outbound connection destination (command=%proc.cmdline connection=%fd.name user=%user.name container_id=%container.id image=%container.image.repository)
output: Disallowed outbound connection destination (command=%proc.cmdline connection=%fd.name user=%user.name)
priority: NOTICE
tags: [network]
@@ -406,7 +402,7 @@
((fd.cip in (allowed_inbound_source_ipaddrs)) or
(fd.cnet in (allowed_inbound_source_networks)) or
(fd.cip.name in (allowed_inbound_source_domains)))
output: Disallowed inbound connection source (command=%proc.cmdline connection=%fd.name user=%user.name container_id=%container.id image=%container.image.repository)
output: Disallowed inbound connection source (command=%proc.cmdline connection=%fd.name user=%user.name)
priority: NOTICE
tags: [network]
@@ -444,7 +440,7 @@
fd.directory in (shell_config_directories)) and
not proc.name in (shell_binaries)
output: >
a shell configuration file has been modified (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
a shell configuration file has been modified (user=%user.name command=%proc.cmdline file=%fd.name)
priority:
WARNING
tag: [file, mitre_persistence]
@@ -466,7 +462,7 @@
fd.directory in (shell_config_directories)) and
(not proc.name in (shell_binaries))
output: >
a shell configuration file was read by a non-shell program (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
a shell configuration file was read by a non-shell program (user=%user.name command=%proc.cmdline file=%fd.name)
priority:
WARNING
tag: [file, mitre_discovery]
@@ -910,7 +906,7 @@
condition: >
open_write and access_repositories and not package_mgmt_procs
output: >
Repository files get updated (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
Repository files get updated (user=%user.name command=%proc.cmdline file=%fd.name)
priority:
NOTICE
tags: [filesystem, mitre_persistence]
@@ -925,7 +921,7 @@
and not python_running_ms_oms
output: >
File below a known binary directory opened for writing (user=%user.name
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2])
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -983,7 +979,7 @@
and not user_known_write_monitored_dir_conditions
output: >
File below a monitored directory opened for writing (user=%user.name
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline gparent=%proc.aname[2])
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -1002,7 +998,7 @@
(not proc.name in (ssh_binaries)))
output: >
ssh-related file/directory read by non-ssh program (user=%user.name
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)
command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline)
priority: ERROR
tags: [filesystem, mitre_discovery]
@@ -1152,10 +1148,6 @@
- macro: user_known_write_etc_conditions
condition: proc.name=confd
# This is a placeholder for user to extend the whitelist for write below etc rule
- macro: user_known_write_below_etc_activities
condition: (never_true)
- macro: write_etc_common
condition: >
etc_dir and evt.dir = < and open_write
@@ -1253,12 +1245,11 @@
and not checkpoint_writing_state
and not jboss_in_container_writing_passwd
and not etcd_manager_updating_dns
and not user_known_write_below_etc_activities
- rule: Write below etc
desc: an attempt to write to any file below /etc
condition: write_etc_common
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)"
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline parent=%proc.pname pcmdline=%proc.pcmdline file=%fd.name program=%proc.name gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])"
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -1317,10 +1308,6 @@
- macro: user_known_write_root_conditions
condition: fd.name=/root/.bash_history
# This is a placeholder for user to extend the whitelist for write below root rule
- macro: user_known_write_below_root_activities
condition: (never_true)
- rule: Write below root
desc: an attempt to write to any file directly below / or /root
condition: >
@@ -1342,8 +1329,7 @@
and not rancher_writing_root
and not known_root_conditions
and not user_known_write_root_conditions
and not user_known_write_below_root_activities
output: "File below / or /root opened for writing (user=%user.name command=%proc.cmdline parent=%proc.pname file=%fd.name program=%proc.name container_id=%container.id image=%container.image.repository)"
output: "File below / or /root opened for writing (user=%user.name command=%proc.cmdline parent=%proc.pname file=%fd.name program=%proc.name)"
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -1358,7 +1344,7 @@
condition: sensitive_files and open_read and server_procs and not proc_is_new and proc.name!="sshd"
output: >
Sensitive file opened for reading by trusted program after startup (user=%user.name
command=%proc.cmdline parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2] container_id=%container.id image=%container.image.repository)
command=%proc.cmdline parent=%proc.pname file=%fd.name parent=%proc.pname gparent=%proc.aname[2])
priority: WARNING
tags: [filesystem, mitre_credential_access]
@@ -1408,7 +1394,7 @@
and not runuser_reading_pam
output: >
Sensitive file opened for reading by non-trusted program (user=%user.name program=%proc.name
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4] container_id=%container.id image=%container.image.repository)
command=%proc.cmdline file=%fd.name parent=%proc.pname gparent=%proc.aname[2] ggparent=%proc.aname[3] gggparent=%proc.aname[4])
priority: WARNING
tags: [filesystem, mitre_credential_access, mitre_discovery]
@@ -1421,7 +1407,7 @@
and not ansible_running_python
and not python_running_chef
and not exe_running_docker_save
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline container_id=%container.id image=%container.image.repository)"
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name parent=%proc.pname pcmdline=%proc.pcmdline)"
priority: ERROR
tags: [filesystem, software_mgmt, mitre_persistence]
@@ -1456,7 +1442,7 @@
and not postgres_running_wal_e
output: >
Database-related program spawned process other than itself (user=%user.name
program=%proc.cmdline parent=%proc.pname container_id=%container.id image=%container.image.repository)
program=%proc.cmdline parent=%proc.pname)
priority: NOTICE
tags: [process, database, mitre_execution]
@@ -1465,7 +1451,7 @@
condition: (bin_dir_rename) and modify and not package_mgmt_procs and not exe_running_docker_save
output: >
File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline
pcmdline=%proc.pcmdline operation=%evt.type file=%fd.name %evt.args container_id=%container.id image=%container.image.repository)
pcmdline=%proc.pcmdline operation=%evt.type file=%fd.name %evt.args)
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -1474,7 +1460,7 @@
condition: mkdir and bin_dir_mkdir and not package_mgmt_procs
output: >
Directory below known binary directory created (user=%user.name
command=%proc.cmdline directory=%evt.arg.path container_id=%container.id image=%container.image.repository)
command=%proc.cmdline directory=%evt.arg.path)
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -1484,18 +1470,6 @@
- list: user_known_change_thread_namespace_binaries
items: []
- macro: user_known_change_thread_namespace_activities
condition: (never_true)
- list: network_plugin_binaries
items: [aws-cni, azure-vnet]
- macro: calico_node
condition: (container.image.repository endswith calico/node and proc.name=calico-node)
- macro: weaveworks_scope
condition: (container.image.repository endswith weaveworks/scope and proc.name=scope)
- rule: Change thread namespace
desc: >
an attempt to change a program/thread\'s namespace (commonly done
@@ -1503,7 +1477,7 @@
condition: >
evt.type = setns
and not proc.name in (docker_binaries, k8s_binaries, lxd_binaries, sysdigcloud_binaries,
sysdig, nsenter, calico, oci-umount, network_plugin_binaries)
sysdig, nsenter, calico, oci-umount)
and not proc.name in (user_known_change_thread_namespace_binaries)
and not proc.name startswith "runc"
and not proc.cmdline startswith "containerd"
@@ -1513,12 +1487,9 @@
and not kubelet_running_loopback
and not rancher_agent
and not rancher_network_manager
and not calico_node
and not weaveworks_scope
and not user_known_change_thread_namespace_activities
output: >
Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline
parent=%proc.pname %container.info container_id=%container.id image=%container.image.repository)
parent=%proc.pname %container.info)
priority: NOTICE
tags: [process]
@@ -1664,38 +1635,35 @@
output: >
Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname
cmdline=%proc.cmdline pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] container_id=%container.id image=%container.image.repository)
aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7])
priority: DEBUG
tags: [shell, mitre_execution]
- macro: allowed_openshift_registry_root
condition: >
(container.image.repository startswith openshift3/ or
container.image.repository startswith registry.redhat.io/openshift3/ or
container.image.repository startswith registry.access.redhat.com/openshift3/)
# Source: https://docs.openshift.com/enterprise/3.2/install_config/install/disconnected_install.html
- macro: openshift_image
condition: >
(allowed_openshift_registry_root and
(container.image.repository endswith /logging-deployment or
container.image.repository endswith /logging-elasticsearch or
container.image.repository endswith /logging-kibana or
container.image.repository endswith /logging-fluentd or
container.image.repository endswith /logging-auth-proxy or
container.image.repository endswith /metrics-deployer or
container.image.repository endswith /metrics-hawkular-metrics or
container.image.repository endswith /metrics-cassandra or
container.image.repository endswith /metrics-heapster or
container.image.repository endswith /ose-haproxy-router or
container.image.repository endswith /ose-deployer or
container.image.repository endswith /ose-sti-builder or
container.image.repository endswith /ose-docker-builder or
container.image.repository endswith /ose-pod or
container.image.repository endswith /ose-node or
container.image.repository endswith /ose-docker-registry or
container.image.repository endswith /prometheus-node-exporter or
container.image.repository endswith /image-inspector))
(container.image.repository contains logging-deployment or
container.image.repository contains logging-elasticsearch or
container.image.repository contains logging-kibana or
container.image.repository contains logging-fluentd or
container.image.repository contains logging-auth-proxy or
container.image.repository contains metrics-deployer or
container.image.repository contains metrics-hawkular-metrics or
container.image.repository contains metrics-cassandra or
container.image.repository contains metrics-heapster or
container.image.repository contains ose-haproxy-router or
container.image.repository contains ose-deployer or
container.image.repository contains ose-sti-builder or
container.image.repository contains ose-docker-builder or
container.image.repository contains ose-pod or
container.image.repository contains ose-docker-registry or
container.image.repository contains image-inspector))
# These images are allowed both to run with --privileged and to mount
# sensitive paths from the host filesystem.
@@ -1707,13 +1675,6 @@
- list: trusted_images
items: []
# NOTE: This macro is only provided for backwards compatibility with
# older local falco rules files that may have been appending to
# trusted_images. To make customizations, it's better to add containers to
# user_trusted_containers, user_privileged_containers or user_sensitive_mount_containers.
- macro: trusted_containers
condition: (container.image.repository in (trusted_images))
# Add conditions to this macro (probably in a separate file,
# overwriting this macro) to specify additional containers that are
# trusted and therefore allowed to run privileged *and* with sensitive
@@ -1726,10 +1687,10 @@
# In this file, it just takes one of the images in trusted_containers
# and repeats it.
- macro: user_trusted_containers
condition: (container.image.repository endswith sysdig/agent)
condition: (container.image.repository=sysdig/agent)
- list: sematext_images
items: [docker.io/sematext/sematext-agent-docker, docker.io/sematext/agent, docker.io/sematext/logagent,
items: [sematext/sematext-agent-docker, sematext/agent, sematext/logagent,
registry.access.redhat.com/sematext/sematext-agent-docker,
registry.access.redhat.com/sematext/agent,
registry.access.redhat.com/sematext/logagent]
@@ -1737,10 +1698,10 @@
# These container images are allowed to run with --privileged
- list: falco_privileged_images
items: [
docker.io/sysdig/agent, docker.io/sysdig/falco, docker.io/sysdig/sysdig,
gcr.io/google_containers/kube-proxy, docker.io/calico/node,
docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/mesosphere/mesos-slave,
docker.io/docker/ucp-agent, sematext_images
sysdig/agent, sysdig/falco, sysdig/sysdig,
gcr.io/google_containers/kube-proxy, calico/node,
rook/toolbox, cloudnativelabs/kube-router, mesosphere/mesos-slave,
docker/ucp-agent, sematext_images
]
- macro: falco_privileged_containers
@@ -1758,7 +1719,8 @@
# In this file, it just takes one of the images in falco_privileged_images
# and repeats it.
- macro: user_privileged_containers
condition: (container.image.repository endswith sysdig/agent)
condition: (container.image.repository=sysdig/agent)
- list: rancher_images
items: [
@@ -1770,11 +1732,11 @@
# host filesystem.
- list: falco_sensitive_mount_images
items: [
docker.io/sysdig/agent, docker.io/sysdig/falco, docker.io/sysdig/sysdig,
sysdig/agent, sysdig/falco, sysdig/sysdig,
gcr.io/google_containers/hyperkube,
gcr.io/google_containers/kube-proxy, docker.io/calico/node,
docker.io/rook/toolbox, docker.io/cloudnativelabs/kube-router, docker.io/consul,
docker.io/datadog/docker-dd-agent, docker.io/datadog/agent, docker.io/docker/ucp-agent, docker.io/gliderlabs/logspout
gcr.io/google_containers/kube-proxy, calico/node,
rook/toolbox, cloudnativelabs/kube-router, consul,
datadog/docker-dd-agent, datadog/agent, docker/ucp-agent, gliderlabs/logspout
]
- macro: falco_sensitive_mount_containers
@@ -1794,7 +1756,7 @@
# In this file, it just takes one of the images in falco_sensitive_mount_images
# and repeats it.
- macro: user_sensitive_mount_containers
condition: (container.image.repository = docker.io/sysdig/agent)
condition: (container.image.repository=sysdig/agent)
- rule: Launch Privileged Container
desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images.
@@ -1875,7 +1837,7 @@
- rule: System user interactive
desc: an attempt to run interactive commands by a system (i.e. non-login) user
condition: spawned_process and system_users and interactive
output: "System user ran an interactive command (user=%user.name command=%proc.cmdline container_id=%container.id image=%container.image.repository)"
output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)"
priority: INFO
tags: [users, mitre_remote_access_tools]
@@ -1887,7 +1849,7 @@
and container_entrypoint
output: >
A shell was spawned in a container with an attached terminal (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository)
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)
priority: NOTICE
tags: [container, shell, mitre_execution]
@@ -1961,7 +1923,7 @@
and not login_doing_dns_lookup
output: >
Known system binary sent/received network traffic
(user=%user.name command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
(user=%user.name command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network, mitre_exfiltration]
@@ -1987,7 +1949,7 @@
proc.env icontains HTTP_PROXY
output: >
Program run with disallowed HTTP_PROXY environment variable
(user=%user.name command=%proc.cmdline env=%proc.env parent=%proc.pname container_id=%container.id image=%container.image.repository)
(user=%user.name command=%proc.cmdline env=%proc.env parent=%proc.pname)
priority: NOTICE
tags: [host, users]
@@ -2010,7 +1972,7 @@
and interpreted_procs)
output: >
Interpreted program received/listened for network traffic
(user=%user.name command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
(user=%user.name command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network, mitre_exfiltration]
@@ -2021,7 +1983,7 @@
and interpreted_procs)
output: >
Interpreted program performed outgoing network connection
(user=%user.name command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
(user=%user.name command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network, mitre_exfiltration]
@@ -2062,7 +2024,7 @@
condition: (inbound_outbound) and do_unexpected_udp_check and fd.l4proto=udp and not expected_udp_traffic
output: >
Unexpected UDP Traffic Seen
(user=%user.name command=%proc.cmdline connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args container_id=%container.id image=%container.image.repository)
(user=%user.name command=%proc.cmdline connection=%fd.name proto=%fd.l4proto evt=%evt.type %evt.args)
priority: NOTICE
tags: [network, mitre_exfiltration]
@@ -2122,7 +2084,7 @@
and not user_known_non_sudo_setuid_conditions
output: >
Unexpected setuid call by non-sudo, non-root program (user=%user.name cur_uid=%user.uid parent=%proc.pname
command=%proc.cmdline uid=%evt.arg.uid container_id=%container.id image=%container.image.repository)
command=%proc.cmdline uid=%evt.arg.uid)
priority: NOTICE
tags: [users, mitre_privilege_escalation]
@@ -2166,7 +2128,7 @@
and not proc.name in (dev_creation_binaries)
and not fd.name in (allowed_dev_files)
and not fd.name startswith /dev/tty
output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)"
output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: ERROR
tags: [filesystem, mitre_persistence]
@@ -2227,7 +2189,7 @@
- rule: Unexpected K8s NodePort Connection
desc: Detect attempts to use K8s NodePorts from a container
condition: (inbound_outbound) and fd.sport >= 30000 and fd.sport <= 32767 and container and not nodeport_containers
output: Unexpected K8s NodePort Connection (command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository)
output: Unexpected K8s NodePort Connection (command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network, k8s, container, mitre_port_knocking]
@@ -2337,19 +2299,12 @@
- macro: access_log_files
condition: (fd.directory in (log_directories) or fd.filename in (log_files))
# a placeholder for whitelist log files that could be cleared. Recommend the macro as (fd.name startswith "/var/log/app1*")
- macro: allowed_clear_log_files
condition: (never_true)
- rule: Clear Log Activities
desc: Detect clearing of critical log files
condition: >
open_write and
access_log_files and
evt.arg.flags contains "O_TRUNC" and
not allowed_clear_log_files
open_write and access_log_files and evt.arg.flags contains "O_TRUNC"
output: >
Log files were tampered (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
Log files were tampered (user=%user.name command=%proc.cmdline file=%fd.name)
priority:
WARNING
tags: [file, mitre_defense_evasion]
@@ -2364,7 +2319,7 @@
desc: Detect process running to clear bulk data from disk
condition: spawned_process and clear_data_procs
output: >
Bulk data has been removed from disk (user=%user.name command=%proc.cmdline file=%fd.name container_id=%container.id image=%container.image.repository)
Bulk data has been removed from disk (user=%user.name command=%proc.cmdline file=%fd.name)
priority:
WARNING
tags: [process, mitre_persistence]
@@ -2432,6 +2387,7 @@
priority: NOTICE
tags: [network, process, mitre_lateral_movement, mitre_exfiltration]
- rule: Create Symlink Over Sensitive Files
desc: Detect symlink created over sensitive files
condition: >

View File

@@ -18,10 +18,6 @@
configure_file(debian/postinst.in debian/postinst)
configure_file(debian/prerm.in debian/prerm)
if(NOT SYSDIG_DIR)
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
endif()
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco"
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")

View File

@@ -41,9 +41,6 @@ class FalcoTest(Test):
build_dir = os.path.join('/build', build_type)
self.falcodir = self.params.get('falcodir', '/', default=os.path.join(self.basedir, build_dir))
self.stdout_is = self.params.get('stdout_is', '*', default='')
self.stderr_is = self.params.get('stderr_is', '*', default='')
self.stdout_contains = self.params.get('stdout_contains', '*', default='')
if not isinstance(self.stdout_contains, list):
@@ -86,21 +83,8 @@ class FalcoTest(Test):
if not isinstance(self.rules_file, list):
self.rules_file = [self.rules_file]
self.validate_rules_file = self.params.get('validate_rules_file', '*', default=False)
if self.validate_rules_file == False:
self.validate_rules_file = []
else:
if not isinstance(self.validate_rules_file, list):
self.validate_rules_file = [self.validate_rules_file]
self.rules_args = ""
for file in self.validate_rules_file:
if not os.path.isabs(file):
file = os.path.join(self.basedir, file)
self.rules_args = self.rules_args + "-V " + file + " "
for file in self.rules_file:
if not os.path.isabs(file):
file = os.path.join(self.basedir, file)
@@ -449,15 +433,6 @@ class FalcoTest(Test):
res = self.falco_proc.run(timeout=180, sig=9)
if self.stdout_is != '':
print(self.stdout_is)
if self.stdout_is != res.stdout:
self.fail("Stdout was not exactly {}".format(self.stdout_is))
if self.stderr_is != '':
if self.stderr_is != res.stdout:
self.fail("Stdout was not exactly {}".format(self.stderr_is))
for pattern in self.stderr_contains:
match = re.search(pattern, res.stderr)
if match is None:

View File

@@ -238,199 +238,6 @@ trace_files: !mux
- rules/endswith.yaml
trace_file: trace_files/cat_write.scap
invalid_not_yaml:
exit_status: 1
stdout_is: |+
Rules content is not yaml
---
This is not yaml
---
validate_rules_file:
- rules/invalid_not_yaml.yaml
trace_file: trace_files/cat_write.scap
invalid_not_array:
exit_status: 1
stdout_is: |+
Rules content is not yaml array of objects
---
foo: bar
---
validate_rules_file:
- rules/invalid_not_array.yaml
trace_file: trace_files/cat_write.scap
invalid_array_item_not_object:
exit_status: 1
stdout_is: |+
Unexpected element of type string. Each element should be a yaml associative array.
---
- foo
---
validate_rules_file:
- rules/invalid_array_item_not_object.yaml
trace_file: trace_files/cat_write.scap
invalid_unexpected object:
exit_status: 1
stdout_is: |+
Unknown rule object: {foo="bar"}
---
- foo: bar
---
validate_rules_file:
- rules/invalid_unexpected_object.yaml
trace_file: trace_files/cat_write.scap
invalid_engine_version_not_number:
exit_status: 1
stdout_is: |+
Value of required_engine_version must be a number
---
- required_engine_version: not-a-number
---
validate_rules_file:
- rules/invalid_engine_version_not_number.yaml
trace_file: trace_files/cat_write.scap
invalid_yaml_parse_error:
exit_status: 1
stdout_is: |+
mapping values are not allowed in this context
---
this : is : not : yaml
---
validate_rules_file:
- rules/invalid_yaml_parse_error.yaml
trace_file: trace_files/cat_write.scap
invalid_list_without_items:
exit_status: 1
stdout_is: |+
List must have property items
---
- list: bad_list
no_items: foo
---
validate_rules_file:
- rules/invalid_list_without_items.yaml
trace_file: trace_files/cat_write.scap
invalid_macro_without_condition:
exit_status: 1
stdout_is: |+
Macro must have property condition
---
- macro: bad_macro
nope: 1
---
validate_rules_file:
- rules/invalid_macro_without_condition.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_without_output:
exit_status: 1
stdout_is: |+
Rule must have property output
---
- rule: no output rule
desc: some desc
condition: evt.type=fork
priority: INFO
---
validate_rules_file:
- rules/invalid_rule_without_output.yaml
trace_file: trace_files/cat_write.scap
invalid_append_rule_without_condition:
exit_status: 1
stdout_is: |+
Rule must have property condition
---
- rule: no condition rule
append: true
---
validate_rules_file:
- rules/invalid_append_rule_without_condition.yaml
trace_file: trace_files/cat_write.scap
invalid_append_macro_dangling:
exit_status: 1
stdout_is: |+
Macro dangling append has 'append' key but no macro by that name already exists
---
- macro: dangling append
condition: and evt.type=execve
append: true
---
validate_rules_file:
- rules/invalid_append_macro_dangling.yaml
trace_file: trace_files/cat_write.scap
invalid_list_append_dangling:
exit_status: 1
stdout_is: |+
List my_list has 'append' key but no list by that name already exists
---
- list: my_list
items: [not-cat]
append: true
---
validate_rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_append_dangling:
exit_status: 1
stdout_is: |+
Rule my_rule has 'append' key but no rule by that name already exists
---
- rule: my_rule
condition: evt.type=open
append: true
---
validate_rules_file:
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_rule_name:
exit_status: 1
stdout_is: |+
Rule name is empty
---
- rule:
desc: some desc
condition: evt.type=execve
output: some output
---
validate_rules_file:
- rules/invalid_missing_rule_name.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_list_name:
exit_status: 1
stdout_is: |+
List name is empty
---
- list:
items: [foo]
---
validate_rules_file:
- rules/invalid_missing_list_name.yaml
trace_file: trace_files/cat_write.scap
invalid_missing_macro_name:
exit_status: 1
stdout_is: |+
Macro name is empty
---
- macro:
condition: evt.type=execve
---
validate_rules_file:
- rules/invalid_missing_macro_name.yaml
trace_file: trace_files/cat_write.scap
invalid_rule_output:
exit_status: 1
stderr_contains: "Runtime error: Error loading rules:.* Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'. Exiting."
@@ -794,7 +601,7 @@ trace_files: !mux
list_append_failure:
exit_status: 1
stderr_contains: "List my_list has 'append' key but no list by that name already exists"
stderr_contains: "List my_list has 'append' key but no list by that name already exists. Exiting"
rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
@@ -814,7 +621,7 @@ trace_files: !mux
macro_append_failure:
exit_status: 1
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists"
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists. Exiting"
rules_file:
- rules/macro_append_failure.yaml
trace_file: trace_files/cat_write.scap
@@ -834,7 +641,7 @@ trace_files: !mux
rule_append_failure:
exit_status: 1
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists"
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists. Exiting"
rules_file:
- rules/rule_append_failure.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -1,3 +0,0 @@
- macro: dangling append
condition: and evt.type=execve
append: true

View File

@@ -1,2 +0,0 @@
- rule: no condition rule
append: true

View File

@@ -1 +0,0 @@
- foo

View File

@@ -1,34 +0,0 @@
#
# 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: not-a-number
- 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

View File

@@ -1,5 +0,0 @@
- list: good_list
items: [foo]
- list: bad_list
no_items: foo

View File

@@ -1,6 +0,0 @@
- macro: bad_macro
nope: 1
- macro: good_macro
condition: evt.type=execve

View File

@@ -1,2 +0,0 @@
- list:
items: [foo]

View File

@@ -1,2 +0,0 @@
- macro:
condition: evt.type=execve

View File

@@ -1,4 +0,0 @@
- rule:
desc: some desc
condition: evt.type=execve
output: some output

View File

@@ -1 +0,0 @@
foo: bar

View File

@@ -1 +0,0 @@
This is not yaml

View File

@@ -1,4 +0,0 @@
- rule: no output rule
desc: some desc
condition: evt.type=fork
priority: INFO

View File

@@ -1 +0,0 @@
- foo: bar

View File

@@ -1 +0,0 @@
this : is : not : yaml

View File

@@ -1,49 +0,0 @@
#
# Copyright (C) 2016-2019 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.
#
set(FALCO_TESTS_SOURCES test_base.cpp engine/test_token_bucket.cpp)
set(FALCO_TESTED_LIBRARIES falco_engine)
option(FALCO_BUILD_TESTS "Determines whether to build tests." ON)
if(FALCO_BUILD_TESTS)
enable_testing()
if(NOT TARGET catch)
include(DownloadCatch)
endif()
if(NOT TARGET fakeit)
include(DownloadFakeIt)
endif()
add_executable(falco_test ${FALCO_TESTS_SOURCES})
target_link_libraries(falco_test PUBLIC ${FALCO_TESTED_LIBRARIES})
target_include_directories(
falco_test
PUBLIC "${CATCH2_INCLUDE}"
"${FAKEIT_INCLUDE}"
"${PROJECT_SOURCE_DIR}/userspace/engine")
include(CMakeParseArguments)
include(CTest)
include(Catch)
catch_discover_tests(falco_test)
add_custom_target(tests COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS falco_test)
endif()

View File

@@ -1,83 +0,0 @@
/*
Copyright (C) 2016-2019 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.
*/
#include "token_bucket.h"
#include <catch.hpp>
using namespace Catch::literals;
TEST_CASE("token bucket default ctor", "[token_bucket]")
{
auto tb = new token_bucket();
REQUIRE(tb->get_tokens() == 1);
SECTION("initialising with specific time, rate 2 tokens/sec")
{
auto max = 2.0;
uint64_t now = 1;
tb->init(1.0, max, now);
REQUIRE(tb->get_last_seen() == now);
REQUIRE(tb->get_tokens() == max);
}
}
TEST_CASE("token bucket ctor with custom timer", "[token_bucket]")
{
auto t = []() -> uint64_t { return 22; };
auto tb = new token_bucket(t);
REQUIRE(tb->get_tokens() == 1);
REQUIRE(tb->get_last_seen() == 22);
}
TEST_CASE("token bucket with 2 tokens/sec rate, max 10 tokens", "[token_bucket]")
{
auto tb = new token_bucket();
tb->init(2.0, 10, 1);
SECTION("claiming 5 tokens")
{
bool claimed = tb->claim(5, 1000000001);
REQUIRE(tb->get_last_seen() == 1000000001);
REQUIRE(tb->get_tokens() == 5.0_a);
REQUIRE(claimed);
SECTION("claiming all the 7 remaining tokens")
{
bool claimed = tb->claim(7, 2000000001);
REQUIRE(tb->get_last_seen() == 2000000001);
REQUIRE(tb->get_tokens() == 0.0_a);
REQUIRE(claimed);
SECTION("claiming 1 token more than the 2 available fails")
{
bool claimed = tb->claim(3, 3000000001);
REQUIRE(tb->get_last_seen() == 3000000001);
REQUIRE(tb->get_tokens() == 2.0_a);
REQUIRE_FALSE(claimed);
}
}
}
}
TEST_CASE("token bucket default initialization", "[token_bucket]")
{
token_bucket tb;
REQUIRE(tb.get_tokens() == 1);
}

View File

@@ -1,24 +0,0 @@
/*
Copyright (C) 2016-2019 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.
*/
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_CONSOLE_WIDTH 300
#include <catch.hpp>
TEST_CASE("all test cases reside in other .cpp files (empty)", "[multi-file:1]")
{
}

View File

@@ -14,12 +14,17 @@
# 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_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp/third-party/jsoncpp")
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libscap")
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
include_directories("${PROJECT_BINARY_DIR}/userspace/engine")
include_directories("${LUAJIT_INCLUDE}")
include_directories("${NJSON_INCLUDE}")
include_directories("${CURL_INCLUDE_DIR}")
include_directories("${TBB_INCLUDE_DIR}")
if(NOT SYSDIG_DIR)
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
endif()
set(FALCO_ENGINE_SOURCE_FILES
add_library(falco_engine STATIC
rules.cpp
falco_common.cpp
falco_engine.cpp
@@ -28,18 +33,10 @@ set(FALCO_ENGINE_SOURCE_FILES
token_bucket.cpp
formats.cpp)
add_library(falco_engine STATIC ${FALCO_ENGINE_SOURCE_FILES})
target_include_directories(falco_engine PUBLIC
"${LUAJIT_INCLUDE}"
"${NJSON_INCLUDE}"
"${CURL_INCLUDE_DIR}"
"${TBB_INCLUDE_DIR}"
"${SYSDIG_DIR}/userspace/libsinsp/third-party/jsoncpp"
"${SYSDIG_DIR}/userspace/libscap"
"${SYSDIG_DIR}/userspace/libsinsp"
"${PROJECT_BINARY_DIR}/userspace/engine"
)
"${PROJECT_BINARY_DIR}/userspace/engine")
target_link_libraries(falco_engine
"${FALCO_SINSP_LIBRARY}"

View File

@@ -93,21 +93,21 @@ void falco_engine::list_fields(bool names_only)
if(!names_only)
{
printf("\n----------------------\n");
printf("Field Class: %s (%s)\n\n", chk_field.m_name.c_str(), chk_field.m_desc.c_str());
printf("Field Class: %s (%s)\n\n", chk_field.name.c_str(), chk_field.desc.c_str());
}
for(auto &field : chk_field.m_fields)
for(auto &field : chk_field.fields)
{
uint32_t l, m;
printf("%s", field.m_name.c_str());
printf("%s", field.name.c_str());
if(names_only)
{
printf("\n");
continue;
}
uint32_t namelen = field.m_name.size();
uint32_t namelen = field.name.size();
if(namelen >= DESCRIPTION_TEXT_START)
{
@@ -120,7 +120,7 @@ void falco_engine::list_fields(bool names_only)
printf(" ");
}
size_t desclen = field.m_desc.size();
size_t desclen = field.desc.size();
for(l = 0; l < desclen; l++)
{
@@ -134,7 +134,7 @@ void falco_engine::list_fields(bool names_only)
}
}
printf("%c", field.m_desc.at(l));
printf("%c", field.desc.at(l));
}
printf("\n");

View File

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

View File

@@ -70,51 +70,30 @@ std::string json_event_filter_check::json_as_string(const json &j)
}
}
json_event_filter_check::field_info::field_info()
json_event_filter_check::alias::alias()
: m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc)
: m_name(name), m_desc(desc),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode,
index_type itype)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(itype)
{
}
json_event_filter_check::field_info::~field_info()
{
}
json_event_filter_check::alias::alias()
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr)
: m_jptr(ptr), m_format(def_format)
: m_jptr(ptr), m_format(def_format),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
format_t format)
: m_jptr(ptr), m_format(format)
: m_jptr(ptr), m_format(format),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
format_t format,
index_mode mode,
index_type itype)
: m_jptr(ptr), m_format(format),
m_idx_mode(mode), m_idx_type(itype)
{
}
@@ -139,25 +118,18 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
size_t idx_len = 0;
for(auto &info : m_info.m_fields)
for(auto &pair : m_aliases)
{
if(m_aliases.find(info.m_name) == m_aliases.end())
{
throw falco_exception("Could not find alias for field name " + info.m_name);
}
auto &al = m_aliases[info.m_name];
// What follows the match must not be alphanumeric or a dot
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
!isalnum((int) str[info.m_name.size()]) &&
str[info.m_name.size()] != '.' &&
info.m_name.size() > match_len)
if(strncmp(pair.first.c_str(), str, pair.first.size()) == 0 &&
!isalnum((int) str[pair.first.size()]) &&
str[pair.first.size()] != '.' &&
pair.first.size() > match_len)
{
m_jptr = al.m_jptr;
m_field = info.m_name;
m_format = al.m_format;
match_len = info.m_name.size();
m_jptr = pair.second.m_jptr;
m_field = pair.first;
m_format = pair.second.m_format;
match_len = pair.first.size();
const char *start = str + m_field.size();
@@ -175,18 +147,18 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
idx_len = (end - start + 2);
}
if(m_idx.empty() && info.m_idx_mode == IDX_REQUIRED)
if(m_idx.empty() && pair.second.m_idx_mode == alias::IDX_REQUIRED)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires an index but none provided"));
}
if(!m_idx.empty() && info.m_idx_mode == IDX_NONE)
if(!m_idx.empty() && pair.second.m_idx_mode == alias::IDX_NONE)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" forbids an index but one provided"));
}
if(!m_idx.empty() &&
info.m_idx_type == IDX_NUMERIC &&
pair.second.m_idx_type == alias::IDX_NUMERIC &&
m_idx.find_first_not_of("0123456789") != string::npos)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires a numeric index"));
@@ -319,7 +291,7 @@ jevt_filter_check::jevt_filter_check()
{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
{s_jevt_time_iso_8601_field, "json event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)"},
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string"},
{s_jevt_obj_field, "The entire json object, stringified"}
}};
}
@@ -631,34 +603,36 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val)."},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists", IDX_REQUIRED, IDX_KEY},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists"},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image"},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)"},
{"ka.req.container.host_network", "When the request object refers to a container, the value of the hostNetwork flag."},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged."},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated"},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated"},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated"},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated"},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)", IDX_REQUIRED, IDX_KEY},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports"},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)"},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"}
}};
{
using a = alias;
m_aliases = {
{"ka.auditid", {"/auditID"_json_pointer}},
{"ka.stage", {"/stage"_json_pointer}},
@@ -669,28 +643,28 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.impuser.name", {"/impersonatedUser/username"_json_pointer}},
{"ka.verb", {"/verb"_json_pointer}},
{"ka.uri", {"/requestURI"_json_pointer}},
{"ka.uri.param", {"/requestURI"_json_pointer, index_query_param}},
{"ka.uri.param", {"/requestURI"_json_pointer, index_query_param, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.target.name", {"/objectRef/name"_json_pointer}},
{"ka.target.namespace", {"/objectRef/namespace"_json_pointer}},
{"ka.target.resource", {"/objectRef/resource"_json_pointer}},
{"ka.target.subresource", {"/objectRef/subresource"_json_pointer}},
{"ka.req.binding.subjects", {"/requestObject/subjects"_json_pointer}},
{"ka.req.binding.subject.has_name", {"/requestObject/subjects"_json_pointer, index_has_name}},
{"ka.req.binding.subject.has_name", {"/requestObject/subjects"_json_pointer, index_has_name, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.req.binding.role", {"/requestObject/roleRef/name"_json_pointer}},
{"ka.req.configmap.name", {"/objectRef/name"_json_pointer}},
{"ka.req.configmap.obj", {"/requestObject/data"_json_pointer}},
{"ka.req.container.image", {"/requestObject/spec/containers"_json_pointer, index_image}},
{"ka.req.container.image.repository", {"/requestObject/spec/containers"_json_pointer, index_image}},
{"ka.req.container.image", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.container.image.repository", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.container.host_network", {"/requestObject/spec/hostNetwork"_json_pointer}},
{"ka.req.container.privileged", {"/requestObject/spec/containers"_json_pointer, index_privileged}},
{"ka.req.container.privileged", {"/requestObject/spec/containers"_json_pointer, index_privileged, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules", {"/requestObject/rules"_json_pointer}},
{"ka.req.role.rules.apiGroups", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.nonResourceURLs", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.resources", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.verbs", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.apiGroups", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.nonResourceURLs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.resources", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.verbs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.service.type", {"/requestObject/spec/type"_json_pointer}},
{"ka.req.service.ports", {"/requestObject/spec/ports"_json_pointer, index_generic}},
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols}},
{"ka.req.service.ports", {"/requestObject/spec/ports"_json_pointer, index_generic, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.resp.name", {"/responseObject/metadata/name"_json_pointer}},
{"ka.response.code", {"/responseStatus/code"_json_pointer}},
{"ka.response.reason", {"/responseStatus/reason"_json_pointer}}

View File

@@ -62,40 +62,19 @@ protected:
class json_event_filter_check : public gen_event_filter_check
{
public:
enum index_mode {
IDX_REQUIRED,
IDX_ALLOWED,
IDX_NONE
};
enum index_type {
IDX_KEY,
IDX_NUMERIC
};
// A struct describing a single filtercheck field ("ka.user")
struct field_info {
std::string m_name;
std::string m_desc;
index_mode m_idx_mode;
index_type m_idx_type;
// The variants allow for brace-initialization either
// with just the name/desc or additionally with index
// information
field_info();
field_info(std::string name, std::string desc);
field_info(std::string name, std::string desc, index_mode mode);
field_info(std::string name, std::string desc, index_mode mode, index_type itype);
virtual ~field_info();
std::string name;
std::string desc;
};
// A struct describing a group of filtercheck fields ("ka")
struct check_info {
std::string m_name;
std::string m_desc;
std::string name;
std::string desc;
std::list<field_info> m_fields;
std::list<field_info> fields;
};
json_event_filter_check();
@@ -136,12 +115,28 @@ protected:
typedef std::function<std::string (const nlohmann::json &, std::string &field, std::string &idx)> format_t;
struct alias {
// Whether this alias requires an index, allows an
// index, or should not have an index.
enum index_mode {
IDX_REQUIRED,
IDX_ALLOWED,
IDX_NONE
};
enum index_type {
IDX_KEY,
IDX_NUMERIC
};
// The variants allow for brace-initialization either
// with just the pointer or with both the pointer and
// a format function.
alias();
alias(nlohmann::json::json_pointer ptr);
alias(nlohmann::json::json_pointer ptr, format_t format);
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode);
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode, index_type itype);
virtual ~alias();
// A json pointer used to extract a referenced value
@@ -154,6 +149,10 @@ protected:
// indexing, searches, etc.) or string reformatting to
// trim unnecessary parts of the value.
format_t m_format;
index_mode m_idx_mode;
index_type m_idx_type;
};
// This map defines the aliases defined by this filter check

View File

@@ -62,12 +62,12 @@ function expand_macros(ast, defs, changed)
elseif ast.type == "Filter" then
if (ast.value.type == "Macro") then
if (defs[ast.value.value] == nil) then
return false, "Undefined macro '".. ast.value.value .. "' used in filter."
error("Undefined macro '".. ast.value.value .. "' used in filter.")
end
defs[ast.value.value].used = true
ast.value = copy_ast_obj(defs[ast.value.value].ast)
changed = true
return true, changed
return changed
end
return expand_macros(ast.value, defs, changed)
@@ -75,7 +75,7 @@ function expand_macros(ast, defs, changed)
if (ast.left.type == "Macro") then
if (defs[ast.left.value] == nil) then
return false, "Undefined macro '".. ast.left.value .. "' used in filter."
error("Undefined macro '".. ast.left.value .. "' used in filter.")
end
defs[ast.left.value].used = true
ast.left = copy_ast_obj(defs[ast.left.value].ast)
@@ -84,27 +84,21 @@ function expand_macros(ast, defs, changed)
if (ast.right.type == "Macro") then
if (defs[ast.right.value] == nil) then
return false, "Undefined macro ".. ast.right.value .. " used in filter."
error("Undefined macro ".. ast.right.value .. " used in filter.")
end
defs[ast.right.value].used = true
ast.right = copy_ast_obj(defs[ast.right.value].ast)
changed = true
end
local status, changed_left = expand_macros(ast.left, defs, false)
if status == false then
return false, changed_left
end
local status, changed_right = expand_macros(ast.right, defs, false)
if status == false then
return false, changed_right
end
return true, changed or changed_left or changed_right
local changed_left = expand_macros(ast.left, defs, false)
local changed_right = expand_macros(ast.right, defs, false)
return changed or changed_left or changed_right
elseif ast.type == "UnaryBoolOp" then
if (ast.argument.type == "Macro") then
if (defs[ast.argument.value] == nil) then
return false, "Undefined macro ".. ast.argument.value .. " used in filter."
error("Undefined macro ".. ast.argument.value .. " used in filter.")
end
defs[ast.argument.value].used = true
ast.argument = copy_ast_obj(defs[ast.argument.value].ast)
@@ -112,7 +106,7 @@ function expand_macros(ast, defs, changed)
end
return expand_macros(ast.argument, defs, changed)
end
return true, changed
return changed
end
function get_macros(ast, set)
@@ -158,35 +152,16 @@ end
function compiler.expand_lists_in(source, list_defs)
for name, def in pairs(list_defs) do
local begin_name_pat = "^("..name..")([%s(),=])"
local mid_name_pat = "([%s(),=])("..name..")([%s(),=])"
local end_name_pat = "([%s(),=])("..name..")$"
local bpos = string.find(source, name, 1, true)
source, subcount1 = string.gsub(source, begin_name_pat, table.concat(def.items, ", ").."%2")
source, subcount2 = string.gsub(source, mid_name_pat, "%1"..table.concat(def.items, ", ").."%3")
source, subcount3 = string.gsub(source, end_name_pat, "%1"..table.concat(def.items, ", "))
while bpos ~= nil do
if (subcount1 + subcount2 + subcount3) > 0 then
def.used = true
local epos = bpos + string.len(name)
-- The characters surrounding the name must be delimiters of beginning/end of string
if (bpos == 1 or string.match(string.sub(source, bpos-1, bpos-1), "[%s(),=]")) and (epos > string.len(source) or string.match(string.sub(source, epos, epos), "[%s(),=]")) then
new_source = ""
if bpos > 1 then
new_source = new_source..string.sub(source, 1, bpos-1)
end
sub = table.concat(def.items, ", ")
new_source = new_source..sub
if epos <= string.len(source) then
new_source = new_source..string.sub(source, epos, string.len(source))
end
source = new_source
bpos = bpos + (string.len(sub)-string.len(name))
end
bpos = string.find(source, name, bpos+1, true)
end
end
@@ -201,7 +176,7 @@ function compiler.compile_macro(line, macro_defs, list_defs)
if (error_msg) then
msg = "Compilation error when compiling \""..line.."\": ".. error_msg
return false, msg
error(msg)
end
-- Simply as a validation step, try to expand all macros in this
@@ -212,18 +187,14 @@ function compiler.compile_macro(line, macro_defs, list_defs)
if (ast.type == "Rule") then
-- Line is a filter, so expand macro references
repeat
status, expanded = expand_macros(ast_copy, macro_defs, false)
if status == false then
msg = "Compilation error when compiling \""..line.."\": ".. expanded
return false, msg
end
expanded = expand_macros(ast_copy, macro_defs, false)
until expanded == false
else
return false, "Unexpected top-level AST type: "..ast.type
error("Unexpected top-level AST type: "..ast.type)
end
return true, ast
return ast
end
--[[
@@ -237,25 +208,22 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
if (error_msg) then
msg = "Compilation error when compiling \""..source.."\": "..error_msg
return false, msg
error(msg)
end
if (ast.type == "Rule") then
-- Line is a filter, so expand macro references
repeat
status, expanded = expand_macros(ast, macro_defs, false)
if status == false then
return false, expanded
end
expanded = expand_macros(ast, macro_defs, false)
until expanded == false
else
return false, "Unexpected top-level AST type: "..ast.type
error("Unexpected top-level AST type: "..ast.type)
end
filters = get_filters(ast)
return true, ast, filters
return ast, filters
end

View File

@@ -152,6 +152,22 @@ local function rel (left, sep, right)
return left * sep * right / function(e1, op, e2) return { type = "BinaryRelOp", operator = op, left = e1, right = e2 } end
end
local function fix_str (str)
str = string.gsub(str, "\\a", "\a")
str = string.gsub(str, "\\b", "\b")
str = string.gsub(str, "\\f", "\f")
str = string.gsub(str, "\\n", "\n")
str = string.gsub(str, "\\r", "\r")
str = string.gsub(str, "\\t", "\t")
str = string.gsub(str, "\\v", "\v")
str = string.gsub(str, "\\\n", "\n")
str = string.gsub(str, "\\\r", "\n")
str = string.gsub(str, "\\'", "'")
str = string.gsub(str, '\\"', '"')
str = string.gsub(str, '\\\\', '\\')
return str
end
-- grammar
@@ -227,7 +243,7 @@ local G = {
(digit^1 * V"Expo");
Number = C(V"Hex" + V"Float" + V"Int") /
function (n) return tonumber(n) end;
String = (P'"' * C(((P'\\' * P(1)) + (P(1) - P'"'))^0) * P'"' + P"'" * C(((P"\\" * P(1)) + (P(1) - P"'"))^0) * P"'");
String = (P'"' * C(((P'\\' * P(1)) + (P(1) - P'"'))^0) * P'"' + P"'" * C(((P"\\" * P(1)) + (P(1) - P"'"))^0) * P"'") / function (s) return fix_str(s) end;
BareString = C(((P(1) - S' (),='))^1);
OrOp = kw("or") / "or";

View File

@@ -59,13 +59,17 @@ function map(f, arr)
return res
end
priorities = {"Emergency", "Alert", "Critical", "Error", "Warning", "Notice", "Informational", "Debug"}
-- Permissive for case and for common abbreviations.
priorities = {
Emergency=0, Alert=1, Critical=2, Error=3, Warning=4, Notice=5, Informational=5, Debug=7,
EMERGENCY=0, ALERT=1, CRITICAL=2, ERROR=3, WARNING=4, NOTICE=5, INFORMATIONAL=5, DEBUG=7,
INFO=5
}
local function priority_num_for(s)
s = string.lower(s)
for i,v in ipairs(priorities) do
if (string.find(string.lower(v), "^"..s)) then
return i - 1 -- (numbers start at 0, lua indices start at 1)
end
end
error("Invalid priority level: "..s)
end
--[[
Take a filter AST and set it up in the libsinsp runtime, using the filter API.
@@ -179,71 +183,6 @@ function table.tostring( tbl )
return "{" .. table.concat( result, "," ) .. "}"
end
-- Split rules_content by lines and also remember the line numbers for
-- each top -level object. Returns a table of lines and a table of
-- line numbers for objects.
function split_lines(rules_content)
lines = {}
indices = {}
idx = 1
last_pos = 1
pos = string.find(rules_content, "\n", 1, true)
while pos ~= nil do
line = string.sub(rules_content, last_pos, pos-1)
if line ~= "" then
lines[#lines+1] = line
if string.sub(line, 1, 1) == '-' then
indices[#indices+1] = idx
end
idx = idx + 1
end
last_pos = pos+1
pos = string.find(rules_content, "\n", pos+1, true)
end
if last_pos < string.len(rules_content) then
line = string.sub(rules_content, last_pos)
lines[#lines+1] = line
if string.sub(line, 1, 1) == '-' then
indices[#indices+1] = idx
end
idx = idx + 1
end
-- Add a final index for last line in document
indices[#indices+1] = idx
return lines, indices
end
function get_context(rules_lines, row, num_lines)
local ret = "---\n"
idx = row
while (idx < (row + num_lines) and idx <= #rules_lines) do
ret = ret..rules_lines[idx].."\n"
idx = idx + 1
end
ret = ret.."---"
return ret
end
function build_error(rules_lines, row, num_lines, err)
local ret = err.."\n"..get_context(rules_lines, row, num_lines)
return ret
end
function load_rules(sinsp_lua_parser,
json_lua_parser,
@@ -255,45 +194,16 @@ function load_rules(sinsp_lua_parser,
replace_container_info,
min_priority)
local rules = yaml.load(rules_content)
local required_engine_version = 0
local lines, indices = split_lines(rules_content)
local status, rules = pcall(yaml.load, rules_content)
if status == false then
local pat = "^([%d]+):([%d]+): "
-- rules is actually an error string
local row = 0
local col = 0
row, col = string.match(rules, pat)
if row ~= nil and col ~= nil then
rules = string.gsub(rules, pat, "")
end
row = tonumber(row)
col = tonumber(col)
return false, build_error(lines, row, 3, rules)
end
if rules == nil then
-- An empty rules file is acceptable
return true, required_engine_version
return required_engine_version
end
if type(rules) ~= "table" then
return false, build_error(lines, 1, 1, "Rules content is not yaml")
end
-- Look for non-numeric indices--implies that document is not array
-- of objects.
for key, val in pairs(rules) do
if type(key) ~= "number" then
return false, build_error(lines, 1, 1, "Rules content is not yaml array of objects")
end
error("Rules content \""..rules_content.."\" is not yaml")
end
-- Iterate over yaml list. In this pass, all we're doing is
@@ -303,25 +213,17 @@ function load_rules(sinsp_lua_parser,
for i,v in ipairs(rules) do
if (not (type(v) == "table")) then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
error ("Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
end
if (v['required_engine_version']) then
required_engine_version = v['required_engine_version']
if type(required_engine_version) ~= "number" then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Value of required_engine_version must be a number")
end
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
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['macro'] == nil or type(v['macro']) == "table") then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro name is empty")
end
if v['source'] == nil then
v['source'] = "syscall"
end
@@ -330,9 +232,9 @@ function load_rules(sinsp_lua_parser,
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
end
for j, field in ipairs({'condition'}) do
for i, field in ipairs({'condition'}) do
if (v[field] == nil) then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro must have property "..field)
error ("Missing "..field.." in macro with name "..v['macro'])
end
end
@@ -345,7 +247,7 @@ function load_rules(sinsp_lua_parser,
if append then
if state.macros_by_name[v['macro']] == nil then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
error ("Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
end
state.macros_by_name[v['macro']]['condition'] = state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
@@ -356,17 +258,13 @@ function load_rules(sinsp_lua_parser,
elseif (v['list']) then
if (v['list'] == nil or type(v['list']) == "table") then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List name is empty")
end
if state.lists_by_name[v['list']] == nil then
state.ordered_list_names[#state.ordered_list_names+1] = v['list']
end
for j, field in ipairs({'items'}) do
for i, field in ipairs({'items'}) do
if (v[field] == nil) then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List must have property "..field)
error ("Missing "..field.." in list with name "..v['list'])
end
end
@@ -379,10 +277,10 @@ function load_rules(sinsp_lua_parser,
if append then
if state.lists_by_name[v['list']] == nil then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List " ..v['list'].. " has 'append' key but no list by that name already exists")
error ("List " ..v['list'].. " has 'append' key but no list by that name already exists")
end
for j, elem in ipairs(v['items']) do
for i, elem in ipairs(v['items']) do
table.insert(state.lists_by_name[v['list']]['items'], elem)
end
else
@@ -392,7 +290,7 @@ function load_rules(sinsp_lua_parser,
elseif (v['rule']) then
if (v['rule'] == nil or type(v['rule']) == "table") then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule name is empty")
error ("Missing name in rule")
end
-- By default, if a rule's condition refers to an unknown
@@ -415,15 +313,15 @@ function load_rules(sinsp_lua_parser,
if append then
-- For append rules, all you need is the condition
for j, field in ipairs({'condition'}) do
for i, field in ipairs({'condition'}) do
if (v[field] == nil) then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
error ("Missing "..field.." in rule with name "..v['rule'])
end
end
if state.rules_by_name[v['rule']] == nil then
if state.skipped_rules_by_name[v['rule']] == nil then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
error ("Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
end
else
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
@@ -431,18 +329,14 @@ function load_rules(sinsp_lua_parser,
else
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
for i, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
if (v[field] == nil) then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
error ("Missing "..field.." in rule with name "..v['rule'])
end
end
-- Convert the priority-as-string to a priority-as-number now
v['priority_num'] = priorities[v['priority']]
if v['priority_num'] == nil then
error("Invalid priority level: "..v['priority'])
end
v['priority_num'] = priority_num_for(v['priority'])
if v['priority_num'] <= min_priority then
-- Note that we can overwrite rules, but the rules are still
@@ -462,7 +356,7 @@ function load_rules(sinsp_lua_parser,
end
end
else
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unknown rule object: "..table.tostring(v))
error ("Unknown rule object: "..table.tostring(v))
end
end
@@ -499,11 +393,7 @@ function load_rules(sinsp_lua_parser,
local v = state.macros_by_name[name]
local status, ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
if status == false then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), ast)
end
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
if v['source'] == "syscall" then
if not all_events then
@@ -523,12 +413,8 @@ function load_rules(sinsp_lua_parser,
warn_evttypes = v['warn_evttypes']
end
local status, filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
state.macros, state.lists)
if status == false then
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), filter_ast)
end
local filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
state.macros, state.lists)
local evtttypes = {}
local syscallnums = {}
@@ -547,32 +433,10 @@ function load_rules(sinsp_lua_parser,
for filter, _ in pairs(filters) do
found = false
if defined_noarg_filters[filter] ~= nil then
found = true
else
bracket_idx = string.find(filter, "[", 1, true)
if bracket_idx ~= nil then
subfilter = string.sub(filter, 1, bracket_idx-1)
if defined_arg_filters[subfilter] ~= nil then
found = true
end
end
if not found then
dot_idx = string.find(filter, ".", 1, true)
while dot_idx ~= nil do
subfilter = string.sub(filter, 1, dot_idx-1)
if defined_arg_filters[subfilter] ~= nil then
found = true
break
end
dot_idx = string.find(filter, ".", dot_idx+1, true)
end
for pat, _ in pairs(defined_filters) do
if string.match(filter, pat) ~= nil then
found = true
break
end
end
@@ -665,7 +529,7 @@ function load_rules(sinsp_lua_parser,
formatter = formats.formatter(v['source'], v['output'])
formats.free_formatter(v['source'], formatter)
else
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected type in load_rule: "..filter_ast.type)
error ("Unexpected type in load_rule: "..filter_ast.type)
end
::next_rule::
@@ -688,7 +552,7 @@ function load_rules(sinsp_lua_parser,
io.flush()
return true, required_engine_version
return required_engine_version
end
local rule_fmt = "%-50s %s"

View File

@@ -17,8 +17,6 @@ limitations under the License.
*/
#include <sstream>
#include "rules.h"
#include "logger.h"
@@ -325,12 +323,12 @@ void falco_rules::load_rules(const string &rules_content,
lua_setglobal(m_ls, m_lua_ignored_syscalls.c_str());
// Create a table containing all filtercheck names.
lua_newtable(m_ls);
vector<const filter_check_info*> fc_plugins;
sinsp::get_filtercheck_fields_info(&fc_plugins);
set<string> no_argument_filters;
set<string> argument_filters;
for(uint32_t j = 0; j < fc_plugins.size(); j++)
{
const filter_check_info* fci = fc_plugins[j];
@@ -352,71 +350,45 @@ void falco_rules::load_rules(const string &rules_content,
// Some filters can work with or without an argument
std::set<string> flexible_filters = {
"proc.aname",
"proc.apid"
"^proc.aname",
"^proc.apid"
};
std::list<string> fields;
std::string field_base = string("^") + fld->m_name;
if(fld->m_flags & EPF_REQUIRES_ARGUMENT ||
flexible_filters.find(fld->m_name) != flexible_filters.end())
flexible_filters.find(field_base) != flexible_filters.end())
{
argument_filters.insert(fld->m_name);
fields.push_back(field_base + "[%[%.]");
}
if(!(fld->m_flags & EPF_REQUIRES_ARGUMENT) ||
flexible_filters.find(fld->m_name) != flexible_filters.end())
flexible_filters.find(field_base) != flexible_filters.end())
{
no_argument_filters.insert(fld->m_name);
fields.push_back(field_base + "$");
}
for(auto &field : fields)
{
lua_pushstring(m_ls, field.c_str());
lua_pushnumber(m_ls, 1);
lua_settable(m_ls, -3);
}
}
}
for(auto &chk_field : m_engine->json_factory().get_fields())
{
for(auto &field : chk_field.m_fields)
for(auto &field : chk_field.fields)
{
switch(field.m_idx_mode)
{
case json_event_filter_check::IDX_REQUIRED:
argument_filters.insert(field.m_name);
break;
case json_event_filter_check::IDX_ALLOWED:
argument_filters.insert(field.m_name);
no_argument_filters.insert(field.m_name);
break;
case json_event_filter_check::IDX_NONE:
no_argument_filters.insert(field.m_name);
break;
default:
break;
}
lua_pushstring(m_ls, field.name.c_str());
lua_pushnumber(m_ls, 1);
lua_settable(m_ls, -3);
}
}
// Create tables containing all filtercheck
// names. They are split into names that require
// arguments and ones that do not.
lua_newtable(m_ls);
for(auto &field : argument_filters)
{
lua_pushstring(m_ls, field.c_str());
lua_pushnumber(m_ls, 1);
lua_settable(m_ls, -3);
}
lua_setglobal(m_ls, m_lua_defined_arg_filters.c_str());
lua_newtable(m_ls);
for(auto &field : no_argument_filters)
{
lua_pushstring(m_ls, field.c_str());
lua_pushnumber(m_ls, 1);
lua_settable(m_ls, -3);
}
lua_setglobal(m_ls, m_lua_defined_noarg_filters.c_str());
lua_setglobal(m_ls, m_lua_defined_filters.c_str());
lua_pushlightuserdata(m_ls, m_sinsp_lua_parser);
lua_pushlightuserdata(m_ls, m_json_lua_parser);
@@ -427,30 +399,15 @@ void falco_rules::load_rules(const string &rules_content,
lua_pushstring(m_ls, extra.c_str());
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
lua_pushnumber(m_ls, min_priority);
if(lua_pcall(m_ls, 9, 2, 0) != 0)
if(lua_pcall(m_ls, 9, 1, 0) != 0)
{
const char* lerr = lua_tostring(m_ls, -1);
string err = "Error loading rules: " + string(lerr);
throw falco_exception(err);
}
// Either returns (true, required_engine_version), or (false, error string)
bool successful = lua_toboolean(m_ls, -2);
if(successful)
{
required_engine_version = lua_tonumber(m_ls, -1);
}
else
{
std::string err = lua_tostring(m_ls, -1);
throw falco_exception(err);
}
lua_pop(m_ls, 4);
required_engine_version = lua_tonumber(m_ls, -1);
lua_pop(m_ls, 1);
} else {
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
}

View File

@@ -21,7 +21,6 @@ limitations under the License.
#include <set>
#include <memory>
#include <regex>
#include "sinsp.h"
#include "filter.h"
@@ -58,18 +57,17 @@ class falco_rules
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<uint32_t> &syscalls, std::set<string> &tags);
void add_k8s_audit_filter(string &rule, std::set<string> &tags);
void enable_rule(string &rule, bool enabled);
std::string get_context(const std::string &content, uint64_t line, uint64_t column);
lua_parser* m_sinsp_lua_parser;
lua_parser* m_json_lua_parser;
sinsp* m_inspector;
falco_engine *m_engine;
lua_State* m_ls;
string m_lua_load_rules = "load_rules";
string m_lua_ignored_syscalls = "ignored_syscalls";
string m_lua_ignored_events = "ignored_events";
string m_lua_defined_arg_filters = "defined_arg_filters";
string m_lua_defined_noarg_filters = "defined_noarg_filters";
string m_lua_defined_filters = "defined_filters";
string m_lua_events = "events";
string m_lua_syscalls = "syscalls";
string m_lua_describe_rule = "describe_rule";

View File

@@ -18,20 +18,13 @@ limitations under the License.
*/
#include <cstddef>
#include <functional>
#include <sys/time.h>
#include "token_bucket.h"
#include "utils.h"
#include "token_bucket.h"
token_bucket::token_bucket():
token_bucket(sinsp_utils::get_current_time_ns)
token_bucket::token_bucket()
{
}
token_bucket::token_bucket(std::function<uint64_t()> timer)
{
m_timer = timer;
init(1, 1);
}
@@ -44,12 +37,20 @@ void token_bucket::init(double rate, double max_tokens, uint64_t now)
m_rate = rate;
m_max_tokens = max_tokens;
m_tokens = max_tokens;
m_last_seen = now == 0 ? m_timer() : now;
if(now == 0)
{
now = sinsp_utils::get_current_time_ns();
}
m_last_seen = now;
}
bool token_bucket::claim()
{
return claim(1, m_timer());
uint64_t now = sinsp_utils::get_current_time_ns();
return claim(1, now);
}
bool token_bucket::claim(double tokens, uint64_t now)

View File

@@ -20,7 +20,6 @@ limitations under the License.
#pragma once
#include <cstdint>
#include <functional>
// A simple token bucket that accumulates tokens at a fixed rate and allows
// for limited bursting in the form of "banked" tokens.
@@ -28,7 +27,6 @@ class token_bucket
{
public:
token_bucket();
token_bucket(std::function<uint64_t()> timer);
virtual ~token_bucket();
//
@@ -54,7 +52,6 @@ public:
uint64_t get_last_seen();
private:
std::function<uint64_t()> m_timer;
//
// The number of tokens generated per second.
@@ -78,3 +75,4 @@ private:
//
uint64_t m_last_seen;
};

View File

@@ -15,11 +15,23 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
if(NOT SYSDIG_DIR)
set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
endif()
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp/third-party/jsoncpp")
include_directories("${LUAJIT_INCLUDE}")
configure_file("${SYSDIG_DIR}/userspace/sysdig/config_sysdig.h.in" config_sysdig.h)
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libscap")
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig")
include_directories("${PROJECT_SOURCE_DIR}/userspace/engine")
include_directories("${PROJECT_BINARY_DIR}/userspace/falco")
include_directories("${PROJECT_BINARY_DIR}/driver/src")
include_directories("${CURL_INCLUDE_DIR}")
include_directories("${TBB_INCLUDE_DIR}")
include_directories("${NJSON_INCLUDE}")
include_directories("${YAMLCPP_INCLUDE_DIR}")
include_directories("${CIVETWEB_INCLUDE_DIR}")
include_directories("${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include")
configure_file("${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig/config_sysdig.h.in" config_sysdig.h)
add_executable(falco
configuration.cpp
@@ -28,21 +40,12 @@ add_executable(falco
event_drops.cpp
statsfilewriter.cpp
falco.cpp
"${SYSDIG_DIR}/userspace/sysdig/fields_info.cpp"
"${PROJECT_SOURCE_DIR}/../sysdig/userspace/sysdig/fields_info.cpp"
webserver.cpp)
target_include_directories(falco PUBLIC
"${SYSDIG_DIR}/userspace/sysdig"
"${PROJECT_SOURCE_DIR}/userspace/engine"
"${PROJECT_BINARY_DIR}/userspace/falco"
"${PROJECT_BINARY_DIR}/driver/src"
"${YAMLCPP_INCLUDE_DIR}"
"${CIVETWEB_INCLUDE_DIR}"
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include")
target_link_libraries(falco falco_engine sinsp)
target_link_libraries(falco
"${LIBYAML_LIB}"
"${LIBYAML_LIB}"
"${YAMLCPP_LIB}"
"${CIVETWEB_LIB}")

View File

@@ -716,17 +716,7 @@ int falco_init(int argc, char **argv)
}
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\n", prefix.c_str(), e.what());
throw;
}
printf("%sOk\n", prefix.c_str());
engine->load_rules_file(file, verbose, all_events);
}
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;