diff --git a/CMakeLists.txt b/CMakeLists.txt index 368456e4..343bfb3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,9 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fno-strict-aliasing -DNDEBUG") add_definitions(-DPLATFORM_NAME="${CMAKE_SYSTEM_NAME}") add_definitions(-DK8S_DISABLE_THREAD) -add_definitions(-DHAS_CAPTURE) +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + add_definitions(-DHAS_CAPTURE) +endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(KBUILD_FLAGS "${DRAIOS_DEBUG_FLAGS} ${DRAIOS_FEATURE_FLAGS}") @@ -46,6 +48,10 @@ set(CMAKE_INSTALL_PREFIX /usr) set(CMD_MAKE make) set(SYSDIG_DIR "${PROJECT_SOURCE_DIR}/../sysdig") +# make luaJIT work on OS X +if(APPLE) + set(CMAKE_EXE_LINKER_FLAGS "-pagezero_size 10000 -image_base 100000000") +endif() include(ExternalProject) @@ -295,14 +301,18 @@ if(NOT USE_BUNDLED_LPEG) else() set(LPEG_SRC "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg") set(LPEG_LIB "${PROJECT_BINARY_DIR}/lpeg-prefix/src/lpeg/build/lpeg.a") + set(LPEG_DEPENDENCIES "") + if(USE_BUNDLED_LUAJIT) + list(APPEND LPEG_DEPENDENCIES "luajit") + endif() ExternalProject_Add(lpeg - DEPENDS luajit + DEPENDS ${LPEG_DEPENDENCIES} 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 + 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 "") + INSTALL_COMMAND "") endif() # @@ -332,11 +342,11 @@ else() 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 + URL_MD5 "4a4bced818da0b9ae7fc8ebc690792a7" + BUILD_COMMAND ${CMD_MAKE} + BUILD_IN_SOURCE 1 CONFIGURE_COMMAND ./bootstrap && ./configure - INSTALL_COMMAND "") + INSTALL_COMMAND "") endif() # @@ -357,8 +367,15 @@ if(NOT USE_BUNDLED_LYAML) else() set(LYAML_SRC "${PROJECT_BINARY_DIR}/lyaml-prefix/src/lyaml/ext/yaml") set(LYAML_LIB "${LYAML_SRC}/.libs/yaml.a") + set(LYAML_DEPENDENCIES "") + if(USE_BUNDLED_LUAJIT) + list(APPEND LYAML_DEPENDENCIES "luajit") + endif() + if(USE_BUNDLED_LIBYAML) + list(APPEND LYAML_DEPENDENCIES "libyaml") + endif() ExternalProject_Add(lyaml - DEPENDS libyaml luajit + DEPENDS ${LYAML_DEPENDENCIES} URL "http://download.draios.com/dependencies/lyaml-release-v6.0.tar.gz" URL_MD5 "dc3494689a0dce7cf44e7a99c72b1f30" BUILD_COMMAND ${CMD_MAKE} @@ -370,7 +387,9 @@ endif() install(FILES falco.yaml DESTINATION "${FALCO_ETC_DIR}") -add_subdirectory("${SYSDIG_DIR}/driver" "${PROJECT_BINARY_DIR}/driver") +if(CMAKE_SYSTEM_NAME MATCHES "Linux") + add_subdirectory("${SYSDIG_DIR}/driver" "${PROJECT_BINARY_DIR}/driver") +endif() add_subdirectory("${SYSDIG_DIR}/userspace/libscap" "${PROJECT_BINARY_DIR}/userspace/libscap") add_subdirectory("${SYSDIG_DIR}/userspace/libsinsp" "${PROJECT_BINARY_DIR}/userspace/libsinsp") diff --git a/examples/k8s-using-daemonset/README.md b/examples/k8s-using-daemonset/README.md new file mode 100644 index 00000000..b81b6f3b --- /dev/null +++ b/examples/k8s-using-daemonset/README.md @@ -0,0 +1,5 @@ +=Example K8s Services for Falco= + +The yaml file in this directory installs the following: + - Open Source Falco, as a DaemonSet. Falco is configured to communicate with the K8s API server via its service account, and changes its output to be K8s-friendly. It also sends to a slack webhook for the `#demo-falco-alerts` channel on our [public slack](https://sysdig.slack.com/messages/demo-falco-alerts/). + - The [Falco Event Generator](https://github.com/draios/falco/wiki/Generating-Sample-Events), as a deployment that ensures it runs on exactly 1 node. diff --git a/examples/k8s-using-daemonset/falco-daemonset.yaml b/examples/k8s-using-daemonset/falco-daemonset.yaml new file mode 100644 index 00000000..19ae8ba4 --- /dev/null +++ b/examples/k8s-using-daemonset/falco-daemonset.yaml @@ -0,0 +1,59 @@ +apiVersion: extensions/v1beta1 +kind: DaemonSet +metadata: + name: falco + labels: + name: falco-daemonset + app: demo +spec: + template: + metadata: + labels: + name: falco + app: demo + role: security + spec: + containers: + - name: falco + image: sysdig/falco:latest + securityContext: + privileged: true + command: [ "/usr/bin/falco", "-K", "/var/run/secrets/kubernetes.io/serviceaccount/token", "-k", "https://kubernetes", "-pk", "-o", "json_output=true", "-o", "program_output.enabled=true", "-o", "program_output.program=jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/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"] + volumeMounts: + - mountPath: /host/var/run/docker.sock + name: docker-socket + readOnly: true + - mountPath: /host/dev + name: dev-fs + readOnly: true + - mountPath: /host/proc + name: proc-fs + readOnly: true + - mountPath: /host/boot + name: boot-fs + readOnly: true + - mountPath: /host/lib/modules + name: lib-modules + readOnly: true + - mountPath: /host/usr + name: usr-fs + readOnly: true + volumes: + - name: docker-socket + hostPath: + path: /var/run/docker.sock + - name: dev-fs + hostPath: + path: /dev + - name: proc-fs + hostPath: + path: /proc + - name: boot-fs + hostPath: + path: /boot + - name: lib-modules + hostPath: + path: /lib/modules + - name: usr-fs + hostPath: + path: /usr diff --git a/examples/k8s-using-daemonset/falco-event-generator-deployment.yaml b/examples/k8s-using-daemonset/falco-event-generator-deployment.yaml new file mode 100644 index 00000000..00789e8c --- /dev/null +++ b/examples/k8s-using-daemonset/falco-event-generator-deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: falco-event-generator-deployment + labels: + name: falco-event-generator-deployment + app: demo +spec: + replicas: 1 + template: + metadata: + labels: + app: falco-event-generator + spec: + containers: + - name: falco-event-generator + image: sysdig/falco-event-generator:latest diff --git a/rules/falco_rules.yaml b/rules/falco_rules.yaml index 3fba1462..6a453d5e 100644 --- a/rules/falco_rules.yaml +++ b/rules/falco_rules.yaml @@ -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, systemd-logind, su, nologin, faillog, lastlog, newgrp, sg] + items: [login, systemd, '"(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 @@ -113,6 +113,9 @@ - list: db_server_binaries items: [mysqld] +- list: gitlab_binaries + items: [gitlab-shell, git] + - macro: server_procs condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd) @@ -256,7 +259,7 @@ 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) + systemd-machine, debconf-show, rollerd, bind9.postinst, sv) and not proc.pname in (sysdigcloud_binaries) and not fd.directory in (/etc/cassandra, /etc/ssl/certs/java) and not ansible_running_python @@ -365,7 +368,7 @@ 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 + landscape-sysin, nessusd, PM2, syslog-summary, erl_child_setup ] - rule: Run shell untrusted @@ -430,7 +433,7 @@ 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) + monitoring_binaries, gitlab_binaries, initdb, pg_ctl, awk, apache2, falco, cron, erl_child_setup) 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 @@ -469,7 +472,7 @@ tags: [host, users] - list: allowed_dev_files - items: [/dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /dev/tty, /dev/random, /dev/urandom, /dev/console] + items: [/dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /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 @@ -479,6 +482,7 @@ (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) + and not fd.name startswith /dev/tty output: "File created below /dev by untrusted program (user=%user.name command=%proc.cmdline file=%fd.name)" priority: WARNING tags: [filesystem] diff --git a/userspace/engine/falco_common.cpp b/userspace/engine/falco_common.cpp index 974dd23b..ac427a12 100644 --- a/userspace/engine/falco_common.cpp +++ b/userspace/engine/falco_common.cpp @@ -24,6 +24,10 @@ along with falco. If not, see . falco_common::falco_common() { m_ls = lua_open(); + if(!m_ls) + { + throw falco_exception("Cannot open lua"); + } luaL_openlibs(m_ls); } diff --git a/userspace/engine/formats.cpp b/userspace/engine/formats.cpp index 7be6eaab..eb25e715 100644 --- a/userspace/engine/formats.cpp +++ b/userspace/engine/formats.cpp @@ -24,12 +24,14 @@ along with falco. If not, see . sinsp* falco_formats::s_inspector = NULL; -bool s_json_output = false; +bool falco_formats::s_json_output = false; +sinsp_evt_formatter_cache *falco_formats::s_formatters = NULL; const static struct luaL_reg ll_falco [] = { {"formatter", &falco_formats::formatter}, {"free_formatter", &falco_formats::free_formatter}, + {"free_formatters", &falco_formats::free_formatters}, {"format_event", &falco_formats::format_event}, {NULL,NULL} }; @@ -38,6 +40,10 @@ void falco_formats::init(sinsp* inspector, lua_State *ls, bool json_output) { s_inspector = inspector; s_json_output = json_output; + if(!s_formatters) + { + s_formatters = new sinsp_evt_formatter_cache(s_inspector); + } luaL_openlib(ls, "formats", ll_falco, 0); } @@ -73,22 +79,43 @@ int falco_formats::free_formatter(lua_State *ls) return 0; } +int falco_formats::free_formatters(lua_State *ls) +{ + if(s_formatters) + { + delete(s_formatters); + s_formatters = NULL; + } + return 0; +} + int falco_formats::format_event (lua_State *ls) { string line; - if (!lua_islightuserdata(ls, -1) || + if (!lua_isstring(ls, -1) || !lua_isstring(ls, -2) || !lua_isstring(ls, -3) || !lua_islightuserdata(ls, -4)) { - throw falco_exception("Invalid arguments passed to format_event()\n"); + lua_pushstring(ls, "Invalid arguments passed to format_event()"); + lua_error(ls); } sinsp_evt* evt = (sinsp_evt*)lua_topointer(ls, 1); const char *rule = (char *) lua_tostring(ls, 2); const char *level = (char *) lua_tostring(ls, 3); - sinsp_evt_formatter* formatter = (sinsp_evt_formatter*)lua_topointer(ls, 4); + const char *format = (char *) lua_tostring(ls, 4); - formatter->tostring(evt, &line); + string sformat = format; + + try { + s_formatters->tostring(evt, sformat, &line); + } + catch (sinsp_exception& e) + { + string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'"; + lua_pushstring(ls, err.c_str()); + lua_error(ls); + } // For JSON output, the formatter returned just the output // string containing the format text and values. Use this to diff --git a/userspace/engine/formats.h b/userspace/engine/formats.h index e5a2781a..c901460b 100644 --- a/userspace/engine/formats.h +++ b/userspace/engine/formats.h @@ -39,8 +39,13 @@ class falco_formats // falco.free_formatter(formatter) static int free_formatter(lua_State *ls); + // falco.free_formatters() + static int free_formatters(lua_State *ls); + // formatted_string = falco.format_event(evt, formatter) static int format_event(lua_State *ls); static sinsp* s_inspector; + static sinsp_evt_formatter_cache *s_formatters; + static bool s_json_output; }; diff --git a/userspace/engine/rules.cpp b/userspace/engine/rules.cpp index 63b9b416..cec545ec 100644 --- a/userspace/engine/rules.cpp +++ b/userspace/engine/rules.cpp @@ -49,7 +49,8 @@ int falco_rules::clear_filters(lua_State *ls) { if (! lua_islightuserdata(ls, -1)) { - throw falco_exception("Invalid arguments passed to clear_filters()\n"); + lua_pushstring(ls, "Invalid arguments passed to clear_filters()"); + lua_error(ls); } falco_rules *rules = (falco_rules *) lua_topointer(ls, -1); @@ -70,7 +71,8 @@ int falco_rules::add_filter(lua_State *ls) ! lua_istable(ls, -2) || ! lua_istable(ls, -1)) { - throw falco_exception("Invalid arguments passed to add_filter()\n"); + lua_pushstring(ls, "Invalid arguments passed to add_filter()"); + lua_error(ls); } falco_rules *rules = (falco_rules *) lua_topointer(ls, -4); @@ -122,7 +124,8 @@ int falco_rules::enable_rule(lua_State *ls) ! lua_isstring(ls, -2) || ! lua_isnumber(ls, -1)) { - throw falco_exception("Invalid arguments passed to enable_rule()\n"); + lua_pushstring(ls, "Invalid arguments passed to enable_rule()"); + lua_error(ls); } falco_rules *rules = (falco_rules *) lua_topointer(ls, -3); diff --git a/userspace/falco/lua/output.lua b/userspace/falco/lua/output.lua index 5a50f9de..a6aa42fb 100644 --- a/userspace/falco/lua/output.lua +++ b/userspace/falco/lua/output.lua @@ -24,8 +24,6 @@ mod.levels = levels local outputs = {} -local formatters = {} - function mod.stdout(level, msg) print (msg) end @@ -84,14 +82,8 @@ function output_event(event, rule, priority, format) end format = "*%evt.time: "..levels[level+1].." "..format - 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) + msg = formats.format_event(event, rule, levels[level+1], format) for index,o in ipairs(outputs) do o.output(level, msg, o.config) @@ -99,11 +91,7 @@ function output_event(event, rule, priority, format) end function output_cleanup() - for rule, formatter in pairs(formatters) do - formats.free_formatter(formatter) - end - - formatters = {} + formats.free_formatters() end function add_output(output_name, config)