mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-27 15:12:08 +00:00
Compare commits
149 Commits
0.4.0
...
agent/0.53
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31464de885 | ||
|
|
df08a80a12 | ||
|
|
8a1f62c610 | ||
|
|
9b308d2793 | ||
|
|
3d5789a297 | ||
|
|
b9d0857362 | ||
|
|
1afbaba632 | ||
|
|
e0a5034a43 | ||
|
|
6356490b1c | ||
|
|
511d0997da | ||
|
|
6f9f1e4792 | ||
|
|
a99f09da96 | ||
|
|
c09b6390a3 | ||
|
|
3f2814259a | ||
|
|
b04bccd1a7 | ||
|
|
e21fecf0ef | ||
|
|
ceafeca87e | ||
|
|
9285aa59c1 | ||
|
|
1e0ddba11a | ||
|
|
34e17cb951 | ||
|
|
bc83ac18a0 | ||
|
|
10d0c8f982 | ||
|
|
8f53bcbb05 | ||
|
|
7286b50f4d | ||
|
|
4c60b7c1d2 | ||
|
|
85480f32d6 | ||
|
|
4139370df5 | ||
|
|
b6d1101cb6 | ||
|
|
43d53bb09e | ||
|
|
af3a708251 | ||
|
|
f4bb49f1f5 | ||
|
|
362a6b7b9a | ||
|
|
77a5429cae | ||
|
|
9ecdf30314 | ||
|
|
7c419b6d6b | ||
|
|
767f2d5bb4 | ||
|
|
3cbf641ded | ||
|
|
4ab72d0391 | ||
|
|
9e933ce5ba | ||
|
|
c3c6ec67f7 | ||
|
|
9062459669 | ||
|
|
94cef1b541 | ||
|
|
dd6b4fd7c0 | ||
|
|
c6953e810b | ||
|
|
104c99c42e | ||
|
|
f2bfa584e4 | ||
|
|
6f54a752a2 | ||
|
|
6c04f53d24 | ||
|
|
db67034338 | ||
|
|
2a2dcaf25d | ||
|
|
e6aefef4eb | ||
|
|
7db8e0921c | ||
|
|
ea97325708 | ||
|
|
3840622984 | ||
|
|
0ee32178b7 | ||
|
|
8b116c2ad1 | ||
|
|
37388c56ff | ||
|
|
0d46fcf819 | ||
|
|
c6c074ef60 | ||
|
|
14c9d05f9f | ||
|
|
882c6c94ea | ||
|
|
349372d733 | ||
|
|
858a69bb2c | ||
|
|
1d0c9b1714 | ||
|
|
8aa9c21d11 | ||
|
|
64ecd157fd | ||
|
|
2bad529d33 | ||
|
|
09a9ab4f85 | ||
|
|
39e9043ac7 | ||
|
|
f4abec4639 | ||
|
|
bed5ab4f0c | ||
|
|
4f645c49e1 | ||
|
|
54b30bc248 | ||
|
|
b509c4f0c8 | ||
|
|
af8d6c9d10 | ||
|
|
ef08478bb7 | ||
|
|
a616301bd9 | ||
|
|
8e2a3ef5c3 | ||
|
|
47bd6af69a | ||
|
|
d1d0dbdbde | ||
|
|
212fd9353e | ||
|
|
28558959f3 | ||
|
|
a8662c60da | ||
|
|
b3c691e920 | ||
|
|
ded3ee5bed | ||
|
|
064b39f2be | ||
|
|
2961eb4d21 | ||
|
|
704eb57e3c | ||
|
|
9ca8ed96b9 | ||
|
|
8b18315c1e | ||
|
|
f95a0ead62 | ||
|
|
b1ad9e644e | ||
|
|
8a2924ad72 | ||
|
|
f98ec60c88 | ||
|
|
0211a94f60 | ||
|
|
e0e640c67f | ||
|
|
faef5621dd | ||
|
|
e543fbf247 | ||
|
|
f761ddff9f | ||
|
|
1f7c711a69 | ||
|
|
880c39633d | ||
|
|
3bb84f5498 | ||
|
|
7e60b4b6c2 | ||
|
|
1a78e45d7a | ||
|
|
20440912b7 | ||
|
|
f6720d3993 | ||
|
|
82903359cb | ||
|
|
144789475e | ||
|
|
644f017b2a | ||
|
|
5008003600 | ||
|
|
82597c9830 | ||
|
|
4354043a44 | ||
|
|
08d204dde9 | ||
|
|
9a5e08d712 | ||
|
|
930b38b894 | ||
|
|
889b252a3f | ||
|
|
164d5016ef | ||
|
|
6e9241a983 | ||
|
|
23e3e99162 | ||
|
|
f632fa62b0 | ||
|
|
33b9ef5d50 | ||
|
|
fbcddba06a | ||
|
|
5644919e70 | ||
|
|
f974922f84 | ||
|
|
08c3befe25 | ||
|
|
ef52e627ec | ||
|
|
23a9b6e1b0 | ||
|
|
3ee1c0f602 | ||
|
|
ceee146f39 | ||
|
|
ceedd772c7 | ||
|
|
2731fd5ae1 | ||
|
|
e717e3e3e0 | ||
|
|
34fcce7c26 | ||
|
|
822770a154 | ||
|
|
65f3725e76 | ||
|
|
6e1f23b9a5 | ||
|
|
2aa8a5c114 | ||
|
|
39ae7680a7 | ||
|
|
12391ee508 | ||
|
|
dcaeebda77 | ||
|
|
f1748060c5 | ||
|
|
09405e4fad | ||
|
|
b1857eff35 | ||
|
|
fc9690b1d3 | ||
|
|
03e6c1b3d9 | ||
|
|
bf431cf222 | ||
|
|
b57eb8659f | ||
|
|
f82288f373 | ||
|
|
a769373bb8 |
@@ -32,7 +32,7 @@ script:
|
||||
- cd ..
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DDRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG"
|
||||
- make VERBOSE=1
|
||||
- make package
|
||||
- cd ..
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -2,6 +2,38 @@
|
||||
|
||||
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
|
||||
|
||||
## v0.5.0
|
||||
|
||||
Released 2016-12-22
|
||||
|
||||
Starting with this release, we're adding a new section "Rule Changes" devoted to changes to the default ruleset `falco_rules.yaml`.
|
||||
|
||||
### Major Changes
|
||||
|
||||
* Cache event formatting objects so they are not re-created for every falco notification. This can result in significant speedups when the ruleset results in lots of notifications. [[#158](https://github.com/draios/falco/pull/158)]
|
||||
* Falco notifications are now throttled by a token bucket, preventing a flood of notifications when many events match a rule. Controlled by the `outputs, rate` and `outputs, max_burst` options. [[#161](https://github.com/draios/falco/pull/161)]
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* When run from a container, you can provide the environment variable `SYSDIG_SKIP_LOAD` to skip the process of building/loading the kernel module. Thanks @carlsverre for the fix. [[#145](https://github.com/draios/falco/pull/145)]
|
||||
* Fully implement `USE_BUNDLED_DEPS` within CMakeFiles so you can build with external third-party libraries. [[#147](https://github.com/draios/falco/pull/147)]
|
||||
* Improve error messages that result when trying to load a rule with a malformed `output:` attribute [[#150](https://github.com/draios/falco/pull/150)] [[#151](https://github.com/draios/falco/pull/151)]
|
||||
* Add the ability to write event capture statistics to a file via the `-s <statsfile>` option. [[#155](https://github.com/draios/falco/pull/155)]
|
||||
* New configuration option `log_level` controls the verbosity of falco's logging. [[#160](https://github.com/draios/falco/pull/160)]
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Improve compatibility with Sysdig Cloud Agent build [[#148](https://github.com/draios/falco/pull/148)]
|
||||
|
||||
### Rule Changes
|
||||
|
||||
* Add DNF as non-alerting for RPM and package management. Thanks @djcross for the fix. [[#153](https://github.com/draios/falco/pull/153)]
|
||||
* Make `google_containers/kube-proxy` a trusted image, affecting the File Open by Privileged Container/Sensitive Mount by Container rules. [[#159](https://github.com/draios/falco/pull/159)]
|
||||
* Add fail2ban-server as a program that can spawn shells. Thanks @jcoetzee for the fix. [[#168](https://github.com/draios/falco/pull/168)]
|
||||
* Add systemd as a program that can access sensitive files. Thanks @jcoetzee for the fix. [[#169](https://github.com/draios/falco/pull/169)]
|
||||
* Add apt/apt-get as programs that can spawn shells. Thanks @jcoetzee for the fix. [[#170](https://github.com/draios/falco/pull/170)]
|
||||
|
||||
## v0.4.0
|
||||
|
||||
Released 2016-10-25
|
||||
|
||||
335
CMakeLists.txt
335
CMakeLists.txt
@@ -14,7 +14,9 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
|
||||
if(NOT DRAIOS_DEBUG_FLAGS)
|
||||
set(DRAIOS_DEBUG_FLAGS "-D_DEBUG")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall -ggdb ${DRAIOS_FEATURE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "-Wall -ggdb --std=c++0x ${DRAIOS_FEATURE_FLAGS}")
|
||||
@@ -47,150 +49,323 @@ set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig")
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
set(ZLIB_SRC "${PROJECT_BINARY_DIR}/zlib-prefix/src/zlib")
|
||||
message(STATUS "Using bundled zlib in '${ZLIB_SRC}'")
|
||||
set(ZLIB_INCLUDE "${ZLIB_SRC}")
|
||||
set(ZLIB_LIB "${ZLIB_SRC}/libz.a")
|
||||
ExternalProject_Add(zlib
|
||||
option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system ones" ON)
|
||||
|
||||
#
|
||||
# zlib
|
||||
#
|
||||
option(USE_BUNDLED_ZLIB "Enable building of the bundled zlib" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_ZLIB)
|
||||
find_path(ZLIB_INCLUDE zlib.h PATH_SUFFIXES zlib)
|
||||
find_library(ZLIB_LIB NAMES z)
|
||||
if(ZLIB_INCLUDE AND ZLIB_LIB)
|
||||
message(STATUS "Found zlib: include: ${ZLIB_INCLUDE}, lib: ${ZLIB_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system zlib")
|
||||
endif()
|
||||
else()
|
||||
set(ZLIB_SRC "${PROJECT_BINARY_DIR}/zlib-prefix/src/zlib")
|
||||
message(STATUS "Using bundled zlib in '${ZLIB_SRC}'")
|
||||
set(ZLIB_INCLUDE "${ZLIB_SRC}")
|
||||
set(ZLIB_LIB "${ZLIB_SRC}/libz.a")
|
||||
ExternalProject_Add(zlib
|
||||
URL "http://download.draios.com/dependencies/zlib-1.2.8.tar.gz"
|
||||
URL_MD5 "44d667c142d7cda120332623eab69f40"
|
||||
CONFIGURE_COMMAND "./configure"
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set(JQ_SRC "${PROJECT_BINARY_DIR}/jq-prefix/src/jq")
|
||||
message(STATUS "Using bundled jq in '${JQ_SRC}'")
|
||||
set(JQ_INCLUDE "${JQ_SRC}")
|
||||
set(JQ_LIB "${JQ_SRC}/.libs/libjq.a")
|
||||
ExternalProject_Add(jq
|
||||
#
|
||||
# jq
|
||||
#
|
||||
option(USE_BUNDLED_JQ "Enable building of the bundled jq" ${USE_BUNDLED_DEPS})
|
||||
if(NOT USE_BUNDLED_JQ)
|
||||
find_path(JQ_INCLUDE jq.h PATH_SUFFIXES jq)
|
||||
find_library(JQ_LIB NAMES jq)
|
||||
if(JQ_INCLUDE AND JQ_LIB)
|
||||
message(STATUS "Found jq: include: ${JQ_INCLUDE}, lib: ${JQ_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system jq")
|
||||
endif()
|
||||
else()
|
||||
set(JQ_SRC "${PROJECT_BINARY_DIR}/jq-prefix/src/jq")
|
||||
message(STATUS "Using bundled jq in '${JQ_SRC}'")
|
||||
set(JQ_INCLUDE "${JQ_SRC}")
|
||||
set(JQ_LIB "${JQ_SRC}/.libs/libjq.a")
|
||||
ExternalProject_Add(jq
|
||||
URL "http://download.draios.com/dependencies/jq-1.5.tar.gz"
|
||||
URL_MD5 "0933532b086bd8b6a41c1b162b1731f9"
|
||||
CONFIGURE_COMMAND ./configure --disable-maintainer-mode --enable-all-static --disable-dependency-tracking
|
||||
BUILD_COMMAND ${CMD_MAKE} LDFLAGS=-all-static
|
||||
BUILD_IN_SOURCE 1
|
||||
PATCH_COMMAND wget -O jq-1.5-fix-tokenadd.patch https://github.com/stedolan/jq/commit/8eb1367ca44e772963e704a700ef72ae2e12babd.patch && patch -i jq-1.5-fix-tokenadd.patch
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set(JSONCPP_SRC "${SYSDIG_DIR}/userspace/libsinsp/third-party/jsoncpp")
|
||||
set(JSONCPP_INCLUDE "${JSONCPP_SRC}")
|
||||
set(JSONCPP_LIB_SRC "${JSONCPP_SRC}/jsoncpp.cpp")
|
||||
|
||||
#
|
||||
# curses
|
||||
#
|
||||
# we pull this in because libsinsp won't build without it
|
||||
set(CURSES_BUNDLE_DIR "${PROJECT_BINARY_DIR}/ncurses-prefix/src/ncurses")
|
||||
set(CURSES_INCLUDE_DIR "${CURSES_BUNDLE_DIR}/include/")
|
||||
set(CURSES_LIBRARIES "${CURSES_BUNDLE_DIR}/lib/libncurses.a")
|
||||
message(STATUS "Using bundled ncurses in '${CURSES_BUNDLE_DIR}'")
|
||||
ExternalProject_Add(ncurses
|
||||
|
||||
option(USE_BUNDLED_NCURSES "Enable building of the bundled ncurses" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_NCURSES)
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
find_package(Curses REQUIRED)
|
||||
message(STATUS "Found ncurses: include: ${CURSES_INCLUDE_DIR}, lib: ${CURSES_LIBRARIES}")
|
||||
else()
|
||||
set(CURSES_BUNDLE_DIR "${PROJECT_BINARY_DIR}/ncurses-prefix/src/ncurses")
|
||||
set(CURSES_INCLUDE_DIR "${CURSES_BUNDLE_DIR}/include/")
|
||||
set(CURSES_LIBRARIES "${CURSES_BUNDLE_DIR}/lib/libncurses.a")
|
||||
message(STATUS "Using bundled ncurses in '${CURSES_BUNDLE_DIR}'")
|
||||
ExternalProject_Add(ncurses
|
||||
URL "http://download.draios.com/dependencies/ncurses-6.0-20150725.tgz"
|
||||
URL_MD5 "32b8913312e738d707ae68da439ca1f4"
|
||||
CONFIGURE_COMMAND ./configure --without-cxx --without-cxx-binding --without-ada --without-manpages --without-progs --without-tests --with-terminfo-dirs=/etc/terminfo:/lib/terminfo:/usr/share/terminfo
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# libb64
|
||||
#
|
||||
option(USE_BUNDLED_B64 "Enable building of the bundled b64" ${USE_BUNDLED_DEPS})
|
||||
|
||||
set(B64_SRC "${PROJECT_BINARY_DIR}/b64-prefix/src/b64")
|
||||
message(STATUS "Using bundled b64 in '${B64_SRC}'")
|
||||
set(B64_INCLUDE "${B64_SRC}/include")
|
||||
set(B64_LIB "${B64_SRC}/src/libb64.a")
|
||||
ExternalProject_Add(b64
|
||||
if(NOT USE_BUNDLED_B64)
|
||||
find_path(B64_INCLUDE NAMES b64/encode.h)
|
||||
find_library(B64_LIB NAMES b64)
|
||||
if(B64_INCLUDE AND B64_LIB)
|
||||
message(STATUS "Found b64: include: ${B64_INCLUDE}, lib: ${B64_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system b64")
|
||||
endif()
|
||||
else()
|
||||
set(B64_SRC "${PROJECT_BINARY_DIR}/b64-prefix/src/b64")
|
||||
message(STATUS "Using bundled b64 in '${B64_SRC}'")
|
||||
set(B64_INCLUDE "${B64_SRC}/include")
|
||||
set(B64_LIB "${B64_SRC}/src/libb64.a")
|
||||
ExternalProject_Add(b64
|
||||
URL "http://download.draios.com/dependencies/libb64-1.2.src.zip"
|
||||
URL_MD5 "a609809408327117e2c643bed91b76c5"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
#
|
||||
# yamlcpp
|
||||
#
|
||||
option(USE_BUNDLED_YAMLCPP "Enable building of the bundled yamlcpp" ${USE_BUNDLED_DEPS})
|
||||
|
||||
set(YAMLCPP_SRC "${PROJECT_BINARY_DIR}/yamlcpp-prefix/src/yamlcpp")
|
||||
message(STATUS "Using bundled yaml-cpp in '${YAMLCPP_SRC}'")
|
||||
set(YAMLCPP_LIB "${YAMLCPP_SRC}/libyaml-cpp.a")
|
||||
set(YAMLCPP_INCLUDE_DIR "${YAMLCPP_SRC}/include")
|
||||
# Once the next version of yaml-cpp is released (first version not requiring
|
||||
# boost), we can switch to that and no longer pull from github.
|
||||
ExternalProject_Add(yamlcpp
|
||||
if(NOT USE_BUNDLED_YAMLCPP)
|
||||
find_path(YAMLCPP_INCLUDE_DIR NAMES yaml-cpp/yaml.h)
|
||||
find_library(YAMLCPP_LIB NAMES yaml-cpp)
|
||||
if(YAMLCPP_INCLUDE_DIR AND YAMLCPP_LIB)
|
||||
message(STATUS "Found yamlcpp: include: ${YAMLCPP_INCLUDE_DIR}, lib: ${YAMLCPP_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system yamlcpp")
|
||||
endif()
|
||||
else()
|
||||
set(YAMLCPP_SRC "${PROJECT_BINARY_DIR}/yamlcpp-prefix/src/yamlcpp")
|
||||
message(STATUS "Using bundled yaml-cpp in '${YAMLCPP_SRC}'")
|
||||
set(YAMLCPP_LIB "${YAMLCPP_SRC}/libyaml-cpp.a")
|
||||
set(YAMLCPP_INCLUDE_DIR "${YAMLCPP_SRC}/include")
|
||||
# Once the next version of yaml-cpp is released (first version not requiring
|
||||
# boost), we can switch to that and no longer pull from github.
|
||||
ExternalProject_Add(yamlcpp
|
||||
GIT_REPOSITORY "https://github.com/jbeder/yaml-cpp.git"
|
||||
GIT_TAG "7d2873ce9f2202ea21b6a8c5ecbc9fe38032c229"
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set(OPENSSL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl")
|
||||
set(OPENSSL_INSTALL_DIR "${OPENSSL_BUNDLE_DIR}/target")
|
||||
set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include")
|
||||
set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl.a")
|
||||
set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a")
|
||||
#
|
||||
# OpenSSL
|
||||
#
|
||||
option(USE_BUNDLED_OPENSSL "Enable building of the bundled OpenSSL" ${USE_BUNDLED_DEPS})
|
||||
|
||||
message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'")
|
||||
if(NOT USE_BUNDLED_OPENSSL)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message(STATUS "Found OpenSSL: include: ${OPENSSL_INCLUDE_DIR}, lib: ${OPENSSL_LIBRARIES}")
|
||||
else()
|
||||
|
||||
ExternalProject_Add(openssl
|
||||
URL "http://download.draios.com/dependencies/openssl-1.0.2d.tar.gz"
|
||||
URL_MD5 "38dd619b2e77cbac69b99f52a053d25a"
|
||||
set(OPENSSL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl")
|
||||
set(OPENSSL_INSTALL_DIR "${OPENSSL_BUNDLE_DIR}/target")
|
||||
set(OPENSSL_INCLUDE_DIR "${PROJECT_BINARY_DIR}/openssl-prefix/src/openssl/include")
|
||||
set(OPENSSL_LIBRARY_SSL "${OPENSSL_INSTALL_DIR}/lib/libssl.a")
|
||||
set(OPENSSL_LIBRARY_CRYPTO "${OPENSSL_INSTALL_DIR}/lib/libcrypto.a")
|
||||
|
||||
message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'")
|
||||
|
||||
ExternalProject_Add(openssl
|
||||
URL "http://download.draios.com/dependencies/openssl-1.0.2j.tar.gz"
|
||||
URL_MD5 "96322138f0b69e61b7212bc53d5e912b"
|
||||
CONFIGURE_COMMAND ./config shared --prefix=${OPENSSL_INSTALL_DIR}
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND ${CMD_MAKE} install)
|
||||
endif()
|
||||
|
||||
set(CURL_SSL_OPTION "--with-ssl=${OPENSSL_INSTALL_DIR}")
|
||||
#
|
||||
# libcurl
|
||||
#
|
||||
option(USE_BUNDLED_CURL "Enable building of the bundled curl" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_CURL)
|
||||
find_package(CURL REQUIRED)
|
||||
message(STATUS "Found CURL: include: ${CURL_INCLUDE_DIR}, lib: ${CURL_LIBRARIES}")
|
||||
else()
|
||||
set(CURL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/curl-prefix/src/curl")
|
||||
set(CURL_INCLUDE_DIR "${CURL_BUNDLE_DIR}/include/")
|
||||
set(CURL_LIBRARIES "${CURL_BUNDLE_DIR}/lib/.libs/libcurl.a")
|
||||
|
||||
set(CURL_BUNDLE_DIR "${PROJECT_BINARY_DIR}/curl-prefix/src/curl")
|
||||
set(CURL_INCLUDE_DIR "${CURL_BUNDLE_DIR}/include/")
|
||||
set(CURL_LIBRARIES "${CURL_BUNDLE_DIR}/lib/.libs/libcurl.a")
|
||||
message(STATUS "Using bundled curl in '${CURL_BUNDLE_DIR}'")
|
||||
message(STATUS "Using SSL for curl in '${CURL_SSL_OPTION}'")
|
||||
if(NOT USE_BUNDLED_OPENSSL)
|
||||
set(CURL_SSL_OPTION "--with-ssl")
|
||||
else()
|
||||
set(CURL_SSL_OPTION "--with-ssl=${OPENSSL_INSTALL_DIR}")
|
||||
message(STATUS "Using bundled curl in '${CURL_BUNDLE_DIR}'")
|
||||
message(STATUS "Using SSL for curl in '${CURL_SSL_OPTION}'")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(curl
|
||||
ExternalProject_Add(curl
|
||||
DEPENDS openssl
|
||||
URL "http://download.draios.com/dependencies/curl-7.45.0.tar.bz2"
|
||||
URL_MD5 "62c1a352b28558f25ba6209214beadc8"
|
||||
URL "http://download.draios.com/dependencies/curl-7.52.1.tar.bz2"
|
||||
URL_MD5 "dd014df06ff1d12e173de86873f9f77a"
|
||||
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
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set(LUAJIT_SRC "${PROJECT_BINARY_DIR}/luajit-prefix/src/luajit/src")
|
||||
message(STATUS "Using bundled LuaJIT in '${LUAJIT_SRC}'")
|
||||
set(LUAJIT_INCLUDE "${LUAJIT_SRC}")
|
||||
set(LUAJIT_LIB "${LUAJIT_SRC}/libluajit.a")
|
||||
ExternalProject_Add(luajit
|
||||
#
|
||||
# LuaJIT
|
||||
#
|
||||
option(USE_BUNDLED_LUAJIT "Enable building of the bundled LuaJIT" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_LUAJIT)
|
||||
find_path(LUAJIT_INCLUDE luajit.h PATH_SUFFIXES luajit-2.0 luajit)
|
||||
find_library(LUAJIT_LIB NAMES luajit luajit-5.1)
|
||||
if(LUAJIT_INCLUDE AND LUAJIT_LIB)
|
||||
message(STATUS "Found LuaJIT: include: ${LUAJIT_INCLUDE}, lib: ${LUAJIT_LIB}")
|
||||
else()
|
||||
# alternatively try stock Lua
|
||||
find_package(Lua51)
|
||||
set(LUAJIT_LIB ${LUA_LIBRARY})
|
||||
set(LUAJIT_INCLUDE ${LUA_INCLUDE_DIR})
|
||||
|
||||
if(NOT ${LUA51_FOUND})
|
||||
message(FATAL_ERROR "Couldn't find system LuaJIT or Lua")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
set(LUAJIT_SRC "${PROJECT_BINARY_DIR}/luajit-prefix/src/luajit/src")
|
||||
message(STATUS "Using bundled LuaJIT in '${LUAJIT_SRC}'")
|
||||
set(LUAJIT_INCLUDE "${LUAJIT_SRC}")
|
||||
set(LUAJIT_LIB "${LUAJIT_SRC}/libluajit.a")
|
||||
ExternalProject_Add(luajit
|
||||
URL "http://download.draios.com/dependencies/LuaJIT-2.0.3.tar.gz"
|
||||
URL_MD5 "f14e9104be513913810cd59c8c658dc0"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set (LPEG_SRC "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg")
|
||||
set (LPEG_LIB "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg/build/lpeg.a")
|
||||
ExternalProject_Add(lpeg
|
||||
DEPENDS luajit
|
||||
URL "http://s3.amazonaws.com/download.draios.com/dependencies/lpeg-1.0.0.tar.gz"
|
||||
URL_MD5 "0aec64ccd13996202ad0c099e2877ece"
|
||||
BUILD_COMMAND LUA_INCLUDE=${LUAJIT_INCLUDE} "${PROJECT_SOURCE_DIR}/scripts/build-lpeg.sh" "${LPEG_SRC}/build"
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
#
|
||||
# Lpeg
|
||||
#
|
||||
option(USE_BUNDLED_LPEG "Enable building of the bundled lpeg" ${USE_BUNDLED_DEPS})
|
||||
|
||||
if(NOT USE_BUNDLED_LPEG)
|
||||
find_library(LPEG_LIB NAMES lpeg.a)
|
||||
if(LPEG_LIB)
|
||||
message(STATUS "Found lpeg: lib: ${LPEG_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system lpeg")
|
||||
endif()
|
||||
else()
|
||||
set(LPEG_SRC "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg")
|
||||
set(LPEG_LIB "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg/build/lpeg.a")
|
||||
ExternalProject_Add(lpeg
|
||||
DEPENDS luajit
|
||||
URL "http://s3.amazonaws.com/download.draios.com/dependencies/lpeg-1.0.0.tar.gz"
|
||||
URL_MD5 "0aec64ccd13996202ad0c099e2877ece"
|
||||
BUILD_COMMAND LUA_INCLUDE=${LUAJIT_INCLUDE} "${PROJECT_SOURCE_DIR}/scripts/build-lpeg.sh" "${LPEG_SRC}/build"
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
endif()
|
||||
|
||||
set (LIBYAML_SRC "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml/src")
|
||||
set(LIBYAML_LIB "${LIBYAML_SRC}/.libs/libyaml.a")
|
||||
ExternalProject_Add(libyaml
|
||||
URL "http://download.draios.com/dependencies/libyaml-0.1.4.tar.gz"
|
||||
URL_MD5 "4a4bced818da0b9ae7fc8ebc690792a7"
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./bootstrap && ./configure
|
||||
INSTALL_COMMAND "")
|
||||
#
|
||||
# Libyaml
|
||||
#
|
||||
option(USE_BUNDLED_LIBYAML "Enable building of the bundled libyaml" ${USE_BUNDLED_DEPS})
|
||||
|
||||
set (LYAML_SRC "${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/ext/yaml")
|
||||
set(LYAML_LIB "${LYAML_SRC}/.libs/yaml.a")
|
||||
ExternalProject_Add(lyaml
|
||||
URL "http://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-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")
|
||||
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
|
||||
# libraries being available.
|
||||
find_library(LIBYAML_LIB NAMES libyaml.a)
|
||||
if(LIBYAML_LIB)
|
||||
message(STATUS "Found libyaml: lib: ${LIBYAML_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system libyaml")
|
||||
endif()
|
||||
else()
|
||||
find_path(AUTORECONF_BIN NAMES autoreconf)
|
||||
if(AUTORECONF_BIN)
|
||||
message(STATUS "Found autoreconf: ${AUTORECONF_BIN}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system autoreconf. Please install autoreconf before continuing or use system libyaml")
|
||||
endif()
|
||||
|
||||
set(LIBYAML_SRC "${PROJECT_BINARY_DIR}/libyaml-prefix/src/libyaml/src")
|
||||
set(LIBYAML_LIB "${LIBYAML_SRC}/.libs/libyaml.a")
|
||||
ExternalProject_Add(libyaml
|
||||
URL "http://download.draios.com/dependencies/libyaml-0.1.4.tar.gz"
|
||||
URL_MD5 "4a4bced818da0b9ae7fc8ebc690792a7"
|
||||
BUILD_COMMAND ${CMD_MAKE}
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./bootstrap && ./configure
|
||||
INSTALL_COMMAND "")
|
||||
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
|
||||
# libraries being available.
|
||||
find_library(LYAML_LIB NAMES yaml.a)
|
||||
if(LYAML_LIB)
|
||||
message(STATUS "Found lyaml: lib: ${LYAML_LIB}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system lyaml")
|
||||
endif()
|
||||
else()
|
||||
set(LYAML_SRC "${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/ext/yaml")
|
||||
set(LYAML_LIB "${LYAML_SRC}/.libs/yaml.a")
|
||||
ExternalProject_Add(lyaml
|
||||
DEPENDS libyaml luajit
|
||||
URL "http://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-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()
|
||||
|
||||
install(FILES falco.yaml
|
||||
DESTINATION "${FALCO_ETC_DIR}")
|
||||
@@ -201,7 +376,7 @@ add_subdirectory("${SYSDIG_DIR}/userspace/libsinsp" "${PROJECT_BINARY_DIR}/users
|
||||
|
||||
add_subdirectory(scripts)
|
||||
set(FALCO_SINSP_LIBRARY sinsp)
|
||||
set(FALCO_SHARE_DIR share/falco)
|
||||
set(FALCO_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/falco)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
####Latest release
|
||||
|
||||
**v0.4.0**
|
||||
**v0.5.0**
|
||||
Read the [change log](https://github.com/draios/falco/blob/dev/CHANGELOG.md)
|
||||
|
||||
Dev Branch: [](https://travis-ci.org/draios/falco)<br />
|
||||
@@ -22,6 +22,11 @@ Falco can detect and alert on any behavior that involves making Linux system cal
|
||||
- A non-device file is written to `/dev`
|
||||
- A standard system binary (like `ls`) makes an outbound network connection
|
||||
|
||||
#### How Falco Compares to Other Security Tools like SELinux, Auditd, etc.
|
||||
|
||||
One of the questions we often get when we talk about Sysdig Falco is “How does it compare to other tools like SELinux, AppArmor, Auditd, etc. that also have security policies?”. We wrote a [blog post](https://sysdig.com/blog/selinux-seccomp-falco-technical-discussion/) comparing Falco to other tools.
|
||||
|
||||
|
||||
Documentation
|
||||
---
|
||||
[Visit the wiki] (https://github.com/draios/falco/wiki) for full documentation on falco.
|
||||
|
||||
@@ -26,10 +26,10 @@ RUN echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources
|
||||
gcc-5 \
|
||||
gcc-4.9 && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Terribly terrible hacks: since our base Debian image ships with GCC 5.0 which breaks older kernels,
|
||||
# revert the default to gcc-4.9. Also, since some customers use some very old distributions whose kernel
|
||||
# makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7 by symlinking
|
||||
# it to 4.9
|
||||
# Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the
|
||||
# default to gcc-4.9. Also, since some customers use some very old distributions whose kernel
|
||||
# makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7
|
||||
# by symlinking it to 4.9
|
||||
|
||||
RUN rm -rf /usr/bin/gcc \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
#!/bin/bash
|
||||
#set -e
|
||||
|
||||
echo "* Setting up /usr/src links from host"
|
||||
# Set the SYSDIG_SKIP_LOAD variable to skip loading the sysdig kernel module
|
||||
|
||||
for i in $(ls $SYSDIG_HOST_ROOT/usr/src)
|
||||
do
|
||||
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
|
||||
done
|
||||
if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then
|
||||
echo "* Setting up /usr/src links from host"
|
||||
|
||||
/usr/bin/sysdig-probe-loader
|
||||
for i in $(ls $SYSDIG_HOST_ROOT/usr/src)
|
||||
do
|
||||
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
|
||||
done
|
||||
|
||||
/usr/bin/sysdig-probe-loader
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -97,6 +97,8 @@ void exfiltration()
|
||||
|
||||
shadow.open("/etc/shadow");
|
||||
|
||||
printf("Reading /etc/shadow and sending to 10.5.2.6:8197...\n");
|
||||
|
||||
if(!shadow.is_open())
|
||||
{
|
||||
fprintf(stderr, "Could not open /etc/shadow for reading: %s", strerror(errno));
|
||||
@@ -219,7 +221,7 @@ void write_rpm_database() {
|
||||
}
|
||||
|
||||
void spawn_shell() {
|
||||
printf("Spawning a shell using system()...\n");
|
||||
printf("Spawning a shell to run \"ls > /dev/null\" using system()...\n");
|
||||
int rc;
|
||||
|
||||
if ((rc = system("ls > /dev/null")) != 0)
|
||||
@@ -259,6 +261,7 @@ void mkdir_binary_dirs() {
|
||||
|
||||
void change_thread_namespace() {
|
||||
printf("Calling setns() to change namespaces...\n");
|
||||
printf("NOTE: does not result in a falco notification in containers, unless container run with --privileged or --security-opt seccomp=unconfined\n");
|
||||
// It doesn't matter that the arguments to setns are
|
||||
// bogus. It's the attempt to call it that will trigger the
|
||||
// rule.
|
||||
@@ -268,6 +271,7 @@ void change_thread_namespace() {
|
||||
void system_user_interactive() {
|
||||
pid_t child;
|
||||
|
||||
printf("Forking a child that becomes user=daemon and then tries to run /bin/login...\n");
|
||||
// Fork a child and do everything in the child.
|
||||
if ((child = fork()) == 0)
|
||||
{
|
||||
@@ -313,6 +317,8 @@ void system_procs_network_activity() {
|
||||
void non_sudo_setuid() {
|
||||
pid_t child;
|
||||
|
||||
printf("Forking a child that becomes \"daemon\" user and then \"root\"...\n");
|
||||
|
||||
// Fork a child and do everything in the child.
|
||||
if ((child = fork()) == 0)
|
||||
{
|
||||
@@ -367,6 +373,9 @@ map<string, action_t> defined_actions = {{"write_binary_dir", write_binary_dir},
|
||||
{"user_mgmt_binaries", user_mgmt_binaries},
|
||||
{"exfiltration", exfiltration}};
|
||||
|
||||
// Some actions don't directly result in suspicious behavior. These
|
||||
// actions are excluded from the ones run with -a all.
|
||||
set<string> exclude_from_all_actions = {"exec_ls", "network_activity"};
|
||||
|
||||
void create_symlinks(const char *program)
|
||||
{
|
||||
@@ -394,9 +403,9 @@ void run_actions(map<string, action_t> &actions, int interval, bool once)
|
||||
{
|
||||
for (auto action : actions)
|
||||
{
|
||||
sleep(interval);
|
||||
printf("***Action %s\n", action.first.c_str());
|
||||
action.second();
|
||||
sleep(interval);
|
||||
}
|
||||
if(once)
|
||||
{
|
||||
@@ -428,7 +437,7 @@ int main(int argc, char **argv)
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"ha:i:l:",
|
||||
"ha:i:l:o",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
@@ -437,12 +446,16 @@ int main(int argc, char **argv)
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
case 'a':
|
||||
if((it = defined_actions.find(optarg)) == defined_actions.end())
|
||||
// "all" is already implied
|
||||
if (strcmp(optarg, "all") != 0)
|
||||
{
|
||||
fprintf(stderr, "No action with name \"%s\" known, exiting.\n", optarg);
|
||||
exit(1);
|
||||
if((it = defined_actions.find(optarg)) == defined_actions.end())
|
||||
{
|
||||
fprintf(stderr, "No action with name \"%s\" known, exiting.\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
actions.insert(*it);
|
||||
}
|
||||
actions.insert(*it);
|
||||
break;
|
||||
case 'i':
|
||||
interval = atoi(optarg);
|
||||
@@ -482,7 +495,13 @@ int main(int argc, char **argv)
|
||||
|
||||
if(actions.size() == 0)
|
||||
{
|
||||
actions = defined_actions;
|
||||
for(auto &act : defined_actions)
|
||||
{
|
||||
if(exclude_from_all_actions.find(act.first) == exclude_from_all_actions.end())
|
||||
{
|
||||
actions.insert(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
50
docker/local/Dockerfile
Normal file
50
docker/local/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
FROM debian:unstable
|
||||
|
||||
MAINTAINER Sysdig <support@sysdig.com>
|
||||
|
||||
ENV FALCO_VERSION 0.1.1dev
|
||||
|
||||
LABEL RUN="docker run -i -t -v /var/run/docker.sock:/host/var/run/docker.sock -v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro --name NAME IMAGE"
|
||||
|
||||
ENV SYSDIG_HOST_ROOT /host
|
||||
|
||||
ENV HOME /root
|
||||
|
||||
RUN cp /etc/skel/.bashrc /root && cp /etc/skel/.profile /root
|
||||
|
||||
ADD http://download.draios.com/apt-draios-priority /etc/apt/preferences.d/
|
||||
|
||||
RUN echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash-completion \
|
||||
curl \
|
||||
jq \
|
||||
gnupg2 \
|
||||
ca-certificates \
|
||||
gcc \
|
||||
gcc-5 \
|
||||
gcc-4.9 \
|
||||
sysdig && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the
|
||||
# default to gcc-4.9. Also, since some customers use some very old distributions whose kernel
|
||||
# makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7
|
||||
# by symlinking it to 4.9
|
||||
|
||||
RUN rm -rf /usr/bin/gcc \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.8 \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.7 \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc-4.6
|
||||
|
||||
RUN ln -s $SYSDIG_HOST_ROOT/lib/modules /lib/modules
|
||||
|
||||
ADD falco-${FALCO_VERSION}-x86_64.deb /
|
||||
RUN dpkg -i /falco-${FALCO_VERSION}-x86_64.deb
|
||||
|
||||
COPY ./docker-entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
CMD ["/usr/bin/falco"]
|
||||
17
docker/local/docker-entrypoint.sh
Executable file
17
docker/local/docker-entrypoint.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
#set -e
|
||||
|
||||
# Set the SYSDIG_SKIP_LOAD variable to skip loading the sysdig kernel module
|
||||
|
||||
if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then
|
||||
echo "* Setting up /usr/src links from host"
|
||||
|
||||
for i in $(ls $SYSDIG_HOST_ROOT/usr/src)
|
||||
do
|
||||
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
|
||||
done
|
||||
|
||||
/usr/bin/sysdig-probe-loader
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
@@ -26,10 +26,10 @@ RUN echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources
|
||||
gcc-5 \
|
||||
gcc-4.9 && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Terribly terrible hacks: since our base Debian image ships with GCC 5.0 which breaks older kernels,
|
||||
# revert the default to gcc-4.9. Also, since some customers use some very old distributions whose kernel
|
||||
# makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7 by symlinking
|
||||
# it to 4.9
|
||||
# Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the
|
||||
# default to gcc-4.9. Also, since some customers use some very old distributions whose kernel
|
||||
# makefile is hardcoded for gcc-4.6 or so (e.g. Debian Wheezy), we pretend to have gcc 4.6/4.7
|
||||
# by symlinking it to 4.9
|
||||
|
||||
RUN rm -rf /usr/bin/gcc \
|
||||
&& ln -s /usr/bin/gcc-4.9 /usr/bin/gcc \
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
#!/bin/bash
|
||||
#set -e
|
||||
|
||||
echo "* Setting up /usr/src links from host"
|
||||
# Set the SYSDIG_SKIP_LOAD variable to skip loading the sysdig kernel module
|
||||
|
||||
for i in $(ls $SYSDIG_HOST_ROOT/usr/src)
|
||||
do
|
||||
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
|
||||
done
|
||||
if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then
|
||||
echo "* Setting up /usr/src links from host"
|
||||
|
||||
/usr/bin/sysdig-probe-loader
|
||||
for i in $(ls $SYSDIG_HOST_ROOT/usr/src)
|
||||
do
|
||||
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
|
||||
done
|
||||
|
||||
/usr/bin/sysdig-probe-loader
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -20,5 +20,4 @@ falco:
|
||||
- /boot:/host/boot:ro
|
||||
- /lib/modules:/host/lib/modules:ro
|
||||
- /usr:/host/usr:ro
|
||||
- ${PWD}/../../rules/falco_rules.yaml:/etc/falco_rules.yaml
|
||||
tty: true
|
||||
|
||||
21
falco.yaml
21
falco.yaml
@@ -9,6 +9,27 @@ json_output: false
|
||||
log_stderr: true
|
||||
log_syslog: true
|
||||
|
||||
# Minimum log level to include in logs. Note: these levels are
|
||||
# separate from the priority field of rules. This refers only to the
|
||||
# log level of falco's internal logging. Can be one of "emergency",
|
||||
# "alert", "critical", "error", "warning", "notice", "info", "debug".
|
||||
log_level: info
|
||||
|
||||
# A throttling mechanism implemented as a token bucket limits the
|
||||
# rate of falco notifications. This throttling is controlled by the following configuration
|
||||
# options:
|
||||
# - rate: the number of tokens (i.e. right to send a notification)
|
||||
# gained per second. Defaults to 1.
|
||||
# - max_burst: the maximum number of tokens outstanding. Defaults to 1000.
|
||||
#
|
||||
# With these defaults, falco could send up to 1000 notifications after
|
||||
# an initial quiet period, and then up to 1 notification per second
|
||||
# afterward. It would gain the full burst back after 1000 seconds of
|
||||
# no activity.
|
||||
|
||||
outputs:
|
||||
rate: 1
|
||||
max_burst: 1000
|
||||
|
||||
# Where security notifications should go.
|
||||
# Multiple outputs can be enabled.
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
# dpkg -L login | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
||||
- list: login_binaries
|
||||
items: [login, systemd-logind, su, nologin, faillog, lastlog, newgrp, sg]
|
||||
items: [login, systemd, systemd-logind, su, nologin, faillog, lastlog, newgrp, sg]
|
||||
|
||||
# dpkg -L passwd | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
|
||||
- list: passwd_binaries
|
||||
@@ -99,10 +99,13 @@
|
||||
items: [setup-backend, dragent, sdchecks]
|
||||
|
||||
- list: docker_binaries
|
||||
items: [docker, dockerd, exe]
|
||||
items: [docker, dockerd, exe, docker-compose]
|
||||
|
||||
- list: k8s_binaries
|
||||
items: [hyperkube, skydns, kube2sky]
|
||||
items: [hyperkube, skydns, kube2sky, exechealthz]
|
||||
|
||||
- list: lxd_binaries
|
||||
items: [lxd, lxcfs]
|
||||
|
||||
- list: http_server_binaries
|
||||
items: [nginx, httpd, httpd-foregroun, lighttpd]
|
||||
@@ -116,7 +119,11 @@
|
||||
# The truncated dpkg-preconfigu is intentional, process names are
|
||||
# truncated at the sysdig level.
|
||||
- list: package_mgmt_binaries
|
||||
items: [dpkg, dpkg-preconfigu, rpm, rpmkey, yum, frontend]
|
||||
items: [
|
||||
dpkg, dpkg-preconfigu, dnf, rpm, rpmkey, yum, frontend,
|
||||
apt, apt-get, aptitude, add-apt-reposit, apt-auto-remova, apt-key,
|
||||
preinst, update-alternat, unattended-upgr
|
||||
]
|
||||
|
||||
- macro: package_mgmt_procs
|
||||
condition: proc.name in (package_mgmt_binaries)
|
||||
@@ -135,11 +142,26 @@
|
||||
- list: user_mgmt_binaries
|
||||
items: [login_binaries, passwd_binaries, shadowutils_binaries]
|
||||
|
||||
- list: dev_creation_binaries
|
||||
items: [blkid]
|
||||
|
||||
- list: aide_wrapper_binaries
|
||||
items: [aide.wrapper, update-aide.con]
|
||||
|
||||
- list: hids_binaries
|
||||
items: [aide]
|
||||
|
||||
- list: nids_binaries
|
||||
items: [bro, broctl]
|
||||
|
||||
- list: monitoring_binaries
|
||||
items: [icinga2, nrpe, npcd, check_sar_perf.]
|
||||
|
||||
- macro: system_procs
|
||||
condition: proc.name in (coreutils_binaries, user_mgmt_binaries)
|
||||
|
||||
- list: mail_binaries
|
||||
items: [sendmail, sendmail-msp, postfix, procmail, exim4]
|
||||
items: [sendmail, sendmail-msp, postfix, procmail, exim4, pickup, showq]
|
||||
|
||||
- macro: sensitive_files
|
||||
condition: fd.name startswith /etc and (fd.name in (/etc/shadow, /etc/sudoers, /etc/pam.conf) or fd.directory in (/etc/sudoers.d, /etc/pam.d))
|
||||
@@ -190,6 +212,31 @@
|
||||
- macro: system_users
|
||||
condition: user.name in (bin, daemon, games, lp, mail, nobody, sshd, sync, uucp, www-data)
|
||||
|
||||
# SPECIAL NOTE: This macro eliminates false positives that result from
|
||||
# running python scripts as a part of ansible. However, the condition
|
||||
# that the command line contains "ansible" is very
|
||||
# permissive. Ideally, you should change this macro to explicitly
|
||||
# scope the python scripts to a specific directory (namely, your
|
||||
# configured remote_tmp directory).
|
||||
- macro: parent_ansible_running_python
|
||||
condition: (proc.pname in (python, pypy) and proc.pcmdline contains ansible)
|
||||
|
||||
- macro: ansible_running_python
|
||||
condition: (proc.name in (python, pypy) and proc.cmdline contains ansible)
|
||||
|
||||
- macro: python_running_denyhosts
|
||||
condition: (proc.name=python and (proc.cmdline contains /usr/sbin/denyhosts or proc.cmdline contains /usr/local/bin/denyhosts.py))
|
||||
|
||||
- macro: parent_python_running_denyhosts
|
||||
condition: (proc.pname=python and (proc.pcmdline contains /usr/sbin/denyhosts or proc.pcmdline contains /usr/local/bin/denyhosts.py))
|
||||
|
||||
- macro: parent_bro_running_python
|
||||
condition: (proc.pname=python and proc.cmdline contains /usr/share/broctl)
|
||||
|
||||
# As a part of kernel upgrades, dpkg will spawn a perl script with the
|
||||
# name linux-image-N.N. This macro matches that.
|
||||
- macro: parent_linux_image_upgrade_script
|
||||
condition: proc.pname startswith linux-image-
|
||||
|
||||
###############
|
||||
# General Rules
|
||||
@@ -204,9 +251,15 @@
|
||||
- macro: write_etc_common
|
||||
condition: >
|
||||
etc_dir and evt.dir = < and open_write
|
||||
and not proc.name in (shadowutils_binaries, sysdigcloud_binaries, package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries, ldconfig.real, ldconfig)
|
||||
and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries,
|
||||
package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries,
|
||||
ldconfig.real, ldconfig, confd, gpg, insserv,
|
||||
apparmor_parser, update-mime, tzdata.config, tzdata.postinst,
|
||||
systemd-machine, debconf-show, rollerd, bind9.postinst)
|
||||
and not proc.pname in (sysdigcloud_binaries)
|
||||
and not fd.directory in (/etc/cassandra, /etc/ssl/certs/java)
|
||||
and not ansible_running_python
|
||||
and not python_running_denyhosts
|
||||
|
||||
- rule: Write below etc
|
||||
desc: an attempt to write to any file below /etc, not in a pipe installer session
|
||||
@@ -221,22 +274,33 @@
|
||||
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name) within pipe installer session"
|
||||
priority: INFO
|
||||
|
||||
- macro: cmp_cp_by_passwd
|
||||
condition: proc.name in (cmp, cp) and proc.pname=passwd
|
||||
|
||||
- rule: Read sensitive file trusted after startup
|
||||
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information) by a trusted program after startup. Trusted programs might read these files at startup to load initial state, but not afterwards.
|
||||
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 file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
- list: read_sensitive_file_binaries
|
||||
items: [iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, sshd, vsftpd, systemd]
|
||||
|
||||
- rule: Read sensitive file untrusted
|
||||
desc: an attempt to read any sensitive file (e.g. files containing user/password/authentication information). Exceptions are made for known trusted programs.
|
||||
condition: sensitive_files and open_read and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, cron_binaries, iptables, ps, lsb_release, check-new-relea, dumpe2fs, accounts-daemon, shell_binaries, sshd) and not proc.cmdline contains /usr/bin/mandb
|
||||
condition: >
|
||||
sensitive_files and open_read
|
||||
and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries)
|
||||
and not cmp_cp_by_passwd
|
||||
and not ansible_running_python
|
||||
and not proc.cmdline contains /usr/bin/mandb
|
||||
output: "Sensitive file opened for reading by non-trusted program (user=%user.name name=%proc.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
# Only let rpm-related programs write to the rpm database
|
||||
- rule: Write below rpm database
|
||||
desc: an attempt to write to the rpm database by any non-rpm related program
|
||||
condition: fd.name startswith /var/lib/rpm and open_write and not proc.name in (rpm,rpmkey,yum)
|
||||
condition: fd.name startswith /var/lib/rpm and open_write and not proc.name in (dnf,rpm,rpmkey,yum) and not ansible_running_python
|
||||
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
@@ -276,23 +340,53 @@
|
||||
|
||||
- rule: Change thread namespace
|
||||
desc: an attempt to change a program/thread\'s namespace (commonly done as a part of creating a container) by calling setns.
|
||||
condition: evt.type = setns and not proc.name in (docker_binaries, k8s_binaries, sysdigcloud_binaries, sysdig, nsenter) and not proc.pname in (sysdigcloud_binaries)
|
||||
condition: >
|
||||
evt.type = setns
|
||||
and not proc.name in (docker_binaries, k8s_binaries, lxd_binaries, sysdigcloud_binaries, sysdig, nsenter)
|
||||
and not proc.pname in (sysdigcloud_binaries)
|
||||
output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline parent=%proc.pname %container.info)"
|
||||
priority: WARNING
|
||||
|
||||
- list: known_shell_spawn_binaries
|
||||
items: [
|
||||
sshd, sudo, su, tmux, screen, emacs, systemd, login, flock, fbash,
|
||||
nginx, monit, supervisord, dragent, aws, initdb, docker-compose,
|
||||
make, configure, awk, falco, fail2ban-server, fleetctl,
|
||||
logrotate, ansible, less, adduser, pycompile, py3compile,
|
||||
pyclean, py3clean, pip, pip2, ansible-playboo, man-db,
|
||||
init, pluto, mkinitramfs, unattended-upgr, watch, sysdig,
|
||||
landscape-sysin, nessusd, PM2, syslog-summary
|
||||
]
|
||||
|
||||
- rule: Run shell untrusted
|
||||
desc: an attempt to spawn a shell by a non-shell program. Exceptions are made for trusted binaries.
|
||||
condition: spawned_process and not container and shell_procs and proc.pname exists and not proc.pname in (cron_binaries, shell_binaries, sshd, sudo, docker_binaries, k8s_binaries, su, tmux, screen, emacs, systemd, login, flock, fbash, nginx, monit, supervisord, dragent, aws, initdb, docker-compose, make, configure, awk, falco)
|
||||
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
|
||||
condition: >
|
||||
spawned_process and not container
|
||||
and shell_procs
|
||||
and proc.pname exists
|
||||
and not proc.pname in (cron_binaries, shell_binaries, known_shell_spawn_binaries, docker_binaries,
|
||||
k8s_binaries, package_mgmt_binaries, aide_wrapper_binaries, nids_binaries,
|
||||
monitoring_binaries)
|
||||
and not parent_ansible_running_python
|
||||
and not parent_bro_running_python
|
||||
and not parent_python_running_denyhosts
|
||||
and not parent_linux_image_upgrade_script
|
||||
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline pcmdline=%proc.pcmdline)"
|
||||
priority: WARNING
|
||||
|
||||
- macro: trusted_containers
|
||||
condition: (container.image startswith sysdig/agent or container.image startswith sysdig/falco or container.image startswith sysdig/sysdig or container.image startswith gcr.io/google_containers/hyperkube)
|
||||
condition: (container.image startswith sysdig/agent or
|
||||
(container.image startswith sysdig/falco and
|
||||
not container.image startswith sysdig/falco-event-generator) or
|
||||
container.image startswith sysdig/sysdig or
|
||||
container.image startswith gcr.io/google_containers/hyperkube or
|
||||
container.image startswith quay.io/coreos/flannel or
|
||||
container.image startswith gcr.io/google_containers/kube-proxy)
|
||||
|
||||
- rule: File Open by Privileged Container
|
||||
desc: Any open by a privileged container. Exceptions are made for known trusted images.
|
||||
condition: (open_read or open_write) and container and container.privileged=true and not trusted_containers
|
||||
output: File opened for read/write by non-privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
|
||||
output: File opened for read/write by privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
|
||||
priority: WARNING
|
||||
|
||||
- macro: sensitive_mount
|
||||
@@ -317,7 +411,13 @@
|
||||
|
||||
- rule: Run shell in container
|
||||
desc: a shell was spawned by a non-shell program in a container. Container entrypoints are excluded.
|
||||
condition: spawned_process and container and shell_procs and proc.pname exists and not proc.pname in (shell_binaries, docker_binaries, k8s_binaries, initdb, pg_ctl, awk, apache2, falco, cron)
|
||||
condition: >
|
||||
spawned_process and container
|
||||
and shell_procs
|
||||
and proc.pname exists
|
||||
and not proc.pname in (shell_binaries, docker_binaries, k8s_binaries, lxd_binaries, aide_wrapper_binaries, nids_binaries,
|
||||
monitoring_binaries, initdb, pg_ctl, awk, apache2, falco, cron)
|
||||
and not trusted_containers
|
||||
output: "Shell spawned in a container other than entrypoint (user=%user.name %container.info shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
|
||||
@@ -340,8 +440,8 @@
|
||||
# sshd, mail programs attempt to setuid to root even when running as non-root. Excluding here to avoid meaningless FPs
|
||||
- rule: Non sudo setuid
|
||||
desc: an attempt to change users by calling setuid. sudo/su are excluded. user "root" is also excluded, as setuid calls typically involve dropping privileges.
|
||||
condition: evt.type=setuid and evt.dir=> and not user.name=root and not proc.name in (userexec_binaries, mail_binaries, sshd, dbus-daemon-lau)
|
||||
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name command=%proc.cmdline uid=%evt.arg.uid)"
|
||||
condition: evt.type=setuid and evt.dir=> and not user.name=root and not proc.name in (userexec_binaries, mail_binaries, sshd, dbus-daemon-lau, ping, ping6, critical-stack-)
|
||||
output: "Unexpected setuid call by non-sudo, non-root program (user=%user.name parent=%proc.pname command=%proc.cmdline uid=%evt.arg.uid)"
|
||||
priority: WARNING
|
||||
|
||||
- rule: User mgmt binaries
|
||||
@@ -350,10 +450,17 @@
|
||||
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
|
||||
priority: WARNING
|
||||
|
||||
- list: allowed_dev_files
|
||||
items: [/dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /dev/tty, /dev/random, /dev/urandom, /dev/console]
|
||||
|
||||
# (we may need to add additional checks against false positives, see: https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
|
||||
- rule: Create files below dev
|
||||
desc: creating any files below /dev other than known programs that manage devices. Some rootkits hide files in /dev.
|
||||
condition: fd.directory = /dev and (evt.type = creat or (evt.type = open and evt.arg.flags contains O_CREAT)) and proc.name != blkid and not fd.name in (/dev/null,/dev/stdin,/dev/stdout,/dev/stderr,/dev/tty)
|
||||
condition: >
|
||||
fd.directory = /dev and
|
||||
(evt.type = creat or (evt.type = open and evt.arg.flags contains O_CREAT))
|
||||
and not proc.name in (dev_creation_binaries)
|
||||
and not fd.name in (allowed_dev_files)
|
||||
output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)"
|
||||
priority: WARNING
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ VARIANT=$3
|
||||
RESULTS_FILE=$4
|
||||
CPU_INTERVAL=$5
|
||||
|
||||
top -d $CPU_INTERVAL -b -p $SUBJ_PID | grep -E '(falco|sysdig|dragent)' --line-buffered | awk -v benchmark=$BENCHMARK -v variant=$VARIANT '{printf("{\"time\": \"%s\", \"sample\": %d, \"benchmark\": \"%s\", \"variant\": \"%s\", \"cpu_usage\": %s},\n", strftime("%Y-%m-%d %H:%M:%S", systime(), 1), NR, benchmark, variant, $9); fflush();}' >> $RESULTS_FILE
|
||||
top -d $CPU_INTERVAL -b -p $SUBJ_PID | grep -E '(falco|sysdig|dragent|test_mm)' --line-buffered | awk -v benchmark=$BENCHMARK -v variant=$VARIANT '{printf("{\"time\": \"%s\", \"sample\": %d, \"benchmark\": \"%s\", \"variant\": \"%s\", \"cpu_usage\": %s},\n", strftime("%Y-%m-%d %H:%M:%S", systime(), 1), NR, benchmark, variant, $9); fflush();}' >> $RESULTS_FILE
|
||||
|
||||
@@ -17,6 +17,9 @@ class FalcoTest(Test):
|
||||
"""
|
||||
self.falcodir = self.params.get('falcodir', '/', default=os.path.join(self.basedir, '../build'))
|
||||
|
||||
self.stdout_contains = self.params.get('stdout_contains', '*', default='')
|
||||
self.stderr_contains = self.params.get('stderr_contains', '*', default='')
|
||||
self.exit_status = self.params.get('exit_status', '*', default=0)
|
||||
self.should_detect = self.params.get('detect', '*', default=False)
|
||||
self.trace_file = self.params.get('trace_file', '*')
|
||||
|
||||
@@ -53,6 +56,16 @@ class FalcoTest(Test):
|
||||
for rule in self.disabled_rules:
|
||||
self.disabled_args = self.disabled_args + "-D " + rule + " "
|
||||
|
||||
self.detect_counts = self.params.get('detect_counts', '*', default=False)
|
||||
if self.detect_counts == False:
|
||||
self.detect_counts = {}
|
||||
else:
|
||||
detect_counts = {}
|
||||
for item in self.detect_counts:
|
||||
for item2 in item:
|
||||
detect_counts[item2[0]] = item2[1]
|
||||
self.detect_counts = detect_counts
|
||||
|
||||
self.rules_warning = self.params.get('rules_warning', '*', default=False)
|
||||
if self.rules_warning == False:
|
||||
self.rules_warning = sets.Set()
|
||||
@@ -158,6 +171,23 @@ class FalcoTest(Test):
|
||||
if not events_detected > 0:
|
||||
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, level))
|
||||
|
||||
def check_detections_by_rule(self, res):
|
||||
# Get the number of events detected for each rule. Must match the expected counts.
|
||||
match = re.search('Triggered rules by rule name:(.*)', res.stdout, re.DOTALL)
|
||||
if match is None:
|
||||
self.fail("Could not find a block 'Triggered rules by rule name: ...' in falco output")
|
||||
|
||||
triggered_rules = match.group(1)
|
||||
|
||||
for rule, count in self.detect_counts.iteritems():
|
||||
expected_line = '{}: {}'.format(rule, count)
|
||||
match = re.search(expected_line, triggered_rules)
|
||||
|
||||
if match is None:
|
||||
self.fail("Could not find a line '{}' in triggered rule counts '{}'".format(expected_line, triggered_rules))
|
||||
else:
|
||||
self.log.debug("Found expected count for {}: {}".format(rule, match.group()))
|
||||
|
||||
def check_outputs(self):
|
||||
for output in self.outputs:
|
||||
# Open the provided file and match each line against the
|
||||
@@ -197,14 +227,30 @@ class FalcoTest(Test):
|
||||
|
||||
res = self.falco_proc.run(timeout=180, sig=9)
|
||||
|
||||
if self.stderr_contains != '':
|
||||
match = re.search(self.stderr_contains, res.stderr)
|
||||
if match is None:
|
||||
self.fail("Stderr of falco process did not contain content matching {}".format(self.stderr_contains))
|
||||
|
||||
if self.stdout_contains != '':
|
||||
match = re.search(self.stdout_contains, res.stdout)
|
||||
if match is None:
|
||||
self.fail("Stdout of falco process '{}' did not contain content matching {}".format(res.stdout, self.stdout_contains))
|
||||
|
||||
if res.exit_status != self.exit_status:
|
||||
self.error("Falco command \"{}\" exited with unexpected return value {} (!= {})".format(
|
||||
cmd, res.exit_status, self.exit_status))
|
||||
|
||||
# No need to check any outputs if the falco process exited abnormally.
|
||||
if res.exit_status != 0:
|
||||
self.error("Falco command \"{}\" exited with non-zero return value {}".format(
|
||||
cmd, res.exit_status))
|
||||
return
|
||||
|
||||
self.check_rules_warnings(res)
|
||||
if len(self.rules_events) > 0:
|
||||
self.check_rules_events(res)
|
||||
self.check_detections(res)
|
||||
if len(self.detect_counts) > 0:
|
||||
self.check_detections_by_rule(res)
|
||||
self.check_json_output(res)
|
||||
self.check_outputs()
|
||||
pass
|
||||
|
||||
@@ -95,6 +95,41 @@ trace_files: !mux
|
||||
- rules/double_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
multiple_rules_overriding:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/override_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
macro_overriding:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/override_macro.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
list_overriding:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/override_list.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
nested_list_overriding:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/override_nested_list.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."
|
||||
rules_file:
|
||||
- rules/invalid_rule_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
disabled_rules:
|
||||
detect: False
|
||||
rules_file:
|
||||
@@ -119,6 +154,14 @@ trace_files: !mux
|
||||
- rules/single_rule_enabled_flag.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
null_output_field:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/null_output_field.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
stdout_contains: "Warning An open was seen .cport=<NA> command=cat /dev/null."
|
||||
|
||||
file_output:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
@@ -138,3 +181,22 @@ trace_files: !mux
|
||||
trace_file: trace_files/cat_write.scap
|
||||
outputs:
|
||||
- /tmp/falco_outputs/program_output.txt: Warning An open was seen
|
||||
|
||||
detect_counts:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
trace_file: traces-positive/falco-event-generator.scap
|
||||
detect_counts:
|
||||
- "Write below binary dir": 1
|
||||
- "Read sensitive file untrusted": 3
|
||||
- "Run shell in container": 1
|
||||
- "Write below rpm database": 1
|
||||
- "Write below etc": 1
|
||||
- "System procs network activity": 1
|
||||
- "Mkdir binary dirs": 1
|
||||
- "System user interactive": 1
|
||||
- "DB program spawned process": 1
|
||||
- "Non sudo setuid": 1
|
||||
- "Create files below dev": 1
|
||||
- "Modify binary dirs": 2
|
||||
- "Change thread namespace": 2
|
||||
|
||||
@@ -13,23 +13,35 @@ if (substr(script.basename, 1, 1) != '/') {
|
||||
|
||||
results = paste(script.basename, "results.json", sep='/')
|
||||
output = "./output.png"
|
||||
metric = "cpu"
|
||||
|
||||
GetoptLong(
|
||||
"results=s", "Path to results file",
|
||||
"benchmark=s", "Benchmark from results file to graph",
|
||||
"variant=s@", "Variant(s) to include in graph. Can be specified multiple times",
|
||||
"output=s", "Output graph file"
|
||||
"output=s", "Output graph file",
|
||||
"metric=s", "Metric to graph. Can be one of (cpu|drops)"
|
||||
)
|
||||
|
||||
if (metric == "cpu") {
|
||||
data_metric="cpu_usage"
|
||||
yaxis_label="CPU Usage (%)"
|
||||
title="Falco/Sysdig/Multimatch CPU Usage: %s"
|
||||
} else if (metric == "drops") {
|
||||
data_metric="drop_pct"
|
||||
yaxis_label="Event Drops (%)"
|
||||
title="Falco/Sysdig/Multimatch Event Drops: %s"
|
||||
}
|
||||
|
||||
res <- fromJSON(results, flatten=TRUE)
|
||||
|
||||
res2 = res[res$benchmark == benchmark & res$variant %in% variant,]
|
||||
|
||||
plot <- ggplot(data=res2, aes(x=sample, y=cpu_usage, group=variant, colour=variant)) +
|
||||
plot <- ggplot(data=res2, aes(x=sample, y=get(data_metric), group=variant, colour=variant)) +
|
||||
geom_line() +
|
||||
ylab("CPU Usage (%)") +
|
||||
ylab(yaxis_label) +
|
||||
xlab("Time") +
|
||||
ggtitle(sprintf("Falco/Sysdig CPU Usage: %s", benchmark))
|
||||
ggtitle(sprintf(title, benchmark))
|
||||
theme(legend.position=c(.2, .88));
|
||||
|
||||
print(paste("Writing graph to", output, sep=" "))
|
||||
|
||||
5
test/rules/invalid_rule_output.yaml
Normal file
5
test/rules/invalid_rule_output.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
- rule: rule_with_invalid_output
|
||||
desc: A rule with an invalid output field
|
||||
condition: evt.type=open
|
||||
output: "An open was seen %not_a_real_field"
|
||||
priority: WARNING
|
||||
5
test/rules/null_output_field.yaml
Normal file
5
test/rules/null_output_field.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
- rule: open_from_cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=cat
|
||||
output: "An open was seen (cport=%fd.cport command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
2
test/rules/override_list.yaml
Normal file
2
test/rules/override_list.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- list: cat_capable_binaries
|
||||
items: [not-cat]
|
||||
2
test/rules/override_macro.yaml
Normal file
2
test/rules/override_macro.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- macro: is_cat
|
||||
condition: proc.name in (not-cat)
|
||||
2
test/rules/override_nested_list.yaml
Normal file
2
test/rules/override_nested_list.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- list: cat_binaries
|
||||
items: [not-cat]
|
||||
5
test/rules/override_rule.yaml
Normal file
5
test/rules/override_rule.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
- rule: open_from_cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and proc.name=not-cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
@@ -1,5 +1,11 @@
|
||||
- list: cat_binaries
|
||||
items: [cat]
|
||||
|
||||
- list: cat_capable_binaries
|
||||
items: [cat_binaries]
|
||||
|
||||
- macro: is_cat
|
||||
condition: proc.name=cat
|
||||
condition: proc.name in (cat_capable_binaries)
|
||||
|
||||
- rule: open_from_cat
|
||||
desc: A process named cat does an open
|
||||
|
||||
@@ -28,7 +28,11 @@ function time_cmd() {
|
||||
function run_falco_on() {
|
||||
file="$1"
|
||||
|
||||
cmd="$ROOT/userspace/falco/falco -c $ROOT/../falco.yaml -r $ROOT/../rules/falco_rules.yaml --option=stdout_output.enabled=false -e $file"
|
||||
if [ -z $RULES_FILE ]; then
|
||||
RULES_FILE=$SOURCE/rules/falco_rules.yaml
|
||||
fi
|
||||
|
||||
cmd="$ROOT/userspace/falco/falco -c $SOURCE/falco.yaml -r $SOURCE/rules/falco_rules.yaml --option=stdout_output.enabled=false -e $file -A"
|
||||
|
||||
time_cmd "$cmd" "$file"
|
||||
}
|
||||
@@ -131,15 +135,26 @@ function start_monitor_cpu_usage() {
|
||||
|
||||
function start_subject_prog() {
|
||||
|
||||
echo " starting falco/sysdig/agent program"
|
||||
# Do a blocking sudo command now just to ensure we have a password
|
||||
sudo bash -c ""
|
||||
|
||||
if [[ $ROOT == *"falco"* ]]; then
|
||||
sudo $ROOT/userspace/falco/falco -c $ROOT/../falco.yaml -r $ROOT/../rules/falco_rules.yaml --option=stdout_output.enabled=false > ./prog-output.txt 2>&1 &
|
||||
if [[ $ROOT == *"multimatch"* ]]; then
|
||||
echo " starting test_mm..."
|
||||
if [ -z $RULES_FILE ]; then
|
||||
RULES_FILE=$SOURCE/../output/rules.yaml
|
||||
fi
|
||||
sudo FALCO_STATS_EXTRA_variant=$VARIANT FALCO_STATS_EXTRA_benchmark=$live_test $ROOT/test_mm -S $SOURCE/search_order.yaml -s $STATS_FILE -r $RULES_FILE > ./prog-output.txt 2>&1 &
|
||||
elif [[ $ROOT == *"falco"* ]]; then
|
||||
echo " starting falco..."
|
||||
if [ -z $RULES_FILE ]; then
|
||||
RULES_FILE=$SOURCE/rules/falco_rules.yaml
|
||||
fi
|
||||
sudo FALCO_STATS_EXTRA_variant=$VARIANT FALCO_STATS_EXTRA_benchmark=$live_test $ROOT/userspace/falco/falco -c $SOURCE/falco.yaml -s $STATS_FILE -r $RULES_FILE --option=stdout_output.enabled=false > ./prog-output.txt -A 2>&1 &
|
||||
elif [[ $ROOT == *"sysdig"* ]]; then
|
||||
echo " starting sysdig..."
|
||||
sudo $ROOT/userspace/sysdig/sysdig -N -z evt.type=none &
|
||||
else
|
||||
echo " starting agent..."
|
||||
write_agent_config
|
||||
pushd $ROOT/userspace/dragent
|
||||
sudo ./dragent > ./prog-output.txt 2>&1 &
|
||||
@@ -180,8 +195,8 @@ function run_juttle_examples() {
|
||||
|
||||
function run_kubernetes_demo() {
|
||||
pushd $SCRIPTDIR/../../infrastructure/test-infrastructures/kubernetes-demo
|
||||
bash run-local.sh
|
||||
bash init.sh
|
||||
sudo bash run-local.sh
|
||||
sudo bash init.sh
|
||||
sleep 600
|
||||
docker stop $(docker ps -qa)
|
||||
docker rm -fv $(docker ps -qa)
|
||||
@@ -306,8 +321,11 @@ usage() {
|
||||
echo " -h/--help: show this help"
|
||||
echo " -v/--variant: a variant name to attach to this set of test results"
|
||||
echo " -r/--root: root directory containing falco/sysdig binaries (i.e. where you ran 'cmake')"
|
||||
echo " -s/--source: root directory containing falco/sysdig source code"
|
||||
echo " -R/--results: append test results to this file"
|
||||
echo " -S/--stats: append capture statistics to this file (only works for falco/test_mm)"
|
||||
echo " -o/--output: append program output to this file"
|
||||
echo " -U/--rules: path to rules file (only applicable for falco/test_mm)"
|
||||
echo " -t/--test: test to run. Argument has the following format:"
|
||||
echo " trace:<trace>: read the specified trace file."
|
||||
echo " trace:all means run all traces"
|
||||
@@ -325,7 +343,7 @@ usage() {
|
||||
echo " -F/--falco-agent: When running an agent, whether or not to enable falco"
|
||||
}
|
||||
|
||||
OPTS=`getopt -o hv:r:R:o:t:T: --long help,variant:,root:,results:,output:,test:,tracedir:,agent-autodrop:,falco-agent: -n $0 -- "$@"`
|
||||
OPTS=`getopt -o hv:r:s:R:S:o:U:t:T: --long help,variant:,root:,source:,results:,stats:,output:,rules:,test:,tracedir:,agent-autodrop:,falco-agent: -n $0 -- "$@"`
|
||||
|
||||
if [ $? != 0 ]; then
|
||||
echo "Exiting" >&2
|
||||
@@ -336,9 +354,12 @@ eval set -- "$OPTS"
|
||||
|
||||
VARIANT="falco"
|
||||
ROOT=`dirname $0`/../build
|
||||
SOURCE=$ROOT
|
||||
SCRIPTDIR=`dirname $0`
|
||||
RESULTS_FILE=`dirname $0`/results.json
|
||||
STATS_FILE=`dirname $0`/capture_stats.json
|
||||
OUTPUT_FILE=`dirname $0`/program-output.txt
|
||||
RULES_FILE=
|
||||
TEST=trace:all
|
||||
TRACEDIR=/tmp/falco-perf-traces.$USER
|
||||
CPU_INTERVAL=10
|
||||
@@ -350,8 +371,11 @@ while true; do
|
||||
-h | --help ) usage; exit 1;;
|
||||
-v | --variant ) VARIANT="$2"; shift 2;;
|
||||
-r | --root ) ROOT="$2"; shift 2;;
|
||||
-s | --source ) SOURCE="$2"; shift 2;;
|
||||
-R | --results ) RESULTS_FILE="$2"; shift 2;;
|
||||
-S | --stats ) STATS_FILE="$2"; shift 2;;
|
||||
-o | --output ) OUTPUT_FILE="$2"; shift 2;;
|
||||
-U | --rules ) RULES_FILE="$2"; shift 2;;
|
||||
-t | --test ) TEST="$2"; shift 2;;
|
||||
-T | --tracedir ) TRACEDIR="$2"; shift 2;;
|
||||
-A | --agent-autodrop ) AGENT_AUTODROP="$2"; shift 2;;
|
||||
@@ -372,12 +396,23 @@ fi
|
||||
|
||||
ROOT=`realpath $ROOT`
|
||||
|
||||
if [ -z $SOURCE ]; then
|
||||
echo "A source directory containing falco/sysdig source code. Not continuing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOURCE=`realpath $SOURCE`
|
||||
|
||||
if [ -z $RESULTS_FILE ]; then
|
||||
echo "An output file for test results must be provided. Not continuing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $STATS_FILE ]; then
|
||||
echo "An output file for capture statistics must be provided. Not continuing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $OUTPUT_FILE ]; then
|
||||
echo "An file for program output must be provided. Not continuing."
|
||||
exit 1
|
||||
|
||||
@@ -4,7 +4,7 @@ include_directories("${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp")
|
||||
include_directories("${PROJECT_BINARY_DIR}/userspace/engine")
|
||||
include_directories("${LUAJIT_INCLUDE}")
|
||||
|
||||
add_library(falco_engine STATIC rules.cpp falco_common.cpp falco_engine.cpp)
|
||||
add_library(falco_engine STATIC rules.cpp falco_common.cpp falco_engine.cpp token_bucket.cpp formats.cpp)
|
||||
|
||||
target_include_directories(falco_engine PUBLIC
|
||||
"${LUAJIT_INCLUDE}")
|
||||
|
||||
@@ -18,5 +18,5 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FALCO_ENGINE_LUA_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}/lua/"
|
||||
#define FALCO_ENGINE_LUA_DIR "${FALCO_SHARE_DIR}/lua/"
|
||||
#define FALCO_ENGINE_SOURCE_LUA_DIR "${PROJECT_SOURCE_DIR}/../falco/userspace/engine/lua/"
|
||||
|
||||
@@ -24,6 +24,8 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "falco_engine.h"
|
||||
#include "config_falco_engine.h"
|
||||
|
||||
#include "formats.h"
|
||||
|
||||
extern "C" {
|
||||
#include "lpeg.h"
|
||||
#include "lyaml.h"
|
||||
@@ -38,7 +40,8 @@ string lua_print_stats = "print_stats";
|
||||
using namespace std;
|
||||
|
||||
falco_engine::falco_engine(bool seed_rng)
|
||||
: m_rules(NULL), m_sampling_ratio(1), m_sampling_multiplier(0)
|
||||
: m_rules(NULL), m_sampling_ratio(1), m_sampling_multiplier(0),
|
||||
m_replace_container_info(false)
|
||||
{
|
||||
luaopen_lpeg(m_ls);
|
||||
luaopen_yaml(m_ls);
|
||||
@@ -46,6 +49,8 @@ falco_engine::falco_engine(bool seed_rng)
|
||||
falco_common::init(m_lua_main_filename.c_str(), FALCO_ENGINE_SOURCE_LUA_DIR);
|
||||
falco_rules::init(m_ls);
|
||||
|
||||
m_evttype_filter.reset(new sinsp_evttype_filter());
|
||||
|
||||
if(seed_rng)
|
||||
{
|
||||
srandom((unsigned) getpid());
|
||||
@@ -72,7 +77,16 @@ void falco_engine::load_rules(const string &rules_content, bool verbose, bool al
|
||||
{
|
||||
m_rules = new falco_rules(m_inspector, this, m_ls);
|
||||
}
|
||||
m_rules->load_rules(rules_content, verbose, all_events);
|
||||
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
// by the falco engine as well as the separate lua state used
|
||||
// by falco outputs. Within the engine, only
|
||||
// formats.formatter is used, so we can unconditionally set
|
||||
// json_output to false.
|
||||
bool json_output = false;
|
||||
falco_formats::init(m_inspector, m_ls, json_output);
|
||||
|
||||
m_rules->load_rules(rules_content, verbose, all_events, m_extra, m_replace_container_info);
|
||||
}
|
||||
|
||||
void falco_engine::load_rules_file(const string &rules_filename, bool verbose, bool all_events)
|
||||
@@ -95,23 +109,23 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
|
||||
|
||||
void falco_engine::enable_rule(string &pattern, bool enabled)
|
||||
{
|
||||
m_evttype_filter.enable(pattern, enabled);
|
||||
m_evttype_filter->enable(pattern, enabled);
|
||||
}
|
||||
|
||||
falco_engine::rule_result *falco_engine::process_event(sinsp_evt *ev)
|
||||
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
|
||||
{
|
||||
|
||||
if(should_drop_evt())
|
||||
{
|
||||
return NULL;
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
if(!m_evttype_filter.run(ev))
|
||||
if(!m_evttype_filter->run(ev))
|
||||
{
|
||||
return NULL;
|
||||
return unique_ptr<struct rule_result>();
|
||||
}
|
||||
|
||||
struct rule_result *res = new rule_result();
|
||||
unique_ptr<struct rule_result> res(new rule_result());
|
||||
|
||||
lua_getglobal(m_ls, lua_on_event.c_str());
|
||||
|
||||
@@ -171,7 +185,12 @@ void falco_engine::add_evttype_filter(string &rule,
|
||||
list<uint32_t> &evttypes,
|
||||
sinsp_filter* filter)
|
||||
{
|
||||
m_evttype_filter.add(rule, evttypes, filter);
|
||||
m_evttype_filter->add(rule, evttypes, filter);
|
||||
}
|
||||
|
||||
void falco_engine::clear_filters()
|
||||
{
|
||||
m_evttype_filter.reset(new sinsp_evttype_filter());
|
||||
}
|
||||
|
||||
void falco_engine::set_sampling_ratio(uint32_t sampling_ratio)
|
||||
@@ -184,6 +203,12 @@ void falco_engine::set_sampling_multiplier(double sampling_multiplier)
|
||||
m_sampling_multiplier = sampling_multiplier;
|
||||
}
|
||||
|
||||
void falco_engine::set_extra(string &extra, bool replace_container_info)
|
||||
{
|
||||
m_extra = extra;
|
||||
m_replace_container_info = replace_container_info;
|
||||
}
|
||||
|
||||
inline bool falco_engine::should_drop_evt()
|
||||
{
|
||||
if(m_sampling_multiplier == 0)
|
||||
|
||||
@@ -19,6 +19,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
// the rule that matched. If no rule matched, returns NULL.
|
||||
//
|
||||
// the reutrned rule_result is allocated and must be delete()d.
|
||||
rule_result *process_event(sinsp_evt *ev);
|
||||
std::unique_ptr<rule_result> process_event(sinsp_evt *ev);
|
||||
|
||||
//
|
||||
// Print details on the given rule. If rule is NULL, print
|
||||
@@ -84,6 +85,9 @@ public:
|
||||
list<uint32_t> &evttypes,
|
||||
sinsp_filter* filter);
|
||||
|
||||
// Clear all existing filters.
|
||||
void clear_filters();
|
||||
|
||||
//
|
||||
// Set the sampling ratio, which can affect which events are
|
||||
// matched against the set of rules.
|
||||
@@ -96,6 +100,16 @@ public:
|
||||
//
|
||||
void set_sampling_multiplier(double sampling_multiplier);
|
||||
|
||||
//
|
||||
// You can optionally add "extra" formatting fields to the end
|
||||
// of all output expressions. You can also choose to replace
|
||||
// %container.info with the extra information or add it to the
|
||||
// end of the expression. This is used in open source falco to
|
||||
// add k8s/mesos/container information to outputs when
|
||||
// available.
|
||||
//
|
||||
void set_extra(string &extra, bool replace_container_info);
|
||||
|
||||
private:
|
||||
|
||||
//
|
||||
@@ -106,7 +120,7 @@ private:
|
||||
inline bool should_drop_evt();
|
||||
|
||||
falco_rules *m_rules;
|
||||
sinsp_evttype_filter m_evttype_filter;
|
||||
std::unique_ptr<sinsp_evttype_filter> m_evttype_filter;
|
||||
|
||||
//
|
||||
// Here's how the sampling ratio and multiplier influence
|
||||
@@ -132,5 +146,8 @@ private:
|
||||
double m_sampling_multiplier;
|
||||
|
||||
std::string m_lua_main_filename = "rule_loader.lua";
|
||||
|
||||
std::string m_extra;
|
||||
bool m_replace_container_info;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void falco_formats::init(sinsp* inspector, lua_State *ls, bool json_output)
|
||||
s_inspector = inspector;
|
||||
s_json_output = json_output;
|
||||
|
||||
luaL_openlib(ls, "falco", ll_falco, 0);
|
||||
luaL_openlib(ls, "formats", ll_falco, 0);
|
||||
}
|
||||
|
||||
int falco_formats::formatter(lua_State *ls)
|
||||
@@ -49,14 +49,13 @@ int falco_formats::formatter(lua_State *ls)
|
||||
try
|
||||
{
|
||||
formatter = new sinsp_evt_formatter(s_inspector, format);
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
}
|
||||
catch(sinsp_exception& e)
|
||||
{
|
||||
throw falco_exception("Invalid output format '" + format + "'.\n");
|
||||
luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
|
||||
}
|
||||
|
||||
lua_pushlightuserdata(ls, formatter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -64,14 +63,14 @@ int falco_formats::free_formatter(lua_State *ls)
|
||||
{
|
||||
if (!lua_islightuserdata(ls, -1))
|
||||
{
|
||||
throw falco_exception("Invalid argument passed to free_formatter");
|
||||
luaL_error(ls, "Invalid argument passed to free_formatter");
|
||||
}
|
||||
|
||||
sinsp_evt_formatter *formatter = (sinsp_evt_formatter *) lua_topointer(ls, 1);
|
||||
|
||||
delete(formatter);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int falco_formats::format_event (lua_State *ls)
|
||||
@@ -43,7 +43,4 @@ class falco_formats
|
||||
static int format_event(lua_State *ls);
|
||||
|
||||
static sinsp* s_inspector;
|
||||
|
||||
private:
|
||||
lua_State* m_ls;
|
||||
};
|
||||
@@ -290,8 +290,8 @@ function compiler.compile_macro(line, list_defs)
|
||||
local ast, error_msg = parser.parse_filter(line)
|
||||
|
||||
if (error_msg) then
|
||||
print ("Compilation error when compiling \""..line.."\": ", error_msg)
|
||||
error(error_msg)
|
||||
msg = "Compilation error when compiling \""..line.."\": ".. error_msg
|
||||
error(msg)
|
||||
end
|
||||
|
||||
-- Traverse the ast looking for events/syscalls in the ignored
|
||||
@@ -315,8 +315,8 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
|
||||
local ast, error_msg = parser.parse_filter(source)
|
||||
|
||||
if (error_msg) then
|
||||
print ("Compilation error when compiling \""..source.."\": ", error_msg)
|
||||
error(error_msg)
|
||||
msg = "Compilation error when compiling \""..source.."\": "..error_msg
|
||||
error(msg)
|
||||
end
|
||||
|
||||
-- Traverse the ast looking for events/syscalls in the ignored
|
||||
|
||||
@@ -121,9 +121,57 @@ end
|
||||
-- object. The by_name index is used for things like describing rules,
|
||||
-- and the by_idx index is used to map the relational node index back
|
||||
-- to a rule.
|
||||
local state = {macros={}, lists={}, filter_ast=nil, rules_by_name={}, n_rules=0, rules_by_idx={}}
|
||||
local state = {macros={}, lists={}, filter_ast=nil, rules_by_name={}, macros_by_name={}, lists_by_name={},
|
||||
n_rules=0, rules_by_idx={}, ordered_rule_names={}, ordered_macro_names={}, ordered_list_names={}}
|
||||
|
||||
function load_rules(rules_content, rules_mgr, verbose, all_events)
|
||||
local function reset_rules(rules_mgr)
|
||||
falco_rules.clear_filters(rules_mgr)
|
||||
state.n_rules = 0
|
||||
state.rules_by_idx = {}
|
||||
state.macros = {}
|
||||
state.lists = {}
|
||||
end
|
||||
|
||||
-- From http://lua-users.org/wiki/TableUtils
|
||||
--
|
||||
function table.val_to_str ( v )
|
||||
if "string" == type( v ) then
|
||||
v = string.gsub( v, "\n", "\\n" )
|
||||
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
|
||||
return "'" .. v .. "'"
|
||||
end
|
||||
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
|
||||
else
|
||||
return "table" == type( v ) and table.tostring( v ) or
|
||||
tostring( v )
|
||||
end
|
||||
end
|
||||
|
||||
function table.key_to_str ( k )
|
||||
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
|
||||
return k
|
||||
else
|
||||
return "[" .. table.val_to_str( k ) .. "]"
|
||||
end
|
||||
end
|
||||
|
||||
function table.tostring( tbl )
|
||||
local result, done = {}, {}
|
||||
for k, v in ipairs( tbl ) do
|
||||
table.insert( result, table.val_to_str( v ) )
|
||||
done[ k ] = true
|
||||
end
|
||||
for k, v in pairs( tbl ) do
|
||||
if not done[ k ] then
|
||||
table.insert( result,
|
||||
table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
|
||||
end
|
||||
end
|
||||
return "{" .. table.concat( result, "," ) .. "}"
|
||||
end
|
||||
|
||||
|
||||
function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replace_container_info)
|
||||
|
||||
compiler.set_verbose(verbose)
|
||||
compiler.set_all_events(all_events)
|
||||
@@ -135,38 +183,50 @@ function load_rules(rules_content, rules_mgr, verbose, all_events)
|
||||
return
|
||||
end
|
||||
|
||||
for i,v in ipairs(rules) do -- iterate over yaml list
|
||||
if type(rules) ~= "table" then
|
||||
error("Rules content \""..rules_content.."\" is not yaml")
|
||||
end
|
||||
|
||||
-- Iterate over yaml list. In this pass, all we're doing is
|
||||
-- populating the set of rules, macros, and lists. We're not
|
||||
-- expanding/compiling anything yet. All that will happen in a
|
||||
-- second pass
|
||||
for i,v in ipairs(rules) do
|
||||
|
||||
if (not (type(v) == "table")) then
|
||||
error ("Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
|
||||
end
|
||||
|
||||
if (v['macro']) then
|
||||
local ast = compiler.compile_macro(v['condition'], state.lists)
|
||||
state.macros[v['macro']] = ast.filter.value
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
|
||||
end
|
||||
|
||||
elseif (v['list']) then
|
||||
-- list items are represented in yaml as a native list, so no
|
||||
-- parsing necessary
|
||||
local items = {}
|
||||
|
||||
-- List items may be references to other lists, so go through
|
||||
-- the items and expand any references to the items in the list
|
||||
for i, item in ipairs(v['items']) do
|
||||
if (state.lists[item] == nil) then
|
||||
items[#items+1] = item
|
||||
else
|
||||
for i, exp_item in ipairs(state.lists[item]) do
|
||||
items[#items+1] = exp_item
|
||||
end
|
||||
for i, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in macro with name "..v['macro'])
|
||||
end
|
||||
end
|
||||
|
||||
state.lists[v['list']] = items
|
||||
state.macros_by_name[v['macro']] = v
|
||||
|
||||
else -- rule
|
||||
elseif (v['list']) then
|
||||
|
||||
if (v['rule'] == nil) then
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
state.ordered_list_names[#state.ordered_list_names+1] = v['list']
|
||||
end
|
||||
|
||||
for i, field in ipairs({'items'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in list with name "..v['list'])
|
||||
end
|
||||
end
|
||||
|
||||
state.lists_by_name[v['list']] = v
|
||||
|
||||
elseif (v['rule']) then
|
||||
|
||||
if (v['rule'] == nil or type(v['rule']) == "table") then
|
||||
error ("Missing name in rule")
|
||||
end
|
||||
|
||||
@@ -176,47 +236,129 @@ function load_rules(rules_content, rules_mgr, verbose, all_events)
|
||||
end
|
||||
end
|
||||
|
||||
-- Note that we can overwrite rules, but the rules are still
|
||||
-- loaded in the order in which they first appeared,
|
||||
-- potentially across multiple files.
|
||||
if state.rules_by_name[v['rule']] == nil then
|
||||
state.ordered_rule_names[#state.ordered_rule_names+1] = v['rule']
|
||||
end
|
||||
|
||||
state.rules_by_name[v['rule']] = v
|
||||
|
||||
local filter_ast, evttypes = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists)
|
||||
else
|
||||
error ("Unknown rule object: "..table.tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
if (filter_ast.type == "Rule") then
|
||||
state.n_rules = state.n_rules + 1
|
||||
-- We've now loaded all the rules, macros, and list. Now
|
||||
-- compile/expand the rules, macros, and lists. We use
|
||||
-- ordered_rule_{lists,macros,names} to compile them in the order
|
||||
-- in which they appeared in the file(s).
|
||||
reset_rules(rules_mgr)
|
||||
|
||||
state.rules_by_idx[state.n_rules] = v
|
||||
for i, name in ipairs(state.ordered_list_names) do
|
||||
|
||||
-- Store the index of this formatter in each relational expression that
|
||||
-- this rule contains.
|
||||
-- This index will eventually be stamped in events passing this rule, and
|
||||
-- we'll use it later to determine which output to display when we get an
|
||||
-- event.
|
||||
mark_relational_nodes(filter_ast.filter.value, state.n_rules)
|
||||
local v = state.lists_by_name[name]
|
||||
|
||||
install_filter(filter_ast.filter.value)
|
||||
-- list items are represented in yaml as a native list, so no
|
||||
-- parsing necessary
|
||||
local items = {}
|
||||
|
||||
-- Pass the filter and event types back up
|
||||
falco_rules.add_filter(rules_mgr, v['rule'], evttypes)
|
||||
-- List items may be references to other lists, so go through
|
||||
-- the items and expand any references to the items in the list
|
||||
for i, item in ipairs(v['items']) do
|
||||
if (state.lists[item] == nil) then
|
||||
items[#items+1] = item
|
||||
else
|
||||
for i, exp_item in ipairs(state.lists[item]) do
|
||||
items[#items+1] = exp_item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Rule ASTs are merged together into one big AST, with "OR" between each
|
||||
-- rule.
|
||||
if (state.filter_ast == nil) then
|
||||
state.filter_ast = filter_ast.filter.value
|
||||
state.lists[v['list']] = items
|
||||
end
|
||||
|
||||
for i, name in ipairs(state.ordered_macro_names) do
|
||||
|
||||
local v = state.macros_by_name[name]
|
||||
|
||||
local ast = compiler.compile_macro(v['condition'], state.lists)
|
||||
state.macros[v['macro']] = ast.filter.value
|
||||
end
|
||||
|
||||
for i, name in ipairs(state.ordered_rule_names) do
|
||||
|
||||
local v = state.rules_by_name[name]
|
||||
|
||||
local filter_ast, evttypes = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists)
|
||||
|
||||
if (filter_ast.type == "Rule") then
|
||||
state.n_rules = state.n_rules + 1
|
||||
|
||||
state.rules_by_idx[state.n_rules] = v
|
||||
|
||||
-- Store the index of this formatter in each relational expression that
|
||||
-- this rule contains.
|
||||
-- This index will eventually be stamped in events passing this rule, and
|
||||
-- we'll use it later to determine which output to display when we get an
|
||||
-- event.
|
||||
mark_relational_nodes(filter_ast.filter.value, state.n_rules)
|
||||
|
||||
install_filter(filter_ast.filter.value)
|
||||
|
||||
-- Pass the filter and event types back up
|
||||
falco_rules.add_filter(rules_mgr, v['rule'], evttypes)
|
||||
|
||||
-- Rule ASTs are merged together into one big AST, with "OR" between each
|
||||
-- rule.
|
||||
if (state.filter_ast == nil) then
|
||||
state.filter_ast = filter_ast.filter.value
|
||||
else
|
||||
state.filter_ast = { type = "BinaryBoolOp", operator = "or", left = state.filter_ast, right = filter_ast.filter.value }
|
||||
end
|
||||
|
||||
-- Enable/disable the rule
|
||||
if (v['enabled'] == nil) then
|
||||
v['enabled'] = true
|
||||
end
|
||||
|
||||
if (v['enabled'] == false) then
|
||||
falco_rules.enable_rule(rules_mgr, v['rule'], 0)
|
||||
end
|
||||
|
||||
-- If the format string contains %container.info, replace it
|
||||
-- with extra. Otherwise, add extra onto the end of the format
|
||||
-- string.
|
||||
if string.find(v['output'], "%container.info", nil, true) ~= nil then
|
||||
|
||||
-- There may not be any extra, or we're not supposed
|
||||
-- to replace it, in which case we use the generic
|
||||
-- "%container.name (id=%container.id)"
|
||||
if replace_container_info == false then
|
||||
v['output'] = string.gsub(v['output'], "%%container.info", "%%container.name (id=%%container.id)")
|
||||
if extra ~= "" then
|
||||
v['output'] = v['output'].." "..extra
|
||||
end
|
||||
else
|
||||
state.filter_ast = { type = "BinaryBoolOp", operator = "or", left = state.filter_ast, right = filter_ast.filter.value }
|
||||
end
|
||||
|
||||
-- Enable/disable the rule
|
||||
if (v['enabled'] == nil) then
|
||||
v['enabled'] = true
|
||||
end
|
||||
|
||||
if (v['enabled'] == false) then
|
||||
falco_rules.enable_rule(rules_mgr, v['rule'], 0)
|
||||
safe_extra = string.gsub(extra, "%%", "%%%%")
|
||||
v['output'] = string.gsub(v['output'], "%%container.info", safe_extra)
|
||||
end
|
||||
else
|
||||
error ("Unexpected type in load_rule: "..filter_ast.type)
|
||||
-- Just add the extra to the end
|
||||
if extra ~= "" then
|
||||
v['output'] = v['output'].." "..extra
|
||||
end
|
||||
end
|
||||
|
||||
-- Ensure that the output field is properly formatted by
|
||||
-- creating a formatter from it. Any error will be thrown
|
||||
-- up to the top level.
|
||||
formatter = formats.formatter(v['output'])
|
||||
formats.free_formatter(formatter)
|
||||
else
|
||||
error ("Unexpected type in load_rule: "..filter_ast.type)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -294,7 +436,10 @@ function on_event(evt_, rule_id)
|
||||
rule_output_counts.by_name[rule.rule] = rule_output_counts.by_name[rule.rule] + 1
|
||||
end
|
||||
|
||||
return rule.rule, rule.priority, rule.output
|
||||
-- Prefix output with '*' so formatting is permissive
|
||||
output = "*"..rule.output
|
||||
|
||||
return rule.rule, rule.priority, output
|
||||
end
|
||||
|
||||
function print_stats()
|
||||
|
||||
@@ -28,6 +28,7 @@ extern "C" {
|
||||
#include "falco_engine.h"
|
||||
const static struct luaL_reg ll_falco_rules [] =
|
||||
{
|
||||
{"clear_filters", &falco_rules::clear_filters},
|
||||
{"add_filter", &falco_rules::add_filter},
|
||||
{"enable_rule", &falco_rules::enable_rule},
|
||||
{NULL,NULL}
|
||||
@@ -44,6 +45,24 @@ void falco_rules::init(lua_State *ls)
|
||||
luaL_openlib(ls, "falco_rules", ll_falco_rules, 0);
|
||||
}
|
||||
|
||||
int falco_rules::clear_filters(lua_State *ls)
|
||||
{
|
||||
if (! lua_islightuserdata(ls, -1))
|
||||
{
|
||||
throw falco_exception("Invalid arguments passed to clear_filters()\n");
|
||||
}
|
||||
|
||||
falco_rules *rules = (falco_rules *) lua_topointer(ls, -1);
|
||||
rules->clear_filters();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void falco_rules::clear_filters()
|
||||
{
|
||||
m_engine->clear_filters();
|
||||
}
|
||||
|
||||
int falco_rules::add_filter(lua_State *ls)
|
||||
{
|
||||
if (! lua_islightuserdata(ls, -3) ||
|
||||
@@ -108,7 +127,9 @@ void falco_rules::enable_rule(string &rule, bool enabled)
|
||||
m_engine->enable_rule(rule, enabled);
|
||||
}
|
||||
|
||||
void falco_rules::load_rules(const string &rules_content, bool verbose, bool all_events)
|
||||
void falco_rules::load_rules(const string &rules_content,
|
||||
bool verbose, bool all_events,
|
||||
string &extra, bool replace_container_info)
|
||||
{
|
||||
lua_getglobal(m_ls, m_lua_load_rules.c_str());
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
@@ -182,7 +203,9 @@ void falco_rules::load_rules(const string &rules_content, bool verbose, bool all
|
||||
lua_pushlightuserdata(m_ls, this);
|
||||
lua_pushboolean(m_ls, (verbose ? 1 : 0));
|
||||
lua_pushboolean(m_ls, (all_events ? 1 : 0));
|
||||
if(lua_pcall(m_ls, 4, 0, 0) != 0)
|
||||
lua_pushstring(m_ls, extra.c_str());
|
||||
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
|
||||
if(lua_pcall(m_ls, 6, 0, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
string err = "Error loading rules:" + string(lerr);
|
||||
|
||||
@@ -31,14 +31,17 @@ class falco_rules
|
||||
public:
|
||||
falco_rules(sinsp* inspector, falco_engine *engine, lua_State *ls);
|
||||
~falco_rules();
|
||||
void load_rules(const string &rules_content, bool verbose, bool all_events);
|
||||
void load_rules(const string &rules_content, bool verbose, bool all_events,
|
||||
std::string &extra, bool replace_container_info);
|
||||
void describe_rule(string *rule);
|
||||
|
||||
static void init(lua_State *ls);
|
||||
static int clear_filters(lua_State *ls);
|
||||
static int add_filter(lua_State *ls);
|
||||
static int enable_rule(lua_State *ls);
|
||||
|
||||
private:
|
||||
void clear_filters();
|
||||
void add_filter(string &rule, list<uint32_t> &evttypes);
|
||||
void enable_rule(string &rule, bool enabled);
|
||||
|
||||
|
||||
71
userspace/engine/token_bucket.cpp
Normal file
71
userspace/engine/token_bucket.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright (C) 2016 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
falco is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
falco is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "token_bucket.h"
|
||||
|
||||
token_bucket::token_bucket()
|
||||
{
|
||||
init(1, 1);
|
||||
}
|
||||
|
||||
token_bucket::~token_bucket()
|
||||
{
|
||||
}
|
||||
|
||||
void token_bucket::init(uint32_t rate, uint32_t max_tokens)
|
||||
{
|
||||
m_rate = rate;
|
||||
m_max_tokens = max_tokens;
|
||||
m_tokens = max_tokens;
|
||||
m_last_seen = sinsp_utils::get_current_time_ns();
|
||||
}
|
||||
|
||||
bool token_bucket::claim()
|
||||
{
|
||||
// Determine the number of tokens gained. Delta between
|
||||
// last_seen and now, divided by the rate.
|
||||
uint64_t now = sinsp_utils::get_current_time_ns();
|
||||
uint64_t tokens_gained = (now - m_last_seen) / (m_rate * 1000000000);
|
||||
m_last_seen = now;
|
||||
|
||||
m_tokens += tokens_gained;
|
||||
|
||||
//
|
||||
// Cap at max_tokens
|
||||
//
|
||||
if(m_tokens > m_max_tokens)
|
||||
{
|
||||
m_tokens = m_max_tokens;
|
||||
}
|
||||
|
||||
//
|
||||
// If tokens is < 1, can't claim.
|
||||
//
|
||||
if(m_tokens < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tokens--;
|
||||
|
||||
return true;
|
||||
}
|
||||
65
userspace/engine/token_bucket.h
Normal file
65
userspace/engine/token_bucket.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright (C) 2016 Draios inc.
|
||||
|
||||
This file is part of falco.
|
||||
|
||||
falco is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
falco is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// A simple token bucket that accumulates tokens at a fixed rate and allows
|
||||
// for limited bursting in the form of "banked" tokens.
|
||||
class token_bucket
|
||||
{
|
||||
public:
|
||||
token_bucket();
|
||||
virtual ~token_bucket();
|
||||
|
||||
//
|
||||
// Initialize the token bucket and start accumulating tokens
|
||||
//
|
||||
void init(uint32_t rate, uint32_t max_tokens);
|
||||
|
||||
//
|
||||
// Returns true if a token can be claimed. Also updates
|
||||
// internal metrics.
|
||||
//
|
||||
bool claim();
|
||||
private:
|
||||
|
||||
//
|
||||
// The number of tokens generated per second.
|
||||
//
|
||||
uint64_t m_rate;
|
||||
|
||||
//
|
||||
// The maximum number of tokens that can be banked for future
|
||||
// claim()s.
|
||||
//
|
||||
uint64_t m_max_tokens;
|
||||
|
||||
//
|
||||
// The current number of tokens
|
||||
//
|
||||
uint64_t m_tokens;
|
||||
|
||||
//
|
||||
// The last time claim() was called (or the object was created).
|
||||
// Nanoseconds since the epoch.
|
||||
//
|
||||
uint64_t m_last_seen;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ include_directories("${CURL_INCLUDE_DIR}")
|
||||
include_directories("${YAMLCPP_INCLUDE_DIR}")
|
||||
include_directories("${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include")
|
||||
|
||||
add_executable(falco configuration.cpp formats.cpp logger.cpp falco_outputs.cpp falco.cpp)
|
||||
add_executable(falco configuration.cpp logger.cpp falco_outputs.cpp statsfilewriter.cpp falco.cpp)
|
||||
|
||||
target_link_libraries(falco falco_engine sinsp)
|
||||
target_link_libraries(falco
|
||||
|
||||
@@ -101,6 +101,13 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
throw invalid_argument("Error reading config file (" + m_config_file + "): No outputs configured. Please configure at least one output file output enabled but no filename in configuration block");
|
||||
}
|
||||
|
||||
string log_level = m_config->get_scalar<string>("log_level", "info");
|
||||
|
||||
falco_logger::set_level(log_level);
|
||||
|
||||
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs", "rate", 1);
|
||||
m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs", "max_burst", 1000);
|
||||
|
||||
falco_logger::log_stderr = m_config->get_scalar<bool>("log_stderr", false);
|
||||
falco_logger::log_syslog = m_config->get_scalar<bool>("log_syslog", true);
|
||||
}
|
||||
|
||||
@@ -144,6 +144,8 @@ class falco_configuration
|
||||
std::list<std::string> m_rules_filenames;
|
||||
bool m_json_output;
|
||||
std::vector<falco_outputs::output_config> m_outputs;
|
||||
uint32_t m_notifications_rate;
|
||||
uint32_t m_notifications_max_burst;
|
||||
private:
|
||||
void init_cmdline_options(std::list<std::string> &cmdline_options);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "configuration.h"
|
||||
#include "falco_engine.h"
|
||||
#include "config_falco.h"
|
||||
#include "statsfilewriter.h"
|
||||
|
||||
bool g_terminate = false;
|
||||
//
|
||||
@@ -97,6 +98,8 @@ static void usage()
|
||||
" -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
|
||||
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
|
||||
" Can be specified multiple times to read from multiple files.\n"
|
||||
" -s <stats_file> If specified, write statistics related to falco's reading/processing of events\n"
|
||||
" to this file. (Only useful in live mode).\n"
|
||||
" -v Verbose output.\n"
|
||||
"\n"
|
||||
);
|
||||
@@ -124,11 +127,23 @@ std::list<string> cmdline_options;
|
||||
//
|
||||
uint64_t do_inspect(falco_engine *engine,
|
||||
falco_outputs *outputs,
|
||||
sinsp* inspector)
|
||||
sinsp* inspector,
|
||||
string &stats_filename)
|
||||
{
|
||||
uint64_t num_evts = 0;
|
||||
int32_t res;
|
||||
sinsp_evt* ev;
|
||||
StatsFileWriter writer;
|
||||
|
||||
if (stats_filename != "")
|
||||
{
|
||||
string errstr;
|
||||
|
||||
if (!writer.init(inspector, stats_filename, 5, errstr))
|
||||
{
|
||||
throw falco_exception(errstr);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Loop through the events
|
||||
@@ -138,6 +153,8 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
|
||||
res = inspector->next(&ev);
|
||||
|
||||
writer.handle();
|
||||
|
||||
if (g_terminate)
|
||||
{
|
||||
break;
|
||||
@@ -171,11 +188,10 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
// engine, which will match the event against the set
|
||||
// of rules. If a match is found, pass the event to
|
||||
// the outputs.
|
||||
falco_engine::rule_result *res = engine->process_event(ev);
|
||||
unique_ptr<falco_engine::rule_result> res = engine->process_event(ev);
|
||||
if(res)
|
||||
{
|
||||
outputs->handle_event(res->evt, res->rule, res->priority, res->format);
|
||||
delete(res);
|
||||
}
|
||||
|
||||
num_evts++;
|
||||
@@ -203,6 +219,7 @@ int falco_init(int argc, char **argv)
|
||||
string pidfilename = "/var/run/falco.pid";
|
||||
bool describe_all_rules = false;
|
||||
string describe_rule = "";
|
||||
string stats_filename = "";
|
||||
bool verbose = false;
|
||||
bool all_events = false;
|
||||
string* k8s_api = 0;
|
||||
@@ -247,7 +264,7 @@ int falco_init(int argc, char **argv)
|
||||
// Parse the args
|
||||
//
|
||||
while((op = getopt_long(argc, argv,
|
||||
"hc:AdD:e:k:K:Ll:m:o:P:p:r:vw:",
|
||||
"hc:AdD:e:k:K:Ll:m:o:P:p:r:s:vw:",
|
||||
long_options, &long_index)) != -1)
|
||||
{
|
||||
switch(op)
|
||||
@@ -319,6 +336,9 @@ int falco_init(int argc, char **argv)
|
||||
case 'r':
|
||||
rules_filenames.push_back(optarg);
|
||||
break;
|
||||
case 's':
|
||||
stats_filename = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
@@ -337,10 +357,10 @@ int falco_init(int argc, char **argv)
|
||||
inspector = new sinsp();
|
||||
engine = new falco_engine();
|
||||
engine->set_inspector(inspector);
|
||||
engine->set_extra(output_format, replace_container_info);
|
||||
|
||||
outputs = new falco_outputs();
|
||||
outputs->set_inspector(inspector);
|
||||
outputs->set_extra(output_format, replace_container_info);
|
||||
|
||||
// Some combinations of arguments are not allowed.
|
||||
if (daemon && pidfilename == "") {
|
||||
@@ -407,7 +427,7 @@ int falco_init(int argc, char **argv)
|
||||
engine->enable_rule(pattern, false);
|
||||
}
|
||||
|
||||
outputs->init(config.m_json_output);
|
||||
outputs->init(config.m_json_output, config.m_notifications_rate, config.m_notifications_max_burst);
|
||||
|
||||
if(!all_events)
|
||||
{
|
||||
@@ -589,7 +609,8 @@ int falco_init(int argc, char **argv)
|
||||
|
||||
num_evts = do_inspect(engine,
|
||||
outputs,
|
||||
inspector);
|
||||
inspector,
|
||||
stats_filename);
|
||||
|
||||
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
|
||||
|
||||
|
||||
@@ -27,17 +27,31 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
using namespace std;
|
||||
|
||||
falco_outputs::falco_outputs()
|
||||
: m_replace_container_info(false)
|
||||
: m_initialized(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
falco_outputs::~falco_outputs()
|
||||
{
|
||||
if(m_initialized)
|
||||
{
|
||||
lua_getglobal(m_ls, m_lua_output_cleanup.c_str());
|
||||
|
||||
if(!lua_isfunction(m_ls, -1))
|
||||
{
|
||||
throw falco_exception("No function " + m_lua_output_cleanup + " found. ");
|
||||
}
|
||||
|
||||
if(lua_pcall(m_ls, 0, 0, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
throw falco_exception(string(lerr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void falco_outputs::init(bool json_output)
|
||||
void falco_outputs::init(bool json_output, uint32_t rate, uint32_t max_burst)
|
||||
{
|
||||
// The engine must have been given an inspector by now.
|
||||
if(! m_inspector)
|
||||
@@ -47,15 +61,16 @@ void falco_outputs::init(bool json_output)
|
||||
|
||||
falco_common::init(m_lua_main_filename.c_str(), FALCO_SOURCE_LUA_DIR);
|
||||
|
||||
// Note that falco_formats is added to both the lua state used
|
||||
// by the falco engine as well as the separate lua state used
|
||||
// by falco outputs.
|
||||
falco_formats::init(m_inspector, m_ls, json_output);
|
||||
|
||||
falco_logger::init(m_ls);
|
||||
}
|
||||
|
||||
void falco_outputs::set_extra(string &extra, bool replace_container_info)
|
||||
{
|
||||
m_extra = extra;
|
||||
m_replace_container_info = replace_container_info;
|
||||
m_notifications_tb.init(rate, max_burst);
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void falco_outputs::add_output(output_config oc)
|
||||
@@ -90,46 +105,22 @@ void falco_outputs::add_output(output_config oc)
|
||||
|
||||
}
|
||||
|
||||
void falco_outputs::handle_event(sinsp_evt *ev, string &level, string &priority, string &format)
|
||||
void falco_outputs::handle_event(sinsp_evt *ev, string &rule, string &priority, string &format)
|
||||
{
|
||||
if(!m_notifications_tb.claim())
|
||||
{
|
||||
falco_logger::log(LOG_DEBUG, "Skipping rate-limited notification for rule " + rule + "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
lua_getglobal(m_ls, m_lua_output_event.c_str());
|
||||
|
||||
// If the format string contains %container.info, replace it
|
||||
// with extra. Otherwise, add extra onto the end of the format
|
||||
// string.
|
||||
string format_w_extra = format;
|
||||
size_t pos;
|
||||
|
||||
if((pos = format_w_extra.find("%container.info")) != string::npos)
|
||||
{
|
||||
// There may not be any extra, or we're not supposed
|
||||
// to replace it, in which case we use the generic
|
||||
// "%container.name (id=%container.id)"
|
||||
if(m_extra == "" || ! m_replace_container_info)
|
||||
{
|
||||
// 15 == strlen(%container.info)
|
||||
format_w_extra.replace(pos, 15, "%container.name (id=%container.id)");
|
||||
}
|
||||
else
|
||||
{
|
||||
format_w_extra.replace(pos, 15, m_extra);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just add the extra to the end
|
||||
if (m_extra != "")
|
||||
{
|
||||
format_w_extra += " " + m_extra;
|
||||
}
|
||||
}
|
||||
|
||||
if(lua_isfunction(m_ls, -1))
|
||||
{
|
||||
lua_pushlightuserdata(m_ls, ev);
|
||||
lua_pushstring(m_ls, level.c_str());
|
||||
lua_pushstring(m_ls, rule.c_str());
|
||||
lua_pushstring(m_ls, priority.c_str());
|
||||
lua_pushstring(m_ls, format_w_extra.c_str());
|
||||
lua_pushstring(m_ls, format.c_str());
|
||||
|
||||
if(lua_pcall(m_ls, 4, 0, 0) != 0)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
#pragma once
|
||||
|
||||
#include "falco_common.h"
|
||||
#include "token_bucket.h"
|
||||
|
||||
//
|
||||
// This class acts as the primary interface between a program and the
|
||||
@@ -40,22 +41,24 @@ public:
|
||||
std::map<std::string, std::string> options;
|
||||
};
|
||||
|
||||
void init(bool json_output);
|
||||
void init(bool json_output, uint32_t rate, uint32_t max_burst);
|
||||
|
||||
void add_output(output_config oc);
|
||||
|
||||
void set_extra(string &extra, bool replace_container_info);
|
||||
|
||||
//
|
||||
// ev is an event that has matched some rule. Pass the event
|
||||
// to all configured outputs.
|
||||
//
|
||||
void handle_event(sinsp_evt *ev, std::string &level, std::string &priority, std::string &format);
|
||||
void handle_event(sinsp_evt *ev, std::string &rule, std::string &priority, std::string &format);
|
||||
|
||||
private:
|
||||
bool m_initialized;
|
||||
|
||||
// Rate limits notifications
|
||||
token_bucket m_notifications_tb;
|
||||
|
||||
std::string m_lua_add_output = "add_output";
|
||||
std::string m_lua_output_event = "output_event";
|
||||
std::string m_lua_output_cleanup = "output_cleanup";
|
||||
std::string m_lua_main_filename = "output.lua";
|
||||
std::string m_extra;
|
||||
bool m_replace_container_info;
|
||||
};
|
||||
|
||||
@@ -20,18 +20,62 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "logger.h"
|
||||
#include "chisel_api.h"
|
||||
|
||||
#include "falco_common.h"
|
||||
|
||||
const static struct luaL_reg ll_falco [] =
|
||||
{
|
||||
{"syslog", &falco_logger::syslog},
|
||||
{NULL,NULL}
|
||||
};
|
||||
|
||||
int falco_logger::level = LOG_INFO;
|
||||
|
||||
void falco_logger::init(lua_State *ls)
|
||||
{
|
||||
luaL_openlib(ls, "falco", ll_falco, 0);
|
||||
}
|
||||
|
||||
void falco_logger::set_level(string &level)
|
||||
{
|
||||
if(level == "emergency")
|
||||
{
|
||||
falco_logger::level = LOG_EMERG;
|
||||
}
|
||||
else if(level == "alert")
|
||||
{
|
||||
falco_logger::level = LOG_ALERT;
|
||||
}
|
||||
else if(level == "critical")
|
||||
{
|
||||
falco_logger::level = LOG_CRIT;
|
||||
}
|
||||
else if(level == "error")
|
||||
{
|
||||
falco_logger::level = LOG_ERR;
|
||||
}
|
||||
else if(level == "warning")
|
||||
{
|
||||
falco_logger::level = LOG_WARNING;
|
||||
}
|
||||
else if(level == "notice")
|
||||
{
|
||||
falco_logger::level = LOG_NOTICE;
|
||||
}
|
||||
else if(level == "info")
|
||||
{
|
||||
falco_logger::level = LOG_INFO;
|
||||
}
|
||||
else if(level == "debug")
|
||||
{
|
||||
falco_logger::level = LOG_DEBUG;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw falco_exception("Unknown log level " + level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int falco_logger::syslog(lua_State *ls) {
|
||||
int priority = luaL_checknumber(ls, 1);
|
||||
|
||||
@@ -49,6 +93,12 @@ bool falco_logger::log_stderr = true;
|
||||
bool falco_logger::log_syslog = true;
|
||||
|
||||
void falco_logger::log(int priority, const string msg) {
|
||||
|
||||
if(priority > falco_logger::level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (falco_logger::log_syslog) {
|
||||
::syslog(priority, "%s", msg.c_str());
|
||||
}
|
||||
|
||||
@@ -32,11 +32,15 @@ class falco_logger
|
||||
public:
|
||||
static void init(lua_State *ls);
|
||||
|
||||
// Will throw exception if level is unknown.
|
||||
static void set_level(string &level);
|
||||
|
||||
// value = falco.syslog(level, message)
|
||||
static int syslog(lua_State *ls);
|
||||
|
||||
static void log(int priority, const string msg);
|
||||
|
||||
static int level;
|
||||
static bool log_stderr;
|
||||
static bool log_syslog;
|
||||
};
|
||||
|
||||
@@ -24,6 +24,8 @@ mod.levels = levels
|
||||
|
||||
local outputs = {}
|
||||
|
||||
local formatters = {}
|
||||
|
||||
function mod.stdout(level, msg)
|
||||
print (msg)
|
||||
end
|
||||
@@ -74,15 +76,34 @@ end
|
||||
|
||||
function output_event(event, rule, priority, format)
|
||||
local level = level_of(priority)
|
||||
|
||||
-- If format starts with a *, remove it, as we're adding our own
|
||||
-- prefix here.
|
||||
if format:sub(1,1) == "*" then
|
||||
format = format:sub(2)
|
||||
end
|
||||
|
||||
format = "*%evt.time: "..levels[level+1].." "..format
|
||||
formatter = falco.formatter(format)
|
||||
msg = falco.format_event(event, rule, levels[level+1], formatter)
|
||||
if formatters[rule] == nil then
|
||||
formatter = formats.formatter(format)
|
||||
formatters[rule] = formatter
|
||||
else
|
||||
formatter = formatters[rule]
|
||||
end
|
||||
|
||||
msg = formats.format_event(event, rule, levels[level+1], formatter)
|
||||
|
||||
for index,o in ipairs(outputs) do
|
||||
o.output(level, msg, o.config)
|
||||
end
|
||||
end
|
||||
|
||||
falco.free_formatter(formatter)
|
||||
function output_cleanup()
|
||||
for rule, formatter in pairs(formatters) do
|
||||
formats.free_formatter(formatter)
|
||||
end
|
||||
|
||||
formatters = {}
|
||||
end
|
||||
|
||||
function add_output(output_name, config)
|
||||
|
||||
120
userspace/falco/statsfilewriter.cpp
Normal file
120
userspace/falco/statsfilewriter.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <sys/time.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "statsfilewriter.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool g_save_stats = false;
|
||||
static void timer_handler (int signum)
|
||||
{
|
||||
g_save_stats = true;
|
||||
}
|
||||
|
||||
extern char **environ;
|
||||
|
||||
StatsFileWriter::StatsFileWriter()
|
||||
: m_num_stats(0), m_inspector(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
StatsFileWriter::~StatsFileWriter()
|
||||
{
|
||||
m_output.close();
|
||||
}
|
||||
|
||||
bool StatsFileWriter::init(sinsp *inspector, string &filename, uint32_t interval_sec, string &errstr)
|
||||
{
|
||||
struct itimerval timer;
|
||||
struct sigaction handler;
|
||||
|
||||
m_inspector = inspector;
|
||||
|
||||
m_output.exceptions ( ofstream::failbit | ofstream::badbit );
|
||||
m_output.open(filename, ios_base::app);
|
||||
|
||||
memset (&handler, 0, sizeof (handler));
|
||||
handler.sa_handler = &timer_handler;
|
||||
if (sigaction(SIGALRM, &handler, NULL) == -1)
|
||||
{
|
||||
errstr = string("Could not set up signal handler for periodic timer: ") + strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
timer.it_value.tv_sec = interval_sec;
|
||||
timer.it_value.tv_usec = 0;
|
||||
timer.it_interval = timer.it_value;
|
||||
if (setitimer(ITIMER_REAL, &timer, NULL) == -1)
|
||||
{
|
||||
errstr = string("Could not set up periodic timer: ") + strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// (Undocumented) feature. Take any environment keys prefixed
|
||||
// with FALCO_STATS_EXTRA_XXX and add them to the output. Used by
|
||||
// run_performance_tests.sh.
|
||||
for(uint32_t i=0; environ[i]; i++)
|
||||
{
|
||||
char *p = strstr(environ[i], "=");
|
||||
if(!p)
|
||||
{
|
||||
errstr = string("Could not find environment separator in ") + string(environ[i]);
|
||||
return false;
|
||||
}
|
||||
string key(environ[i], p-environ[i]);
|
||||
string val(p+1, strlen(environ[i])-(p-environ[i])-1);
|
||||
if(key.compare(0, 18, "FALCO_STATS_EXTRA_") == 0)
|
||||
{
|
||||
string sub = key.substr(18);
|
||||
if (m_extra != "")
|
||||
{
|
||||
m_extra += ", ";
|
||||
}
|
||||
m_extra += "\"" + sub + "\": " + "\"" + val + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StatsFileWriter::handle()
|
||||
{
|
||||
if (g_save_stats)
|
||||
{
|
||||
scap_stats cstats;
|
||||
scap_stats delta;
|
||||
|
||||
g_save_stats = false;
|
||||
m_num_stats++;
|
||||
m_inspector->get_capture_stats(&cstats);
|
||||
|
||||
if(m_num_stats == 1)
|
||||
{
|
||||
delta = cstats;
|
||||
}
|
||||
else
|
||||
{
|
||||
delta.n_evts = cstats.n_evts - m_last_stats.n_evts;
|
||||
delta.n_drops = cstats.n_drops - m_last_stats.n_drops;
|
||||
delta.n_preemptions = cstats.n_preemptions - m_last_stats.n_preemptions;
|
||||
}
|
||||
|
||||
m_output << "{\"sample\": " << m_num_stats;
|
||||
if(m_extra != "")
|
||||
{
|
||||
m_output << ", " << m_extra;
|
||||
}
|
||||
m_output << ", \"cur\": {" <<
|
||||
"\"events\": " << cstats.n_evts <<
|
||||
", \"drops\": " << cstats.n_drops <<
|
||||
", \"preemptions\": " << cstats.n_preemptions <<
|
||||
"}, \"delta\": {" <<
|
||||
"\"events\": " << delta.n_evts <<
|
||||
", \"drops\": " << delta.n_drops <<
|
||||
", \"preemptions\": " << delta.n_preemptions <<
|
||||
"}, \"drop_pct\": " << (delta.n_evts == 0 ? 0 : (100.0*delta.n_drops/delta.n_evts)) <<
|
||||
"}," << endl;
|
||||
|
||||
m_last_stats = cstats;
|
||||
}
|
||||
}
|
||||
32
userspace/falco/statsfilewriter.h
Normal file
32
userspace/falco/statsfilewriter.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <sinsp.h>
|
||||
|
||||
// Periodically collects scap stats files and writes them to a file as
|
||||
// json.
|
||||
|
||||
class StatsFileWriter {
|
||||
public:
|
||||
StatsFileWriter();
|
||||
virtual ~StatsFileWriter();
|
||||
|
||||
// Returns success as bool. On false fills in errstr.
|
||||
bool init(sinsp *inspector, std::string &filename,
|
||||
uint32_t interval_sec,
|
||||
string &errstr);
|
||||
|
||||
// Should be called often (like for each event in a sinsp
|
||||
// loop).
|
||||
void handle();
|
||||
|
||||
protected:
|
||||
uint32_t m_num_stats;
|
||||
sinsp *m_inspector;
|
||||
std::ofstream m_output;
|
||||
std::string m_extra;
|
||||
scap_stats m_last_stats;
|
||||
};
|
||||
Reference in New Issue
Block a user