Compare commits

...

119 Commits

Author SHA1 Message Date
Luca Marturana
6fd7f0d628 Merge branch 'dev' into agent-master 2017-08-23 10:30:27 +02:00
Mark Stemm
240a8ffffa Merge pull request #264 from draios/mergable-lists
Mergable lists
2017-08-10 11:08:36 -07:00
Mark Stemm
d1265ff520 Merge pull request #265 from draios/remove-trailing-newline-output
Remove trailing newlines from output
2017-08-10 09:44:39 -07:00
Mark Stemm
0bc2d4f162 Automated tests for list append.
Test the case of appending to a list and appending to a nonexistent
list (should error).
2017-08-10 09:36:31 -07:00
Mark Stemm
2c189d6a60 Add ability to append to lists.
List nodes can now have an 'append' key. If present and true, any values
in this list will be appended to the end of any existing list with the
same name.

It is an error to have a list with 'append' true that has a name that is
not an existing list.
2017-08-09 18:07:34 -07:00
Mark Stemm
ebed9f8dfd Remove trailing newlines from output
If in yaml, the output field is folded-style aka:

output: <
   some multi-line
   output here

The unfolded string will have a trailing newline. Remove it.
2017-08-09 17:53:53 -07:00
Mark Stemm
9d6fe878e1 Merge pull request #262 from draios/allow-dots-in-paths
Allow dots in paths.
2017-08-04 11:56:15 -07:00
Mark Stemm
de520a60fb Allow dots in paths.
Add a dot to the set of characters that can be in a path string.
2017-08-04 11:06:51 -07:00
Thom van Os
d6fe29b47d Merge branch 'dev' into agent-master 2017-07-27 14:04:16 -07:00
Mark Stemm
5c1aa8dc44 Merge pull request #260 from draios/fix-kernel-path
Use uname -r for kernel modules
2017-07-14 10:08:41 -07:00
Mark Stemm
8d57d18959 Use uname -r for kernel modules
This handles cases where multiple sets of kernel headers are installed.
2017-07-14 09:17:28 -07:00
Riccardo Schirone
a71cbcd7ee Merge branch 'dev' into agent-master 2017-07-03 12:18:10 +02:00
Mark Stemm
3349decd22 Merge pull request #258 from draios/better-list-substitution
Better list substitution
2017-06-30 16:01:05 -07:00
Mark Stemm
eecc92736b Add unit tests for list substitution/order
Add new unit tests to check that list substitution is working as
expected, with test cases for the list substitution occurring at the
beginning, middle, and end of a condition.

Also add tests that verify that overrides on list/macro/rule names
always occur in order.
2017-06-30 15:12:43 -07:00
Mark Stemm
f1b44da90c Perform list substitution only on word boundaries
When performing list substitution, only replace a list name when it is
surrounded by whitespace or expected punctuation characters. Lua
patterns don't have a notion of this-or-that patterns e.g. (^|abc), so
we have 3 versions of the substitution depending on whether he list name
occurs in the beginning, middle, or end of a string.

This fixes #197.
2017-06-30 15:11:44 -07:00
Mark Stemm
42e50356cf Merge pull request #257 from draios/validate-macros
Also validate macros at parse time.
2017-06-27 17:18:13 -07:00
Mark Stemm
9e7ce4d36f Also validate macros at parse time.
Also validate macros when they are parsed. Macros are also validated as
a part of rules being parsed, but it's possible to have an individual
rules file containing only macros, or a macro not explicitly tied to any
rule. In this case, it's useful to be able to check the macro to see if
it contains dangling macro references.
2017-06-27 16:44:42 -07:00
Mark Stemm
2991ea423a Merge pull request #254 from draios/dont-trim-strings
Don't trim quoted strings
2017-06-20 13:48:31 -07:00
Mark Stemm
481582ca09 Don't trim quoted strings
When parsing condition expressions, if the type of an ast node is
String (aka quoted string), don't trim whitespace from the value. This
ensures that conditions that want to match exact strings e.g. command
lines with leading/trailing spaces will work properly.

This fixes #253.
2017-06-20 11:47:00 -07:00
Mark Stemm
38f488bfda Beta rule updates (#247)
* Updates from beta customers.

- add anacron as a cron program

* Reorganize package management binaries

Split package_management_binaries into two separate lists rpm_binaries
and deb_binaries. unattended-upgr is common to both worlds so it's still
in package_management_binaries.

Also change Write below rpm database to use rpm_binaries instead of its
own list.

Also add 75-system-updat (truncated) as a shell spawner.

* Add rules for jenkins

Add rules that allow jenkins to spawn shells, both in containers and
directly on the host.

Also handle jenkins slaves that run /tmp/slave.jar.

* Allow npm to run shells.

Not yet allowing node to run shells itself, although we want to add
something to reduce node-related FPs.

* Allow urlgrabber/git-remote to access /etc

urlgrabber and git-remote both try to access the RHEL nss database,
containing shared certificates. I may change this in a more general way
by changing open_read/open_write to only look for successful opens.

* Only look for successful open_read/open_writes

Change the macros open_read/open_write to only trigger on successful
opens (when fd.num > 0). This is a pretty big change to behavior, but
is more intuitive.

This required a small update to the open counts for a couple of unit
tests, but otherwise they still all passed with this change.

* Allow rename_device to write below /dev

Part of udev.

* Allow cloud-init to spawn shells.

Part of https://cloud-init.io/

* Allow python to run a shell that runs sdchecks

sdchecks is a part of the sysdig monitor agent.

* Allow dev creation binaries to write below etc.

Specifically this includes blkid and /etc/blkid/blkid.tab.

* Allow git binaries to spawn shells.

They were already allowed to run shells in a container.

* Add /dev/kmsg as an allowed /dev file

Allows userspace programs to write to kernel log.

* Allow other make programs to spawn shells.

Also allow gmake/cmake to spawn shells and put them in their own list
make_binaries.

* Add better mesos support.

Mesos slaves appear to be in a container due to their cgroup and can run
programs mesos-health-check/mesos-docker-exec to monitor the containers
on the slave, so allow them to run shells.

Add mesos-agent, mesos-logrotate, mesos-fetch as shell spawners both in
and out of containers.

Add gen_resolvconf. (short for gen_resolvconf.py) as a program that can
write to /etc.

Add toybox (used by mesos, part of http://landley.net/toybox/about.html)
as a shell spawner.

* systemd can listen on network ports.

Systemd can listen on network ports to launch daemons on demand, so
allow it to perform network activity.

* Let docker binaries setuid.

Let docker binaries setuid and add docker-entrypoi (truncation
intentional) to the set of docker binaries.

* Change cis-related rules to be less noisy

Change the two cis-related falco rules "File Open by Privileged
Container" and "Sensitive Mount by Container" to be less noisy. We found
in practice that tracking every open still results in too many falco
notifications.

For now, change the rules to only track the initial process start in the
container by looking for vpid=1. This should result in only triggering
when a privileged/sensitive mount container is started. This is slightly
less coverage but is far less noisy.

* Add quay.io/sysdig as trusted containers

These are used for sysdig cloud onpremise deployments.

* Add gitlab-runner-b(uild) as a gitlab binary.

Add gitlab-runner-b (truncated gitlab-runner-build) as a gitlab binary.

* Add ceph as a shell spawner.

Also allow ceph to spawn shells in a container.

* Allow some shells by command line.

For some mesos containers, where the container doesn't have an image and
is just a tarball in a cgroup/namespace, we don't have any image to work
with. In those cases, allow specific command lines.

* Allow user 'nobody' to setuid.

Allow the user nobody to setuid. This depends on the user nobody being
set up in the first place to have no access, but that should be an ok
assumption.

* Additional allowed shell commandlines

* Add additional shells.

* Allow multiple users to become themself.

Add rule somebody_becoming_themself that handles cases of nobody and
www-data trying to setuid to themself. The sysdig filter language
doesn't support template/variable values to allow "user.name=X and
evt.arg.uid=X for a given X", so we have to enumerate the users.

* More known spawn command lines

* Let make binaries be run in containers.

Some CI/CD pipelines build in containers.

* Add additional shell spawning command lines

* Add additional apt program apt-listchanges.

* Add gitlab-ce as shell spawning container.

* Allow PM2 to spawn shells in containers.

Was already in the general list, seen in some customers, so adding to
the in containers list.

* Clean up pass to fix long lines.

Take a pass through the rules making sure each line is < 120 characters.

* Change tests for privileged container rules.

Change unit tests to reflect the new privileged/sensitive mount
container rules that only detect container launch.
2017-06-19 11:28:15 -07:00
Riccardo Schirone
42a3dd1ea3 Merge branch 'osx-install' into dev 2017-06-19 10:08:59 +02:00
Riccardo Schirone
b8743385e8 Fix installation of falco on OS X (no driver, /usr not writable) 2017-06-16 22:36:13 +02:00
Mark Stemm
87caa55b17 Merge pull request #248 from draios/fix-nodejs-example
Explicitly spawn program via shell.
2017-06-14 16:12:59 -07:00
Mark Stemm
646aed5b8b Explicitly spawn program via shell.
So example continues to work.
2017-06-14 15:26:17 -07:00
Brett
6bfff60fc3 Add *.pyc to .gitignore 2017-06-14 13:04:14 -07:00
Mark Stemm
99d6bccc81 Merge branch 'dev' into agent-master 2017-06-06 10:13:23 -07:00
Mark Stemm
6ebbbd47d8 Merge pull request #245 from draios/prepare-for-0-7-0
Update for 0.7.0.
2017-05-30 12:52:44 -07:00
Mark Stemm
69ebcdd8e9 Update for 0.7.0.
Update README/CHANGELOG for 0.7.0.
2017-05-30 09:35:24 -07:00
Mark Stemm
74c97489bd Merge pull request #244 from draios/better-priorities
Use a wider range of priorities in rules.
2017-05-25 13:51:12 -07:00
Mark Stemm
5bafa198c6 Update automated tests to handle new priority lvls
The default falco ruleset now has a wider variety of priorities, so
adjust the automated tests to match:

 - Instead of creating a generic test yaml entry for every trace file in
   traces-{positive,negative,info} with assumptions about detect levels,
   add a new falco_traces.yaml.in multiplex file that has specific
   information about the detect priorities and rule detect counts for each
   trace file.
 - If a given trace file doesn't have a corresponding entry in
   falco_traces.yaml.in, a generic entry is added with a simple
   detect: (True|False) value and level. That way you can get specific
   detect levels/counts for existing trace files, but if you forget to
   add a trace to falco_traces.yaml.in, you'll still get some coverage.
 - falco_tests.yaml.in isn't added to any longer, so rename it to
   falco_tests.yaml.
 - Avocado is now run twice--once on each yaml file. The final test
   passes if both avocado runs pass.
2017-05-25 12:15:35 -07:00
Mark Stemm
edce729bd9 Use a wider range of priorities in rules.
Review the priorities used by each rule and try to use a consistent set
that uses more of the possible priorities. The general guidelines I used
were:

 - If a rule is related to a write of state (i.e. filesystem, etc.),
   its priority is ERROR.
 - If a rule is related to an unauthorized read of state (i.e. reading
   sensitive filees, etc.), its priority is WARNING.
 - If a rule is related to unexpected behavior (spawning an unexpected
   shell in a container, opening an unexpected network connection, etc.), its priority
   is NOTICE.
 - If a rule is related to behaving against good practices (unexpected
   privileged containers, containers with sensitive mounts, running
   interactive commands as root), its priority is INFO.

One exception is that the most FP-prone rule (Run shell untrusted) has a
priority of DEBUG.
2017-05-24 18:54:14 -07:00
Mark Stemm
f426c4292d Merge pull request #243 from draios/falco-fps
Address some setns FPs.
2017-05-24 13:18:08 -07:00
Mark Stemm
277d8ab887 Merge pull request #242 from draios/container-shell-with-tty
Add rule for shell with terminal in container.
2017-05-24 10:49:03 -07:00
Mark Stemm
697d718739 Merge pull request #237 from dkerwin/add_gitlab_mon_command
Add gitlab-mon command
2017-05-24 10:29:28 -07:00
Mark Stemm
307a484425 Merge pull request #241 from sublimino/patch-1
fix: invalid spaces in README markdown
2017-05-24 10:28:22 -07:00
Mark Stemm
c5a964e651 Address some setns FPs.
Allow the sysdig cloud agent to call setns to collect java process
metrics.

We've also seen cases where some of the intermediate processes created
below runc appear to call setns. It appears that this only should happen
if some events (like the execve that spawns the intermediate processes)
are lost, but just to be safe allow processes starting with "runc:" to
call setns.
2017-05-24 10:17:57 -07:00
Andrew Martin
612fbb00d9 fix: invalid spaces in README markdown
Fix invalid spaces in markdown links

sysdig-CLA-1.0-signed-off-by: Andrew Martin sublimino@gmail.com
2017-05-24 11:16:16 +10:00
Mark Stemm
e88612a1af Add rule for shell with terminal in container.
Add a new falco rule "Terminal shell in container" that looks for shells
spawned in a container with an attached terminal. This is similar to the
existing "Run shell in container" rule, but doesn't have as many
exceptions as we expect this to be even less rare.
2017-05-23 13:37:44 -07:00
Mark Stemm
a86e3fc748 Merge pull request #239 from draios/update-for-0.6.1
Update for 0.6.1.
2017-05-15 11:07:44 -07:00
Mark Stemm
e97056569f Update for 0.6.1.
Update README/CHANGELOG for 0.6.1.
2017-05-15 10:37:57 -07:00
Brett
f92f74eaa8 Merge branch 'dev' into agent-master 2017-05-05 12:01:57 -07:00
Mark Stemm
0e163b892f Merge pull request #238 from draios/claim-multiple-tokens
Add ability to claim multiple tokens.
2017-05-02 14:04:23 -07:00
Mark Stemm
4d148ce28f Add ability to claim multiple tokens.
This way you can use it as a form of bandwidth throttling.
2017-05-02 11:46:20 -07:00
Daniel Kerwin
974d864b3b Add gitlab-mon command
sysdig-CLA-1.0-signed-off-by: Daniel Kerwin daniel@gini.net
2017-05-02 17:30:50 +02:00
Mark Stemm
a3c83e7f6e Merge pull request #236 from draios/expose-tokens
Add ability to get number of tokens.
2017-04-27 13:19:47 -07:00
Mark Stemm
dafc4c2b88 Expose last seen time.
Also expose last seen time for token bucket.
2017-04-27 12:03:02 -07:00
Mark Stemm
c066be3905 Allow the initial time to be externally provided.
Allow the initial start time to be externally provided. Saves a call to
getttimeofday and allows running from an external clock (i.e. trace files).
2017-04-27 12:02:21 -07:00
Mark Stemm
f5ce6752be Add ability to get number of tokens.
Add a method to fetch the current number of available tokens.
2017-04-27 11:22:19 -07:00
Mark Stemm
060db62644 Merge pull request #235 from draios/fix-token-bucket-rate
Fix token bucket rate
2017-04-27 08:12:25 -07:00
Mark Stemm
1ad91c05f5 Fix token bucket rate
We were dividing the tokens gained by the rate instead of multiplying.
2017-04-26 19:02:04 -07:00
Mark Stemm
76876bc3ae Merge pull request #234 from draios/token-bucket-external-time
Allow for an external clock in token bucket.
2017-04-25 17:40:50 -07:00
Mark Stemm
e183de3b89 Allow rate to be less than 1.
Change all the token-related variables to doubles so the rate can be
less than 1.
2017-04-25 13:02:34 -07:00
Mark Stemm
87a6c74290 Allow for an external clock in token bucket.
Allow now to be externally provided to avoid unnecessary gettimeofday()
calls.
2017-04-25 10:01:25 -07:00
Luca Marturana
d42d0e2dd1 Merge branch 'dev' into agent-master 2017-04-14 14:57:56 +02:00
Mark Stemm
718113f7bd Merge pull request #232 from draios/remove-apache-shell-spawner
Don't allow apache2 to spawn shells in containers
2017-04-07 13:05:30 -07:00
Mark Stemm
955e1d78b1 Don't allow apache2 to spawn shells in containers
This ensures that interpreted php,perl,etc code run by apache won't be
able to spawn shells, either.

This fixes https://github.com/draios/falco/issues/231.
2017-04-06 15:24:21 -07:00
Luca Marturana
135b4d9975 Merge branch 'dev' into agent-master 2017-03-30 14:46:44 +02:00
Mark Stemm
0cabeddf49 Merge pull request #228 from draios/prepare-for-0.6.0
Update changelog/readme for 0.6.0.
2017-03-29 15:11:05 -07:00
Mark Stemm
6127ca6e41 Update k8s README
To reflect github's new slightly stricter markdown format.
2017-03-29 14:39:27 -07:00
Mark Stemm
a2a707f771 Update changelog/readme for 0.6.0.
Updating with 0.6.0 featues/bug fixes.

Also update the formatting of README to honor github's new slightly
stricter markdown format.
2017-03-29 12:05:37 -07:00
Mark Stemm
3c2051176e Merge pull request #224 from draios/own-driver
Own driver
2017-03-24 21:35:48 -07:00
Mark Stemm
73fbbdb577 Add automated tests for packages/driver installs
Add automated tests for running falco from a package and container. As a
result, this will also test building the kernel module as well as
runnning falco-probe-loader as a backup.

In travis.yml, switch to the docker-enabled vm and install dkms. This
changed the environment slightly, so change how avocado's python
dependencies are installed. After building falco, copy the .deb package
to docker/local and build a local docker image based on that package.

Add the following new tests:

 - docker_package: this uses "docker run" to run the image created in
   travis.yml. This includes using dkms to build the kernel module and
   load it. In addition, the conf directory is mounted to /host/conf, the
   rules directory is mounted to /host/rules, and the traces directory is
   mounted to /host/traces.
 - docker_package_local_driver: this disables dkms via a volume mount
   that maps /dev/null to /usr/sbin/dkms and copies the kernel module by
   hand into the container to /root/.sysdig/falco-probe-....ko. As a
   result, falco-probe-loader will use the local kernel module instead
   of building one itself.
 - debian_package: this installs the .deb package and runs the installed
   version of falco.

Ideally, there'd also be a test for downloading the driver, but since
the driver depends on the kernel as well as the falco version string,
you can't put a single driver on download.draios.com that will work
long-term.

These tests depend on the following new test attributes:
  - package: if present, this points to the docker image/debian package
    to install.
  - addl_docker_run_args: if present, will be added to the docker run
    command.
  - copy_local_driver: if present, will copy the built kernel module to
    ~/.sysdig. ~/.sysdig/* is always cleared out before each test.
  - run_duration: maps to falco's -M <secs> flag
  - trace_file is now optional.

Also add some misc general test changes:
  - Clean up our use of process.run. By default it will fail a test if the
    run program returns non-zero, so we don't have to grab the exit
    status. In addition, get rid of sudo in the command lines and use the
    sudo attribute instead.

  - Fix some tests that were writing to files below /tmp/falco_outputs
    by creating the directory first. Useful when running avocado directly.
2017-03-24 16:54:42 -07:00
Mark Stemm
52b006e875 Add ability to run live for specific duration
Use -M <secs> (same as sysdig) to run falco for a specific duration and
exit.
2017-03-24 13:54:20 -07:00
Mark Stemm
f72182d9af Merge pull request #226 from draios/fix-k8s-daemonset
Make sure entrypoint runs for docker pod.
2017-03-21 14:44:57 -07:00
Mark Stemm
8d58589c39 Make sure entrypoint runs for docker pod.
If a daemonset specifies a command, this overrides the entrypoint. In
falco's case, the entrypoint handles the details of loading the kernel
driver, so specifying a command accidently prevents the driver from
being loaded.

This happens to work if you had a previously loaded sysdig_probe driver
lying around.

The fix is to specify args instead. In this case, the driver will be
loaded via the entrypoint.

This fixes https://github.com/draios/falco/issues/225.
2017-03-21 14:10:44 -07:00
Mark Stemm
ec5adfe892 Build and package standalone falco kernel module
Start packaging (and building when necessary) a falco-specific kernel
module in falco releases. Previously, falco would depend on sysdig and
use its kernel module instead.

The kernel module was already templated to some degree in various
places, so we just had to change the templated name from
sysdig/sysdig-probe to falco/falco-probe.

In containers, run falco-probe-loader instead of
sysdig-probe-loader. This is actually a script in the sysdig repository
which is modified in https://github.com/draios/sysdig/pull/789, and uses
the filename to indicate what kernel module to build and/or load.

For the falco package itself, don't depend on sysdig any longer but instead
depend on dkms and its dependencies, using sysdig as a guide on the set
of required packages.

Additionally, for the package pre-install/post-install scripts start
running falco-probe-loader.

Finally, add a --version argument to falco so it can pass the desired
version string to falco-probe-loader.
2017-03-20 15:56:37 -07:00
Luca Marturana
a25166b7ac Merge branch 'dev' into agent-master 2017-03-20 15:45:29 +01:00
Mark Stemm
18900089f3 Merge pull request #221 from dkerwin/erl_child_setup_spawn_in_container
Add erl_child_setup to shell spawning binaries in a container.
2017-03-14 20:05:51 -07:00
Mark Stemm
490a3fef00 Merge pull request #222 from draios/add-k8s-example
Add falco,event generator files for k8s.
2017-03-07 14:36:33 -05:00
Mark Stemm
5e8dc8bce4 Add falco,event generator files for k8s.
Add example k8s yaml files that allow for running falco as a k8s
daemonset and the event generator as a deployment, running on 1 node.

Falco is configured to send its output to a slack webhook corresponding
to the #demo-falco-alerts channel on sysdig's public slack channel.

The output is is k8s friendly by using -pk, -k (k8s api server), and
-K (credentials to communicate with api server).
2017-03-07 10:46:32 -08:00
Daniel Kerwin
d29742a617 Add erl_child_setup to shell spawning binaries in a container.
sysdig-CLA-1.0-signed-off-by: Daniel Kerwin <daniel@linuxaddicted.de>
2017-03-06 21:33:44 +01:00
Mark Stemm
353defe362 Merge pull request #220 from dkerwin/add_gitlab_binaries
Add support for gitlab omnibus containers/pod
2017-03-06 11:13:28 -08:00
Mark Stemm
6b9620084f Merge pull request #218 from draios/add-erl-child-setup
Add erl_child_setup as a shell spawner.
2017-03-06 11:07:25 -08:00
Daniel Kerwin
537565d27a Add support for gitlab omnibus containers/pod
(https://docs.gitlab.com/omnibus/README.html).

sysdig-CLA-1.0-signed-off-by: Daniel Kerwin <daniel@linuxaddicted.de>
2017-03-06 17:22:24 +01:00
Mark Stemm
b2529f1108 Add erl_child_setup as a shell spawner. 2017-03-06 08:00:30 -08:00
Mark Stemm
561c388dab Merge pull request #212 from draios/use-formatter-cache
Use formatter cache
2017-02-27 21:10:44 -08:00
Mark Stemm
db469c6514 Use sysdig's formatter cache.
Use the sinsp_evt_formatter_cache added in
https://github.com/draios/sysdig/pull/771 instead of a local cache. This
simplifies the lua side quite a bit, as it only needs to call
format_output(), and clean up everything via free_formatters() in
output_cleanup().

On the C side, use a sinsp_evt_formatter object and use it in
format_event().
2017-02-27 12:15:49 -08:00
Mark Stemm
fb36af12cf Return lua errors not falco_exceptions
In C functions that implement lua functions, don't directly throw
falco_exceptions, which results in opaque error messages like:

Mon Feb 27 10:09:58 2017: Runtime error: Error invoking function output:
C++ exception. Exiting.

Instead, return lua errors via lua_error().
2017-02-27 11:57:36 -08:00
Riccardo Schirone
7d711dbb32 Merge branch 'compile-osx2' into dev 2017-02-23 18:42:27 +01:00
Riccardo Schirone
58357d3bf9 CMakeLists: set ExternalProject dependencies only when necessary 2017-02-22 14:40:44 +01:00
Riccardo Schirone
8b98a61bcc CMakeLists: fix compilation on OS X 2017-02-22 14:25:34 +01:00
Riccardo Schirone
f70a7aef6f CMakeLists: fix whitespaces 2017-02-22 14:18:02 +01:00
Riccardo Schirone
c12ab700ec engine: throw an exception if lua cannot be opened 2017-02-22 14:16:16 +01:00
Mark Stemm
38f562ea89 Merge pull request #209 from draios/address-falco-beta-fps
More changes to address FPs.
2017-02-21 16:21:18 -08:00
Mark Stemm
f1aadef054 More changes to address FPs.
- Sometimes systemd changes its process name to '(systemd)', probably
   for a forked daemon process. Add that version to login_binaries.
 - Add sv (part of runit) as a program that can write below /etc.
 - Allow all /dev/tty* files by moving /dev/tty from the list to a
   "startswith /dev/tty" condition.
2017-02-21 14:58:55 -08:00
Luca Marturana
800a3f1ea1 Merge branch 'dev' into agent-master 2017-02-21 11:47:36 +01:00
Mark Stemm
1c21b3bc8a Merge pull request #206 from draios/add-tags
Add tags
2017-02-13 13:18:27 -08:00
Mark Stemm
185729d5d6 Address feedback from PR
- Instead of having a possibly null string pointer as the argument to
   enable_* and process_event, have wrapper versions that assume a
   default falco ruleset. The default ruleset name is a static member of
   the falco_engine class, and the default ruleset id is created/found
   in the constructor.
 - This makes the whole mechanism simple enough that it doesn't require
   seprarate testing, so remove the capability within falco to read a
   ruleset from the environment and remove automated tests that specify
   a ruleset.
 - Make pattern/tags/ruleset arguments to enable_* functions const.

(I'll squash this down before I commit)
2017-02-10 11:54:30 -08:00
Mark Stemm
0a69fc0c85 Tag existing falco ruleset.
Tag the existing ruleset to group tags in a meaningful way. The added
tags are:

 - filesystem: the rule relates to reading/writing files
 - sofware_mgmt: the rule relates to any software/package management
   tool like rpm, dpkg, etc.
 - process: the rule relates to starting a new process or changing the
   state of a current process.
 - database: the rule relates to databases
 - host: the rule *only* works outside of containers
 - shell: the rule specifically relates to starting shells
 - container: the rule *only* works inside containers
 - cis: the rule is related to the CIS Docker benchmark.
 - users: the rule relates to management of users or changing the
   identity of a running process.
 - network: the rule relates to network activity

Rules can have multiple tags if they relate to multiple of the
above. Rules do not have to have tags, although all the current rules do.
2017-02-08 11:08:37 -08:00
Mark Stemm
88faa7c1e7 Add automated tests for tagged rules
Add automated tests that verify the ability to tag sets of rules,
disable them with -T, and run them with -t, works:

 - New test option disable_tags adds -T <tag> arguments to the falco
   command line, and run_tags adds -t <tag> arguments to the falco command
   line.
 - A new trace file open-multiple-files.scap opens 13 different files,
   and a new rules file has 13 different rules with all combinations of
   the tags a, b, c (both forward and backward), a rule with an empty
   list of tags, a rule with no tags field, and a rule with a completely
   different tag d.

Using the above, add tests for:

 - Both disabling all combations of a, b, c using disable_tags as well as
   run all combinations of a, b, c, using run_tags.
 - Specifying both disabled (-T/-D) and enabled (-t) rules. Not allowed.
 - Specifying a ruleset while having tagged rules enabled, rules based
   on a name disabled, and no particular rules enabled or disabled.
2017-02-08 11:08:36 -08:00
Mark Stemm
a0a6914b6a Add support for tagging rules.
- in lua, look for a tags attribute to each rule. This is passed up in
  add_filter as a tags argument (as a lua table). If not present, an
  empty table is used. The tags table is iterated to populate a set
  of tags as strings, which is passed to add_filter().
- A new method falco_engine::enable_rule_by_tag is similar to
  enable_rule(), but is given a set of tag strings. Any rules containing
  one of the tags is enabled/disabled.
- The list of event types has been changed to a set to more accurately
  reflect its purpose.
- New argument to falco -T allows disabling all rules matching a given
  tag, via enable_rule_by_tag(). It can be provided multiple times.
- New argument to falco -t allows running those rules matching a given
  tag. If provided all rules are first disabled. It can be
  provided multiple times, but can not be combined with -T or
  -D (disable rules by name)
- falco_enging supports the notion of a ruleset. The idea is that you
  can choose a set of rules that are enabled/disabled by using
  enable_rule()/enable_rule_by_tag() in combination with a
  ruleset. Later, in process_event() you include that ruleset and the
  rules you had previously enabled will be run.
- rulsets are provided as strings in enable_rule()/enable_rule_by_tag()
  and as numbers in process_event()--this avoids the overhead of string
  lookups per-event. Ruleset ids are created on the fly as needed. A
  utility method find_ruleset_id() looks up the ruleset id for a given
  name. The default ruleset is NULL string/0 numeric if not provided.
- Although the ruleset is a useful falco engine feature, it isn't that
  important to the falco standalone program, so it's not
  documented. However, you can change the ruleset by providing
  FALCO_RULESET in the environment.
2017-02-08 11:08:36 -08:00
Luca Marturana
31464de885 Merge branch 'dev' into agent-master 2017-02-07 11:06:22 +01:00
Mark Stemm
df08a80a12 Merge pull request #207 from draios/address-addl-falco-fps
Additional changes to reduce FPs.
2017-02-06 16:46:11 -08:00
Mark Stemm
8a1f62c610 Additional changes to reduce FPs.
- Add flanneld as a privileged container.
 - Add parentheses grouping around many of the "x running y"
   containers. I haven't found this strictly necessary with their
   current use in rules, but this ensures they will be isolated when
   used.
 - Allow denyhosts to spawn shells--it runs iptables to add/remove hosts
   from its deny list.
2017-02-06 15:57:54 -08:00
Mark Stemm
1e205db8aa Use right name for event-generator. 2017-02-03 18:10:34 -08:00
Luca Marturana
9b308d2793 Merge branch 'dev' into agent-master 2017-02-02 12:35:47 +01:00
Mark Stemm
3d5789a297 Merge pull request #200 from draios/ndis-hids-etc-rule-updates
Rule updates related to other security products
2017-02-01 17:37:09 -08:00
Mark Stemm
b9d0857362 Rule updates related to other security products
This is a rework of a PR made by @juju4 that had a bunch of additions
related to running other security/monitoring products, including aide,
bro, icinga2, nagios, ansible, etc.

This overlapped a lot with changes I had been making to reduce
noisiness, so rather than have @juju4 deal with the conflicts I took the
changes and made a separate commit with the non-conflicting additions.

A summary of the changes:
 - Add docker-compose as a docker binary.
 - Add showq/critical-stack as setuid binaries.
 - Add lxd binaries
 - Add some additional package management binaries.
 - Add support for host intrustion detection systems like aide.
 - Add support for network intrustion detections systems like bro.
 - Add support for monitoring systems like nagios, icinga2, npcd.
 - Other one-off additions to other lists of mail/etc programs.
2017-02-01 16:25:50 -08:00
Mark Stemm
1afbaba632 Merge pull request #205 from draios/demo-improvements
Demo improvements
2017-02-01 16:24:05 -08:00
Mark Stemm
e0a5034a43 Ensure falco-event-generator actions are detected.
A new trace file falco-event-generator.scap contains the result of
running the falco event generator in docker, via:

docker run --security-opt seccomp=unconfined sysdig/falco-event-generator:latest /usr/local/bin/event_generator --once

Make sure this trace file detects the exact set of events we expect for
each rule. This required adding a new verification method
check_detections_by_rule that finds the per-rule counts and compares
them to the expected counts, which are included in the test description
under the key "detect_counts".

This is the first time a trace file for a test is actually in one of the
downloaded zip files. This means it will be tested twice (one for simple
detect-or-not, once for actual counts).

Adding this test showed a problem with Run shell in container
rule--since sysdig/falco-event-generator startswith sysdig/falco, it was
being treated as a trusted container. Modify the macro
trusted_containers to not allow falco-event-generator to be trusted.
2017-02-01 15:02:44 -08:00
Mark Stemm
6356490b1c Misc demo improvements.
Small changes to improve the use of falco_event_generator with falco:

 - In event_generator, some actions like exec_ls won't trigger
   notifications on their own. So exclude them from -a all.
 - For all actions, print details on what the action will do.
 - For actions that won't result in a falco notification in containers,
   note that in the output.
 - The short version of --once wasn't working, fix the getopt.
 - Explicitly saying -a all wasn't working, fix.
 - Don't rely on an external ruleset in the nodejs docker-compose
   demo--the built in rules are sufficient now.
2017-02-01 14:51:18 -08:00
Mark Stemm
511d0997da Merge pull request #204 from draios/cmake-dependencies
CMakeLists: add dependencies to lyaml project (fix #130)
2017-01-31 14:40:05 -08:00
Riccardo Schirone
6f9f1e4792 CMakeLists: add dependencies to lyaml project 2017-01-31 21:57:26 +00:00
Luca Marturana
a99f09da96 Merge branch 'dev' into agent-master 2017-01-31 11:47:33 +01:00
Mark Stemm
c09b6390a3 Merge pull request #202 from draios/more-spurious-alerts
Address more spurious alerts
2017-01-27 12:21:22 -08:00
Mark Stemm
3f2814259a Address more spurious alerts
- Add a second possible location for denyhosts
 - Add PM2 (http://pm2.keymetrics.io/) as a shell spawner.
 - There was a bug in use of ansible_running_python. We actually need
   two variants depending on whether ansible is the parent or current
   process. parent_ansble_running_python is used for Run shell
   untrusted, ansible_running_python is used for other rules.
2017-01-27 11:49:02 -08:00
Mark Stemm
b04bccd1a7 Merge pull request #201 from draios/remove-cchh
Remove cchh image.
2017-01-27 10:14:51 -08:00
Mark Stemm
e21fecf0ef Remove cchh image.
We had added this image while the changes in
https://github.com/draios/falco/pull/177 made it to everyone. This is in
a release now, so we'll remove it from the rule set.
2017-01-27 09:03:25 -08:00
Mark Stemm
ceafeca87e Merge pull request #199 from draios/no-assert-travis-debug
Set -DNDEBUG for travis debug builds.
2017-01-26 10:55:32 -08:00
Mark Stemm
9285aa59c1 Set -DNDEBUG for travis debug builds.
Within the sysdig code there are several ASSERTS() that can occur for
error paths that aren't truly critical, such as:

17:33:52 DEBUG| [stderr] falco: /home/travis/build/draios/sysdig/userspace/libsinsp/parsers.cpp:1657: static void sinsp_parser::parse_openat_dir(sinsp_evt*, char*, int64_t, std::string*): Assertion `false' failed.

Looking at the code, it's not a truly fatal error, just an inability to
find fd information:

----
     if(evt->m_fdinfo == NULL)
     {
             ASSERT(false);
             *sdir = "<UNKNOWN>";
     }
----

When running regression tests in travis, we don't want these ASSERTs to
cause falco to exit.

To allow this, in CMakeLists.txt only set DRAIOS_DEBUG_FLAGS if it
wasn't already set, and in travis's cmake, add -DNDEBUG to
DRAIOS_DEBUG_FLAGS.
2017-01-26 10:12:11 -08:00
Luca Marturana
1e0ddba11a Merge branch 'dev' into agent-master 2017-01-25 18:08:35 +01:00
Mark Stemm
34e17cb951 Several changes to reduce FPs
Several changes to reduce spurious alerts when managing machines via
ansible:

 - Add ansible_running_python (that is, ansible-spawned python scripts)
   as scripts that can read sensitive files and write below
   /etc. Notably this is the user ansible module.
 - Also add comments to ansible_running_python suggesting users make it
   more strict by specifically naming the root directory for ansible
   scripts.
 - Add pypy as a python variant that can run ansible-related scripts.

Also other changes to reduce FPs:

 - add apt-add-reposit, apt-auto-remova (truncation intentional),
   apt-get, apt, apt-key as package management programs, and add package
   management binaries to the set of shell spawners. The overlapping
   binaries that were in known_shell_spawn_binaries were removed.
 - add passwd_binaries, gpg, insserv, apparmor_parser, update-mime,
   tzdata.{config,postinst}, systemd-machine, and debconf-show to
   the set of binaries that can write below /etc.
 - Add vsftpd as a program that can read sensitive files.
 - Add additional programs (incl. python support programs like pip,
   pycompile) as ones that can spawn shells.
 - Allow privileged containers to spawn shells.
 - Break out the set of files below /dev that are written to with O_CREAT
   into a separate list, and add /dev/random,urandom,console to the list.
 - Add python running denyhosts as a program that can write below /etc.
 - Also add binaries starting with linux-image- as ones that can spawn
   shells. These are perl scripts run as a part of installing
   linux-image-N.N packages.
2017-01-25 08:34:52 -08:00
Mark Stemm
bc83ac18a0 Allow shells spawned by ansible.
Changes to allow shells spawned by ansible. In general this is actually
pretty difficult--on the remote managed machine, ansible performs
actions simply by running python over ssh without any explicit ansible
helper or command line.

One (weak) hint is that the python scripts being run are usually under a
directory with ansible in the name. So use that as the basis for a macro
ansible_running_python. In turn, that macro is used as a negative
condition for the run shell untrusted rule.

This is a pretty fragile and easily exploited condition, so add a note
to the macro saying so.
2017-01-19 15:09:24 -08:00
Mark Stemm
10d0c8f982 Add a local dockerfile variant.
Add a local dockerfile variant that allows creating an image from a
local .deb package.
2017-01-17 10:24:38 -08:00
Mark Stemm
8f53bcbb05 Patch jq 1.5 with a fix for security vulns.
After downloading jq 1.5, apply the changes in
stedolan/jq@8eb1367
by downloading the commit as a patch and applying it. This fixes
CVE-2015-8863.
2017-01-17 10:24:38 -08:00
Mark Stemm
7286b50f4d Update libcurl to 7.52.1.
This fixes a set of ~10 security vulnerabilities.
2017-01-17 10:24:38 -08:00
Mark Stemm
4c60b7c1d2 Update openssl to 1.0.2j.
This fixes a set of ~25 security vulnerabilities.
2017-01-17 10:24:38 -08:00
Mark Stemm
85480f32d6 Avoid FPs resulting from ubuntu weekly cron jobs
Feedback from a falco user:

--
to more findings from last night:

logrotate cronjob (Debian default):

Shell spawned by untrusted binary (user=root shell=sh parent=logrotate cmdline=sh -c invoke-rc.d rsyslog rotate > /dev/null logrotate_script /var/log/syslog)

passwd cronjob (Debian default):

Sensitive file opened for reading by non-trusted program (user=root name=cmp command=cmp -s shadow.bak /etc/shadow file=/etc/shadow)
--

New macro cmp_cp_by_passwd allows cmp/cp to be run by passwd to examine
sensitive files. Add logrotate as a program that can spawn a shell.

Also do some cleanups, moving items to lists and splitting long
single-line conditions into multiple lines.
2017-01-17 09:12:53 -08:00
Luca Marturana
4139370df5 Merge branch 'agent-master' into dev
agent-master went out of sync, probably some rebase/forcepush happened
on dev. Used `git merge -s ours agent-master` here to put all the
commits of agent-master on dev and ignoring anything from agent-master.

So now we can merge from dev to agent-master with fast forward and no
conflicts
2017-01-17 10:58:08 +01:00
57 changed files with 2412 additions and 459 deletions

5
.gitignore vendored
View File

@@ -1,6 +1,7 @@
/build*
*~
test/falco_test.pyc
*.pyc
test/falco_tests.yaml
test/traces-negative
test/traces-positive
@@ -12,7 +13,7 @@ test/results*.json.*
userspace/falco/lua/re.lua
userspace/falco/lua/lpeg.so
docker/event-generator/event-generator
docker/event-generator/event_generator
docker/event-generator/mysqld
docker/event-generator/httpd
docker/event-generator/sha1sum

View File

@@ -2,6 +2,9 @@ language: c
env:
- BUILD_TYPE=Debug
- BUILD_TYPE=Release
sudo: required
services:
- docker
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update
@@ -9,16 +12,16 @@ install:
- sudo apt-get --force-yes install g++-4.8
- sudo apt-get install rpm linux-headers-$(uname -r)
- git clone https://github.com/draios/sysdig.git ../sysdig
- sudo apt-get install -y python-pip libvirt-dev jq
- sudo apt-get install -y python-pip libvirt-dev jq dkms
- cd ..
- curl -Lo avocado-36.0-tar.gz https://github.com/avocado-framework/avocado/archive/36.0lts.tar.gz
- tar -zxvf avocado-36.0-tar.gz
- cd avocado-36.0lts
- sudo pip install -r requirements-travis.txt
- sudo -H pip install -r requirements.txt
- sudo python setup.py install
- cd ../falco
before_script:
- export KERNELDIR=/lib/modules/$(ls /lib/modules | sort | head -1)/build
- export KERNELDIR=/lib/modules/$(uname -r)/build
script:
- set -e
- export CC="gcc-4.8"
@@ -32,10 +35,13 @@ 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 ..
- cp falco*.deb ../docker/local
- cd ../docker/local
- docker build -t sysdig/falco:test .
- cd ../..
- sudo test/run_regression_tests.sh $TRAVIS_BRANCH
notifications:
webhooks:

View File

@@ -2,6 +2,87 @@
This file documents all notable changes to Falco. The release numbering uses [semantic versioning](http://semver.org).
## v0.7.0
Released 2016-05-30
### Major Changes
* Update the priorities of falco rules to use a wider range of priorities rather than just ERROR/WARNING. More info on the use of priorities in the ruleset can be found [here](https://github.com/draios/falco/wiki/Falco-Rules#rule-priorities). [[#244](https://github.com/draios/falco/pull/244)]
### Minor Changes
None.
### Bug Fixes
* Fix typos in various markdown files. Thanks @sublimino! [[#241](https://github.com/draios/falco/pull/241)]
### Rule Changes
* Add gitlab-mon as a gitlab binary, which allows it to run shells, etc. Thanks @dkerwin! [[#237](https://github.com/draios/falco/pull/237)]
* A new rule Terminal shell in container" that looks for shells spawned in a container with an attached terminal. [[#242](https://github.com/draios/falco/pull/242)]
* Fix some FPs related to the sysdig monitor agent. [[#243](https://github.com/draios/falco/pull/243)]
* Fix some FPs related to stating containers combined with missed events [[#243](https://github.com/draios/falco/pull/243)]
## v0.6.1
Released 2016-05-15
### Major Changes
None
### Minor Changes
* Small changes to token bucket used to throttle falco events [[#234](https://github.com/draios/falco/pull/234)] [[#235](https://github.com/draios/falco/pull/235)] [[#236](https://github.com/draios/falco/pull/236)] [[#238](https://github.com/draios/falco/pull/238)]
### Bug Fixes
* Update the falco driver to work with kernel 4.11 [[#829](https://github.com/draios/sysdig/pull/829)]
### Rule Changes
* Don't allow apache2 to spawn shells in containers [[#231](https://github.com/draios/falco/issues/231)] [[#232](https://github.com/draios/falco/pull/232)]
## v0.6.0
Released 2016-03-29
### Major Changes
* Add the notion of tagged falco rules. Full documentation for this feature is available on the [wiki](https://github.com/draios/falco/wiki/Falco-Rules#rule-tags). [[#58](https://github.com/draios/falco/issues/58)] [[#59](https://github.com/draios/falco/issues/59)] [[#60](https://github.com/draios/falco/issues/60)] [[#206](https://github.com/draios/falco/pull/206)]
* Falco now has its own dedicated kernel module. Previously, it would depend on sysdig being installed and would use sysdig's `sysdig-probe` kernel module. This ensures you can upgrade sysdig and falco without kernel driver compatibility problems. More details on the kernel module and its installation are on the [wiki](https://github.com/draios/falco/wiki/Falco-Kernel-Module). [[#215](https://github.com/draios/falco/issues/215)] [[#223](https://github.com/draios/falco/issues/223)] [[#224](https://github.com/draios/falco/pull/224)]
* When providing multiple rules files by specifying `-r' multiple times, make sure that you can override rules/lists/macros. Previously, a list/macro/rule specified in an earlier file could not be overridden in a later file. [[#176](https://github.com/draios/falco/issues/176)] [[#177](https://github.com/draios/falco/pull/177)]
* Add example k8s yaml files that show how to run falco as a k8s DaemonSet, and how to run falco-event-generator as a deployment running on one node. [[#222](https://github.com/draios/falco/pull/222)] [[#225](https://github.com/draios/falco/issues/225)] [[#226](https://github.com/draios/falco/pull/226)]
* Update third party libraries to address security vulnerabilities. [[#182](https://github.com/draios/falco/pull/182)]
* Falco can now be built on OSX. Like sysdig, on OSX it is limited to reading existing trace files. [[#210](https://github.com/draios/falco/pull/210)]
### Minor Changes
* Several changes to [falco-event-generator](https://github.com/draios/falco/wiki/Generating-Sample-Events) to improve usability. [[#205](https://github.com/draios/falco/pull/205)]
* Switch to a formatter cache provided by sysdig code instead of using our own. [[#212](https://github.com/draios/falco/pull/212)]
* Add automated tests that use locally-built docker images. [[#188](https://github.com/draios/falco/issues/188)]
### Bug Fixes
* Make sure output strings are not truncated when a given %field expression has a NULL value. [[#180](https://github.com/draios/falco/issues/180)] [[#181](https://github.com/draios/falco/pull/181)]
* Allow ASSERTs when running travisci tests. [[#199](https://github.com/draios/falco/pull/199)]
* Fix make dependencies for lyaml. [[#204](https://github.com/draios/falco/pull/204)] [[#130](https://github.com/draios/falco/issues/130)]
* (This was a change in sysdig, but affected falco). Prevent hangs when traversing malformed parent thread state. [[#208](https://github.com/draios/falco/issues/208)]
### Rule Changes
* Add confd as a program that can write files below /etc and fleetctl as a program that can spawn shells. [[#175](https://github.com/draios/falco/pull/175)]
* Add [exechealthz](https://github.com/kubernetes/contrib/tree/master/exec-healthz), a k8s liveness checking utility, to the list of shell spawners. [[#190](https://github.com/draios/falco/pull/190)]
* Eliminate FPs related to weekly ubuntu cron jobs. [[#192](https://github.com/draios/falco/pull/192)]
* Allow shells spawned by ansible, and eliminate FPs when managing machines via ansible. [[#193](https://github.com/draios/falco/pull/193)] [[#196](https://github.com/draios/falco/pull/196)] [[#202](https://github.com/draios/falco/pull/202)]
* Eliminate FPs related to use of other security products. Thanks to @juju4 for the useful rule updates. [[#200](https://github.com/draios/falco/pull/200)]
* Add additional possible locations for denyhosts, add [PM2](http://pm2.keymetrics.io/) as a shell spawner. [[#202](https://github.com/draios/falco/pull/202)]
* Add flanneld as a privileged container, improve grouping for the "x running y" macros, allow denyhosts to spawn shells. [[#207](https://github.com/draios/falco/pull/207)]
* Handle systemd changing its name to "(systemd)", add sv (part of [runit](http://smarden.org/runit/)) as a program that can write below /etc, allow writing to all `/dev/tty*` files. [[#209](https://github.com/draios/falco/pull/209)]
* Add erl_child_setup as a shell spawner. Thanks to @dkerwin for the useful rule updates. [[#218](https://github.com/draios/falco/pull/218)] [[#221](https://github.com/draios/falco/pull/221)]
* Add support for gitlab omnibus containers/pods. Thanks to @dkerwin for the useful rule updates. [[#220](https://github.com/draios/falco/pull/220)]
## v0.5.0
Released 2016-12-22

View File

@@ -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}")
@@ -27,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}")
@@ -37,13 +41,19 @@ endif()
set(PACKAGE_NAME "falco")
set(PROBE_VERSION "${FALCO_VERSION}")
set(PROBE_NAME "sysdig-probe")
set(PROBE_DEVICE_NAME "sysdig")
set(CMAKE_INSTALL_PREFIX /usr)
set(PROBE_NAME "falco-probe")
set(PROBE_DEVICE_NAME "falco")
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX /usr CACHE PATH "Default install path" FORCE)
endif()
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)
@@ -51,7 +61,7 @@ option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system
#
# zlib
#
option(USE_BUNDLED_ZLIB "Enable building of the bundled zlib" ${USE_BUNDLED_DEPS})
if(NOT USE_BUNDLED_ZLIB)
@@ -99,6 +109,7 @@ else()
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()
@@ -204,8 +215,8 @@ else()
message(STATUS "Using bundled openssl in '${OPENSSL_BUNDLE_DIR}'")
ExternalProject_Add(openssl
URL "http://download.draios.com/dependencies/openssl-1.0.2d.tar.gz"
URL_MD5 "38dd619b2e77cbac69b99f52a053d25a"
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
@@ -235,8 +246,8 @@ else()
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
@@ -292,14 +303,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()
#
@@ -318,15 +333,22 @@ if(NOT USE_BUNDLED_LIBYAML)
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
URL_MD5 "4a4bced818da0b9ae7fc8ebc690792a7"
BUILD_COMMAND ${CMD_MAKE}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./bootstrap && ./configure
INSTALL_COMMAND "")
INSTALL_COMMAND "")
endif()
#
@@ -347,7 +369,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 ${LYAML_DEPENDENCIES}
URL "http://download.draios.com/dependencies/lyaml-release-v6.0.tar.gz"
URL_MD5 "dc3494689a0dce7cf44e7a99c72b1f30"
BUILD_COMMAND ${CMD_MAKE}
@@ -359,13 +389,16 @@ 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")
add_subdirectory(scripts)
set(FALCO_SINSP_LIBRARY sinsp)
set(FALCO_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/falco)
set(FALCO_BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
add_subdirectory(scripts)
add_subdirectory(userspace/engine)
add_subdirectory(userspace/falco)
@@ -385,12 +418,12 @@ set(CPACK_GENERATOR DEB RPM TGZ)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig <support@sysdig.com>")
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.sysdig.org")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "sysdig")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_SOURCE_DIR}/scripts/debian/postinst;${PROJECT_SOURCE_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "dkms (>= 2.1.0.0)")
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm")
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org")
set(CPACK_RPM_PACKAGE_REQUIRES "sysdig")
set(CPACK_RPM_PACKAGE_REQUIRES "dkms, gcc, make, kernel-devel, perl")
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall")
set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall")
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postuninstall")

View File

@@ -1,8 +1,8 @@
# Sysdig Falco
####Latest release
#### Latest release
**v0.5.0**
**v0.7.0**
Read the [change log](https://github.com/draios/falco/blob/dev/CHANGELOG.md)
Dev Branch: [![Build Status](https://travis-ci.org/draios/falco.svg?branch=dev)](https://travis-ci.org/draios/falco)<br />
@@ -29,13 +29,13 @@ One of the questions we often get when we talk about Sysdig Falco is “How does
Documentation
---
[Visit the wiki] (https://github.com/draios/falco/wiki) for full documentation on falco.
[Visit the wiki](https://github.com/draios/falco/wiki) for full documentation on falco.
Join the Community
---
* Contact the [official mailing list] (https://groups.google.com/forum/#!forum/falco) for support and to talk with other users.
* Follow us on [Twitter] (https://twitter.com/sysdig) for general falco and sysdig news.
* This is our [blog] (https://sysdig.com/blog/), where you can find the latest [falco](https://sysdig.com/blog/tag/falco/) posts.
* Contact the [official mailing list](https://groups.google.com/forum/#!forum/falco) for support and to talk with other users.
* Follow us on [Twitter](https://twitter.com/sysdig) for general falco and sysdig news.
* This is our [blog](https://sysdig.com/blog/), where you can find the latest [falco](https://sysdig.com/blog/tag/falco/) posts.
* Join our [Public Slack](https://sysdig.slack.com) channel for sysdig and falco announcements and discussions.
License Terms
@@ -44,7 +44,7 @@ Falco is licensed to you under the [GPL 2.0](./COPYING) open source license.
Contributor License Agreements
---
###Background
### Background
As we did for sysdig, we are formalizing the way that we accept contributions of code from the contributing community. We must now ask that contributions to falco be provided subject to the terms and conditions of a [Contributor License Agreement (CLA)](./cla). The CLA comes in two forms, applicable to contributions by individuals, or by legal entities such as corporations and their employees. We recognize that entering into a CLA with us involves real consideration on your part, and weve tried to make this process as clear and simple as possible.
Weve modeled our CLA off of industry standards, such as [the CLA used by Kubernetes](https://github.com/kubernetes/kubernetes/blob/master/CONTRIBUTING.md). Note that this agreement is not a transfer of copyright ownership, this simply is a license agreement for contributions, intended to clarify the intellectual property license granted with contributions from any person or entity. It is for your protection as a contributor as well as the protection of falco; it does not change your rights to use your own contributions for any other purpose.
@@ -57,7 +57,7 @@ Contributor License Agreements
As always, we are grateful for your past and present contributions to falco.
###What do I need to do in order to contribute code?
### What do I need to do in order to contribute code?
**Individual contributions**: Individuals who wish to make contributions must review the [Individual Contributor License Agreement](./cla/falco_contributor_agreement.txt) and indicate agreement by adding the following line to every GIT commit message:

View File

@@ -11,7 +11,7 @@ if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
done
/usr/bin/sysdig-probe-loader
/usr/bin/falco-probe-loader
fi
exec "$@"

View File

@@ -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
View 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 \
dkms && 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"]

View 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/falco-probe-loader
fi
exec "$@"

View File

@@ -11,7 +11,7 @@ if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then
ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i
done
/usr/bin/sysdig-probe-loader
/usr/bin/falco-probe-loader
fi
exec "$@"

View File

@@ -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.

View File

@@ -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
args: [ "/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

View File

@@ -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

View File

@@ -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

View File

@@ -14,7 +14,7 @@ router.get('/', function(req, res) {
});
router.get('/exec/:cmd', function(req, res) {
var ret = child_process.spawnSync(req.params.cmd);
var ret = child_process.spawnSync(req.params.cmd, { shell: true});
res.send(ret.stdout);
});

View File

@@ -14,10 +14,10 @@
# condition: (syscall.type=read and evt.dir=> and fd.type in (file, directory))
- macro: open_write
condition: (evt.type=open or evt.type=openat) and evt.is_open_write=true and fd.typechar='f'
condition: (evt.type=open or evt.type=openat) and evt.is_open_write=true and fd.typechar='f' and fd.num>=0
- macro: open_read
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f'
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f' and fd.num>=0
- macro: rename
condition: evt.type = rename
@@ -40,17 +40,34 @@
condition: fd.directory in (/bin, /sbin, /usr/bin, /usr/sbin)
- macro: bin_dir_mkdir
condition: evt.arg[0] startswith /bin/ or evt.arg[0] startswith /sbin/ or evt.arg[0] startswith /usr/bin/ or evt.arg[0] startswith /usr/sbin/
condition: >
evt.arg[0] startswith /bin/ or
evt.arg[0] startswith /sbin/ or
evt.arg[0] startswith /usr/bin/ or
evt.arg[0] startswith /usr/sbin/
- macro: bin_dir_rename
condition: evt.arg[1] startswith /bin/ or evt.arg[1] startswith /sbin/ or evt.arg[1] startswith /usr/bin/ or evt.arg[1] startswith /usr/sbin/
condition: >
evt.arg[1] startswith /bin/ or
evt.arg[1] startswith /sbin/ or
evt.arg[1] startswith /usr/bin/ or
evt.arg[1] startswith /usr/sbin/
- macro: etc_dir
condition: fd.name startswith /etc
- macro: ubuntu_so_dirs
condition: fd.name startswith /lib/x86_64-linux-gnu or fd.name startswith /usr/lib/x86_64-linux-gnu or fd.name startswith /usr/lib/sudo
condition: >
fd.name startswith /lib/x86_64-linux-gnu or
fd.name startswith /usr/lib/x86_64-linux-gnu or
fd.name startswith /usr/lib/sudo
- macro: centos_so_dirs
condition: fd.name startswith /lib64 or fd.name startswith /usr/lib64 or fd.name startswith /usr/libexec
condition: >
fd.name startswith /lib64 or
fd.name startswith /usr/lib64 or
fd.name startswith /usr/libexec
- macro: linux_so_dirs
condition: ubuntu_so_dirs or centos_so_dirs or fd.name=/etc/ld.so.cache
@@ -76,7 +93,10 @@
# 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
@@ -87,7 +107,8 @@
gpasswd, chfn, expiry, passwd, vigr, cpgr
]
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' | awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
# repoquery -l shadow-utils | grep bin | xargs ls -ld | grep -v '^d' |
# awk '{print $9}' | xargs -L 1 basename | tr "\\n" ","
- list: shadowutils_binaries
items: [
chage, gpasswd, lastlog, newgrp, sg, adduser, deluser, chpasswd,
@@ -99,24 +120,49 @@
items: [setup-backend, dragent, sdchecks]
- list: docker_binaries
items: [docker, dockerd, exe]
items: [docker, dockerd, exe, docker-compose, docker-entrypoi]
- list: k8s_binaries
items: [hyperkube, skydns, kube2sky, exechealthz]
- list: lxd_binaries
items: [lxd, lxcfs]
# Utility/etc programs known to run on mesos slaves. Truncation
# intentional.
- list: mesos_slave_binaries
items: [mesos-health-ch, mesos-docker-ex, mesos-agent, mesos-logrotate, mesos-fetcher]
- list: http_server_binaries
items: [nginx, httpd, httpd-foregroun, lighttpd]
- list: db_server_binaries
items: [mysqld]
- list: gitlab_binaries
items: [gitlab-shell, gitlab-mon, gitlab-runner-b, git]
- macro: server_procs
condition: proc.name in (http_server_binaries, db_server_binaries, docker_binaries, sshd)
# The explicit quotes are needed to avoid the - characters being
# interpreted by the filter expression.
- list: rpm_binaries
items: [dnf, rpm, rpmkey, yum, '"75-system-updat"']
- macro: rpm_procs
condition: proc.name in (rpm_binaries)
- list: deb_binaries
items: [dpkg, dpkg-preconfigu, apt, apt-get, aptitude,
frontend, preinst, add-apt-reposit, apt-auto-remova, apt-key,
apt-listchanges, unattended-upgr
]
# The truncated dpkg-preconfigu is intentional, process names are
# truncated at the sysdig level.
- list: package_mgmt_binaries
items: [dpkg, dpkg-preconfigu, dnf, rpm, rpmkey, yum, frontend]
items: [rpm_binaries, deb_binaries, update-alternat]
- macro: package_mgmt_procs
condition: proc.name in (package_mgmt_binaries)
@@ -135,14 +181,35 @@
- list: user_mgmt_binaries
items: [login_binaries, passwd_binaries, shadowutils_binaries]
- list: dev_creation_binaries
items: [blkid, rename_device]
- 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]
- list: make_binaries
items: [make, gmake, cmake]
- 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))
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))
# Indicates that the process is new. Currently detected using time
# since process was started, using a threshold of 5 seconds.
@@ -153,7 +220,8 @@
- macro: inbound
condition: ((evt.type=listen and evt.dir=>) or (evt.type=accept and evt.dir=<))
# Currently sendto is an ignored syscall, otherwise this could also check for (evt.type=sendto and evt.dir=>)
# Currently sendto is an ignored syscall, otherwise this could also
# check for (evt.type=sendto and evt.dir=>)
- macro: outbound
condition: evt.type=connect and evt.dir=< and (fd.typechar=4 or fd.typechar=6)
@@ -162,7 +230,10 @@
# Ssh
- macro: ssh_error_message
condition: (evt.arg.data contains "Invalid user" or evt.arg.data contains "preauth" or evt.arg.data contains "Failed password")
condition: >
(evt.arg.data contains "Invalid user" or
evt.arg.data contains "preauth" or
evt.arg.data contains "Failed password")
# System
- macro: modules
@@ -178,18 +249,67 @@
# the command line.
- macro: container
condition: container.id != host
- macro: interactive
condition: ((proc.aname=sshd and proc.name != sshd) or proc.name=systemd-logind or proc.name=login)
condition: >
((proc.aname=sshd and proc.name != sshd) or
proc.name=systemd-logind or proc.name=login)
- macro: syslog
condition: fd.name in (/dev/log, /run/systemd/journal/syslog)
- list: cron_binaries
items: [cron, crond]
items: [anacron, cron, crond]
# System users that should never log into a system. Consider adding your own
# service users (e.g. 'apache' or 'mysqld') here.
- 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_python_running_sdchecks
condition: >
(proc.name in (python, python2.7) and
(proc.cmdline contains /opt/draios/bin/sdchecks))
- macro: parent_bro_running_python
condition: (proc.pname=python and proc.cmdline contains /usr/share/broctl)
- macro: parent_java_running_jenkins
condition: >
(proc.pname=java and proc.pcmdline contains jenkins.war
or proc.pcmdline contains /tmp/slave.jar)
# 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-
- macro: java_running_sdjagent
condition: proc.name=java and proc.cmdline contains sdjagent.jar
###############
# General Rules
@@ -198,65 +318,115 @@
- rule: Write below binary dir
desc: an attempt to write to any file below a set of binary directories
condition: bin_dir and evt.dir = < and open_write and not package_mgmt_procs
output: "File below a known binary directory opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
output: >
File below a known binary directory opened for writing (user=%user.name
command=%proc.cmdline file=%fd.name)
priority: ERROR
tags: [filesystem]
- 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, confd)
and not proc.name in (passwd_binaries, shadowutils_binaries, sysdigcloud_binaries,
package_mgmt_binaries, ssl_mgmt_binaries, dhcp_binaries,
dev_creation_binaries,
ldconfig.real, ldconfig, confd, gpg, insserv,
apparmor_parser, update-mime, tzdata.config, tzdata.postinst,
systemd-machine, debconf-show, rollerd, bind9.postinst, sv,
gen_resolvconf.)
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
condition: write_etc_common and not proc.sname=fbash
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name)"
priority: WARNING
priority: ERROR
tags: [filesystem]
# Within a fbash session, the severity is lowered to INFO
- rule: Write below etc in installer
desc: an attempt to write to any file below /etc, in a pipe installer session
condition: write_etc_common and proc.sname=fbash
output: "File below /etc opened for writing (user=%user.name command=%proc.cmdline file=%fd.name) within pipe installer session"
output: >
File below /etc opened for writing (user=%user.name command=%proc.cmdline
file=%fd.name) within pipe installer session
priority: INFO
tags: [filesystem]
- 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.
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)"
output: >
Sensitive file opened for reading by trusted program after startup (user=%user.name
command=%proc.cmdline file=%fd.name)
priority: WARNING
tags: [filesystem]
- 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
output: "Sensitive file opened for reading by non-trusted program (user=%user.name name=%proc.name command=%proc.cmdline file=%fd.name)"
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, 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
tags: [filesystem]
# 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 (dnf,rpm,rpmkey,yum)
condition: fd.name startswith /var/lib/rpm and open_write and not rpm_procs and not ansible_running_python
output: "Rpm database opened for writing by a non-rpm program (command=%proc.cmdline file=%fd.name)"
priority: WARNING
priority: ERROR
tags: [filesystem, software_mgmt]
- rule: DB program spawned process
desc: a database-server related program spawned a new process other than itself. This shouldn\'t occur and is a follow on from some SQL injection attacks.
desc: >
a database-server related program spawned a new process other than itself.
This shouldn\'t occur and is a follow on from some SQL injection attacks.
condition: proc.pname in (db_server_binaries) and spawned_process and not proc.name in (db_server_binaries)
output: "Database-related program spawned process other than itself (user=%user.name program=%proc.cmdline parent=%proc.pname)"
priority: WARNING
output: >
Database-related program spawned process other than itself (user=%user.name
program=%proc.cmdline parent=%proc.pname)
priority: NOTICE
tags: [process, database]
- rule: Modify binary dirs
desc: an attempt to modify any file below a set of binary directories.
condition: bin_dir_rename and modify and not package_mgmt_procs
output: "File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline operation=%evt.type file=%fd.name %evt.args)"
priority: WARNING
output: >
File below known binary directory renamed/removed (user=%user.name command=%proc.cmdline
operation=%evt.type file=%fd.name %evt.args)
priority: ERROR
tags: [filesystem]
- rule: Mkdir binary dirs
desc: an attempt to create a directory below a set of binary directories.
condition: mkdir and bin_dir_mkdir and not package_mgmt_procs
output: "Directory below known binary directory created (user=%user.name command=%proc.cmdline directory=%evt.arg.path)"
priority: WARNING
output: >
Directory below known binary directory created (user=%user.name
command=%proc.cmdline directory=%evt.arg.path)
priority: ERROR
tags: [filesystem]
# Don't load shared objects coming from unexpected places
# Commenting this out for now--there are lots of shared library
@@ -269,45 +439,99 @@
# Temporarily disabling this rule as it's tripping over https://github.com/draios/sysdig/issues/598
# - rule: Syscall returns eaccess
# desc: any system call that returns EACCESS. This is not always a strong indication of a problem, hence the INFO priority.
# desc: >
# any system call that returns EACCESS. This is not always a strong
# indication of a problem, hence the INFO priority.
# condition: evt.res = EACCESS
# output: "System call returned EACCESS (user=%user.name command=%proc.cmdline syscall=%evt.type args=%evt.args)"
# output: >
# System call returned EACCESS (user=%user.name command=%proc.cmdline
# syscall=%evt.type args=%evt.args)
# priority: INFO
- 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)
output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline parent=%proc.pname %container.info)"
priority: WARNING
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, lxd_binaries, sysdigcloud_binaries, sysdig, nsenter)
and not proc.name startswith "runc:"
and not proc.pname in (sysdigcloud_binaries)
and not java_running_sdjagent
output: >
Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline
parent=%proc.pname %container.info)
priority: NOTICE
tags: [process]
- list: known_shell_spawn_binaries
items: [
sshd, sudo, su, tmux, screen, emacs, systemd, login, flock, fbash,
nginx, monit, supervisord, dragent, aws, initdb, docker-compose,
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, erl_child_setup,
npm, cloud-init, toybox, ceph
]
- 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, fail2ban-server, apt-get, apt, fleetctl)
output: "Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)"
priority: WARNING
condition: >
spawned_process and not container
and shell_procs
and proc.pname exists
and not proc.pname in (cron_binaries, shell_binaries, make_binaries, known_shell_spawn_binaries, docker_binaries,
k8s_binaries, package_mgmt_binaries, aide_wrapper_binaries, nids_binaries,
monitoring_binaries, gitlab_binaries, mesos_slave_binaries)
and not parent_ansible_running_python
and not parent_bro_running_python
and not parent_python_running_denyhosts
and not parent_python_running_sdchecks
and not parent_linux_image_upgrade_script
and not parent_java_running_jenkins
output: >
Shell spawned by untrusted binary (user=%user.name shell=%proc.name parent=%proc.pname
cmdline=%proc.cmdline pcmdline=%proc.pcmdline)
priority: DEBUG
tags: [host, shell]
- macro: trusted_containers
condition: (container.image startswith sysdig/agent or
container.image startswith sysdig/falco or
(container.image startswith sysdig/falco and
not container.image startswith sysdig/falco-event-generator) or
container.image startswith quay.io/sysdig or
container.image startswith sysdig/sysdig or
container.image startswith gcr.io/google_containers/hyperkube or
container.image startswith gcr.io/google_containers/kube-proxy or
container.image startswith cchh/sysdig)
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 privileged container (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
priority: WARNING
# These containers are ones that are known to spawn lots of
# shells. Generally, they are for systems where the container is used
# as a packaging mechanism more than for a dedicated microservice.
- macro: shell_spawning_containers
condition: (container.image startswith jenkins or
container.image startswith gitlab/gitlab-ce)
- rule: Launch Privileged Container
desc: Detect the initial process started in a privileged container. Exceptions are made for known trusted images.
condition: evt.type=execve and proc.vpid=1 and container and container.privileged=true and not trusted_containers
output: Privileged container started (user=%user.name command=%proc.cmdline %container.info)
priority: INFO
tags: [container, cis]
- macro: sensitive_mount
condition: (container.mount.dest[/proc*] != "N/A")
- rule: Sensitive Mount by Container
desc: Any open by a container that has a mount from a sensitive host directory (i.e. /proc). Exceptions are made for known trusted images.
condition: (open_read or open_write) and container and sensitive_mount and not trusted_containers
output: File opened for read/write by container mounting sensitive directory (user=%user.name command=%proc.cmdline %container.info file=%fd.name)
priority: WARNING
- rule: Launch Sensitive Mount Container
desc: >
Detect the initial process started by a container that has a mount from a sensitive host directory
(i.e. /proc). Exceptions are made for known trusted images.
condition: evt.type=execve and proc.vpid=1 and container and sensitive_mount and not trusted_containers
output: Container with sensitive mount started (user=%user.name command=%proc.cmdline %container.info)
priority: INFO
tags: [container, cis]
# Anything run interactively by root
# - condition: evt.type != switch and user.name = root and proc.name != sshd and interactive
@@ -318,20 +542,66 @@
desc: an attempt to run interactive commands by a system (i.e. non-login) user
condition: spawned_process and system_users and interactive
output: "System user ran an interactive command (user=%user.name command=%proc.cmdline)"
priority: WARNING
priority: INFO
tags: [users]
- rule: Terminal shell in container
desc: A shell was spawned by a program in a container with an attached terminal.
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
output: >
A shell was spawned in a container with an attached terminal (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)
priority: NOTICE
tags: [container, shell]
# For some container types (mesos), there isn't a container image to
# work with, and the container name is autogenerated, so there isn't
# any stable aspect of the software to work with. In this case, we
# fall back to allowing certain command lines.
- list: known_container_shell_spawn_cmdlines
items: [
'"bash -c curl -f localhost:$API_PORT/admin/healthcheck"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null "',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null"',
'"sh -c curl http://localhost:6060/debug/vars>/dev/null "',
'"sh -c pgrep java && exit 0 || exit 1 "',
'"sh -c uname -p 2> /dev/null"',
'"sh -c echo healthy "',
'"sh -c echo alive "'
]
- 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)
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
condition: >
spawned_process and container
and shell_procs
and proc.pname exists
and not proc.pname in (shell_binaries, make_binaries, docker_binaries, k8s_binaries,
lxd_binaries, mesos_slave_binaries, aide_wrapper_binaries, nids_binaries,
monitoring_binaries, gitlab_binaries, initdb, pg_ctl, awk, falco, cron,
erl_child_setup, ceph, PM2)
and not trusted_containers
and not shell_spawning_containers
and not proc.cmdline in (known_container_shell_spawn_cmdlines)
output: >
Shell spawned in a container other than entrypoint (user=%user.name %container.info
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
priority: NOTICE
tags: [container, shell]
# sockfamily ip is to exclude certain processes (like 'groups') that communicate on unix-domain sockets
# systemd can listen on ports to launch things like sshd on demand
- rule: System procs network activity
desc: any network activity performed by system binaries that are not expected to send or receive any network traffic
condition: (fd.sockfamily = ip and system_procs) and (inbound or outbound)
output: "Known system binary sent/received network traffic (user=%user.name command=%proc.cmdline connection=%fd.name)"
priority: WARNING
condition: (fd.sockfamily = ip and system_procs) and (inbound or outbound) and not proc.name=systemd
output: >
Known system binary sent/received network traffic
(user=%user.name command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network]
# With the current restriction on system calls handled by falco
# (e.g. excluding read/write/sendto/recvfrom/etc, this rule won't
@@ -342,44 +612,84 @@
# output: "sshd sent error message to syslog (error=%evt.buffer)"
# priority: WARNING
- macro: somebody_becoming_themself
condition: ((user.name=nobody and evt.arg.uid=nobody) or
(user.name=www-data and evt.arg.uid=www-data))
# 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)"
priority: WARNING
desc: >
an attempt to change users by calling setuid. sudo/su are excluded. users "root" and "nobody"
suing to itself are also excluded, as setuid calls typically involve dropping privileges.
condition: >
evt.type=setuid and evt.dir=> and
not user.name=root and not somebody_becoming_themself
and not proc.name in (userexec_binaries, mail_binaries, docker_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: NOTICE
tags: [users]
- rule: User mgmt binaries
desc: activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded. Activity in containers is also excluded--some containers create custom users on top of a base linux distribution at startup.
condition: spawned_process and proc.name in (user_mgmt_binaries) and not proc.name in (su, sudo) and not container and not proc.pname in (cron_binaries, systemd, run-parts)
output: "User management binary command run outside of container (user=%user.name command=%proc.cmdline parent=%proc.pname)"
priority: WARNING
desc: >
activity by any programs that can manage users, passwords, or permissions. sudo and su are excluded.
Activity in containers is also excluded--some containers create custom users on top
of a base linux distribution at startup.
condition: >
spawned_process and proc.name in (user_mgmt_binaries) and
not proc.name in (su, sudo) and not container and
not proc.pname in (cron_binaries, systemd, run-parts)
output: >
User management binary command run outside of container
(user=%user.name command=%proc.cmdline parent=%proc.pname)
priority: NOTICE
tags: [host, users]
# (we may need to add additional checks against false positives, see: https://bugs.launchpad.net/ubuntu/+source/rkhunter/+bug/86153)
- list: allowed_dev_files
items: [
/dev/null, /dev/stdin, /dev/stdout, /dev/stderr,
/dev/random, /dev/urandom, /dev/console, /dev/kmsg
]
# (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)
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
priority: ERROR
tags: [filesystem]
# fbash is a small shell script that runs bash, and is suitable for use in curl <curl> | fbash installers.
- rule: Installer bash starts network server
desc: an attempt by a program in a pipe installer session to start listening for network connections
condition: evt.type=listen and proc.sname=fbash
output: "Unexpected listen call by a process in a fbash session (command=%proc.cmdline)"
priority: WARNING
priority: NOTICE
tags: [network]
- rule: Installer bash starts session
desc: an attempt by a program in a pipe installer session to start a new session
condition: evt.type=setsid and proc.sname=fbash
output: "Unexpected setsid call by a process in fbash session (command=%proc.cmdline)"
priority: WARNING
priority: NOTICE
tags: [process]
- rule: Installer bash non https connection
desc: an attempt by a program in a pipe installer session to make an outgoing connection on a non-http(s) port
condition: proc.sname=fbash and outbound and not fd.sport in (80, 443, 53)
output: "Outbound connection on non-http(s) port by a process in a fbash session (command=%proc.cmdline connection=%fd.name)"
priority: WARNING
output: >
Outbound connection on non-http(s) port by a process in a fbash session
(command=%proc.cmdline connection=%fd.name)
priority: NOTICE
tags: [network]
# It'd be nice if we could warn when processes in a fbash session try
# to download from any nonstandard location? This is probably blocked
@@ -393,6 +703,7 @@
condition: evt.type=execve and proc.name in (chkconfig, systemctl) and proc.sname=fbash
output: "Service management program run by process in a fbash session (command=%proc.cmdline)"
priority: INFO
tags: [software_mgmt]
# Notice when processes try to run any package management binary within a fbash session.
# Note: this is not a WARNING, as you'd expect some package management
@@ -402,6 +713,7 @@
condition: evt.type=execve and package_mgmt_procs and proc.sname=fbash
output: "Package management program run by process in a fbash session (command=%proc.cmdline)"
priority: INFO
tags: [software_mgmt]
###########################
# Application-Related Rules
@@ -469,7 +781,10 @@
- macro: cassandra_jmx_port
condition: fd.sport=7199
- macro: cassandra_port
condition: cassandra_thrift_client_port or cassandra_cql_port or cassandra_cluster_port or cassandra_ssl_cluster_port or cassandra_jmx_port
condition: >
cassandra_thrift_client_port or
cassandra_cql_port or cassandra_cluster_port or
cassandra_ssl_cluster_port or cassandra_jmx_port
# - rule: Cassandra unexpected network inbound traffic
# desc: inbound network traffic to cassandra on a port other than the standard ports
@@ -555,7 +870,9 @@
# - rule: Mongodb unexpected network inbound traffic
# desc: inbound network traffic to mongodb on a port other than the standard ports
# condition: user.name = mongodb and inbound and not (mongodb_server_port or mongodb_shardserver_port or mongodb_configserver_port or mongodb_webserver_port)
# condition: >
# user.name = mongodb and inbound and not (mongodb_server_port or
# mongodb_shardserver_port or mongodb_configserver_port or mongodb_webserver_port)
# output: "Inbound network traffic to MongoDB on unexpected port (connection=%fd.name)"
# priority: WARNING

View File

@@ -1,5 +1,14 @@
configure_file(debian/postinst.in debian/postinst)
configure_file(debian/prerm.in debian/prerm)
file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco"
DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian")
file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco"
DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm")
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
install(PROGRAMS ${SYSDIG_DIR}/scripts/sysdig-probe-loader
DESTINATION ${FALCO_BIN_DIR}
RENAME falco-probe-loader)
endif()

View File

@@ -1,9 +0,0 @@
#!/bin/sh
set -e
NAME=falco
if [ -x "/etc/init.d/$NAME" ]; then
update-rc.d $NAME defaults >/dev/null
fi

32
scripts/debian/postinst.in Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
set -e
DKMS_PACKAGE_NAME="@PACKAGE_NAME@"
DKMS_VERSION="@PROBE_VERSION@"
NAME="@PACKAGE_NAME@"
postinst_found=0
case "$1" in
configure)
for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do
if [ -f $DKMS_POSTINST ]; then
$DKMS_POSTINST $DKMS_PACKAGE_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2
postinst_found=1
break
fi
done
if [ "$postinst_found" -eq 0 ]; then
echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not"
echo "built with legacy DKMS support."
echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst"
echo "support or upgrade DKMS to a more current version."
exit 1
fi
;;
esac
if [ -x "/etc/init.d/$NAME" ]; then
update-rc.d $NAME defaults >/dev/null
fi

View File

@@ -1,13 +0,0 @@
#!/bin/sh
set -e
NAME=falco
if [ -x "/etc/init.d/$NAME" ]; then
if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
invoke-rc.d $NAME stop || exit $?
else
/etc/init.d/$NAME stop || exit $?
fi
fi

23
scripts/debian/prerm.in Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/sh
set -e
NAME="@PACKAGE_NAME@"
if [ -x "/etc/init.d/$NAME" ]; then
if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
invoke-rc.d $NAME stop || exit $?
else
/etc/init.d/$NAME stop || exit $?
fi
fi
DKMS_PACKAGE_NAME="@PACKAGE_NAME@"
DKMS_VERSION="@PROBE_VERSION@"
case "$1" in
remove|upgrade|deconfigure)
if [ "$(dkms status -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION)" ]; then
dkms remove -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION --all
fi
;;
esac

View File

@@ -1 +1,15 @@
dkms add -m falco -v %{version} --rpm_safe_upgrade
if [ `uname -r | grep -c "BOOT"` -eq 0 ] && [ -e /lib/modules/`uname -r`/build/include ]; then
dkms build -m falco -v %{version}
dkms install --force -m falco -v %{version}
elif [ `uname -r | grep -c "BOOT"` -gt 0 ]; then
echo -e ""
echo -e "Module build for the currently running kernel was skipped since you"
echo -e "are running a BOOT variant of the kernel."
else
echo -e ""
echo -e "Module build for the currently running kernel was skipped since the"
echo -e "kernel source for this kernel does not seem to be installed."
fi
/sbin/chkconfig --add falco

View File

@@ -2,3 +2,5 @@ if [ $1 = 0 ]; then
/sbin/service falco stop > /dev/null 2>&1
/sbin/chkconfig --del falco
fi
dkms remove -m falco -v %{version} --all --rpm_safe_upgrade

View File

@@ -4,6 +4,9 @@ import os
import re
import json
import sets
import glob
import shutil
import subprocess
from avocado import Test
from avocado.utils import process
@@ -21,9 +24,9 @@ class FalcoTest(Test):
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', '*')
self.trace_file = self.params.get('trace_file', '*', default='')
if not os.path.isabs(self.trace_file):
if self.trace_file and not os.path.isabs(self.trace_file):
self.trace_file = os.path.join(self.basedir, self.trace_file)
self.json_output = self.params.get('json_output', '*', default=False)
@@ -43,6 +46,8 @@ class FalcoTest(Test):
if not os.path.isabs(self.conf_file):
self.conf_file = os.path.join(self.basedir, self.conf_file)
self.run_duration = self.params.get('run_duration', '*', default='')
self.disabled_rules = self.params.get('disabled_rules', '*', default='')
if self.disabled_rules == '':
@@ -56,6 +61,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()
@@ -79,15 +94,23 @@ class FalcoTest(Test):
if not isinstance(self.detect_level, list):
self.detect_level = [self.detect_level]
# Doing this in 2 steps instead of simply using
# module_is_loaded to avoid logging lsmod output to the log.
lsmod_output = process.system_output("lsmod", verbose=False)
self.package = self.params.get('package', '*', default='None')
if linux_modules.parse_lsmod_for_module(lsmod_output, 'sysdig_probe') == {}:
self.log.debug("Loading sysdig kernel module")
process.run('sudo insmod {}/driver/sysdig-probe.ko'.format(self.falcodir))
if self.package == 'None':
# Doing this in 2 steps instead of simply using
# module_is_loaded to avoid logging lsmod output to the log.
lsmod_output = process.system_output("lsmod", verbose=False)
self.str_variant = self.trace_file
if linux_modules.parse_lsmod_for_module(lsmod_output, 'falco_probe') == {}:
self.log.debug("Loading falco kernel module")
process.run('insmod {}/driver/falco-probe.ko'.format(self.falcodir), sudo=True)
self.addl_docker_run_args = self.params.get('addl_docker_run_args', '*', default='')
self.copy_local_driver = self.params.get('copy_local_driver', '*', default=False)
# Used by possibly_copy_local_driver as well as docker run
self.module_dir = os.path.expanduser("~/.sysdig")
self.outputs = self.params.get('outputs', '*', default='')
@@ -101,8 +124,26 @@ class FalcoTest(Test):
output['file'] = item2[0]
output['line'] = item2[1]
outputs.append(output)
filedir = os.path.dirname(output['file'])
# Create the parent directory for the trace file if it doesn't exist.
if not os.path.isdir(filedir):
os.makedirs(filedir)
self.outputs = outputs
self.disable_tags = self.params.get('disable_tags', '*', default='')
if self.disable_tags == '':
self.disable_tags=[]
self.run_tags = self.params.get('run_tags', '*', default='')
if self.run_tags == '':
self.run_tags=[]
def tearDown(self):
if self.package != 'None':
self.uninstall_package()
def check_rules_warnings(self, res):
found_warning = sets.Set()
@@ -161,6 +202,28 @@ 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 = '{}: (\d+)'.format(rule)
match = re.search(expected, triggered_rules)
if match is None:
actual_count = 0
else:
actual_count = int(match.group(1))
if actual_count != count:
self.fail("Different counts for rule {}: expected={}, actual={}".format(rule, count, actual_count))
else:
self.log.debug("Found expected count for rule {}: {}".format(rule, count))
def check_outputs(self):
for output in self.outputs:
# Open the provided file and match each line against the
@@ -189,12 +252,112 @@ class FalcoTest(Test):
if not attr in obj:
self.fail("Falco JSON object {} does not contain property \"{}\"".format(line, attr))
def install_package(self):
if self.package.startswith("docker:"):
image = self.package.split(":", 1)[1]
# Remove an existing falco-test container first. Note we don't check the output--docker rm
# doesn't have an -i equivalent.
res = process.run("docker rm falco-test", ignore_status=True)
rules_dir = os.path.abspath(os.path.join(self.basedir, "./rules"))
conf_dir = os.path.abspath(os.path.join(self.basedir, "../"))
traces_dir = os.path.abspath(os.path.join(self.basedir, "./trace_files"))
self.falco_binary_path = "docker run -i -t --name falco-test --privileged " \
"-v {}:/host/rules -v {}:/host/conf -v {}:/host/traces " \
"-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 {}:/root/.sysdig:ro -v " \
"/usr:/host/usr:ro {} {} falco".format(
rules_dir, conf_dir, traces_dir,
self.module_dir, self.addl_docker_run_args, image)
elif self.package.endswith(".deb"):
self.falco_binary_path = '/usr/bin/falco';
package_glob = "{}/{}".format(self.falcodir, self.package)
matches = glob.glob(package_glob)
if len(matches) != 1:
self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}", package_glob, ",".join(matches))
package_path = matches[0]
cmdline = "dpkg -i {}".format(package_path)
self.log.debug("Installing debian package via \"{}\"".format(cmdline))
res = process.run(cmdline, timeout=120, sudo=True)
def uninstall_package(self):
if self.package.startswith("docker:"):
# Remove the falco-test image. Here we *do* check the return value
res = process.run("docker rm falco-test")
elif self.package.endswith(".deb"):
cmdline = "dpkg -r falco"
self.log.debug("Uninstalling debian package via \"{}\"".format(cmdline))
res = process.run(cmdline, timeout=120, sudo=True)
def possibly_copy_driver(self):
# Remove the contents of ~/.sysdig regardless of
# copy_local_driver.
self.log.debug("Checking for module dir {}".format(self.module_dir))
if os.path.isdir(self.module_dir):
self.log.info("Removing files below directory {}".format(self.module_dir))
for rmfile in glob.glob(self.module_dir + "/*"):
self.log.debug("Removing file {}".format(rmfile))
os.remove(rmfile)
if self.copy_local_driver:
verstr = subprocess.check_output([self.falco_binary_path, "--version"]).rstrip()
self.log.info("verstr {}".format(verstr))
falco_version = verstr.split(" ")[2]
self.log.info("falco_version {}".format(falco_version))
arch = subprocess.check_output(["uname", "-m"]).rstrip()
self.log.info("arch {}".format(arch))
kernel_release = subprocess.check_output(["uname", "-r"]).rstrip()
self.log.info("kernel release {}".format(kernel_release))
# sysdig-probe-loader has a more comprehensive set of ways to
# find the config hash. We only look at /boot/config-<kernel release>
md5_output = subprocess.check_output(["md5sum", "/boot/config-{}".format(kernel_release)]).rstrip()
config_hash = md5_output.split(" ")[0]
probe_filename = "falco-probe-{}-{}-{}-{}.ko".format(falco_version, arch, kernel_release, config_hash)
driver_path = os.path.join(self.falcodir, "driver", "falco-probe.ko")
module_path = os.path.join(self.module_dir, probe_filename)
self.log.debug("Copying {} to {}".format(driver_path, module_path))
shutil.copyfile(driver_path, module_path)
def test(self):
self.log.info("Trace file %s", self.trace_file)
# Run the provided trace file though falco
cmd = '{}/userspace/falco/falco {} {} -c {} -e {} -o json_output={} -v'.format(
self.falcodir, self.rules_args, self.disabled_args, self.conf_file, self.trace_file, self.json_output)
self.falco_binary_path = '{}/userspace/falco/falco'.format(self.falcodir)
self.possibly_copy_driver()
if self.package != 'None':
# This sets falco_binary_path as a side-effect.
self.install_package()
trace_arg = self.trace_file
if self.trace_file:
trace_arg = "-e {}".format(self.trace_file)
# Run falco
cmd = '{} {} {} -c {} {} -o json_output={} -v'.format(
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output)
for tag in self.disable_tags:
cmd += ' -T {}'.format(tag)
for tag in self.run_tags:
cmd += ' -t {}'.format(tag)
if self.run_duration:
cmd += ' -M {}'.format(self.run_duration)
self.falco_proc = process.SubProcess(cmd)
@@ -222,6 +385,8 @@ class FalcoTest(Test):
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

601
test/falco_tests.yaml Normal file
View File

@@ -0,0 +1,601 @@
trace_files: !mux
docker_package:
package: docker:sysdig/falco:test
detect: True
detect_level: WARNING
rules_file: /host/rules/rule_names_with_spaces.yaml
trace_file: /host/traces/cat_write.scap
conf_file: /host/conf/falco.yaml
# This uses a volume mount to overwrite and prevent /usr/sbin/dkms
# from being run. As a result, it will force falco-probe-loader to
# fall back to loading the driver from ~/.sysdig. Setting
# copy_local_driver to True copied the driver to ~/.sysdig, so it
# will be available. In this case, we're running live for 5 seconds
# just to see if falco can load the driver.
docker_package_local_driver:
package: docker:sysdig/falco:test
addl_docker_run_args: -v /dev/null:/usr/sbin/dkms
copy_local_driver: True
detect: False
detect_level: WARNING
rules_file: /host/rules/tagged_rules.yaml
conf_file: /host/conf/falco.yaml
run_duration: 5
debian_package:
package: falco*.deb
detect: True
detect_level: WARNING
rules_file:
- rules/rule_names_with_spaces.yaml
trace_file: trace_files/cat_write.scap
builtin_rules_no_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_warning: False
test_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_file: rules/falco_rules_warnings.yaml
rules_warning:
- no_evttype
- evttype_not_equals
- leading_not
- not_equals_at_end
- not_at_end
- not_before_trailing_evttype
- not_equals_before_trailing_evttype
- not_equals_and_not
- not_equals_before_in
- not_before_in
- not_in_before_in
- leading_in_not_equals_before_evttype
- leading_in_not_equals_at_evttype
- not_with_evttypes
- not_with_evttypes_addl
- not_equals_before_evttype
- not_equals_before_in_evttype
- not_before_evttype
- not_before_evttype_using_in
rules_events:
- no_warnings: [execve]
- no_evttype: [all]
- evttype_not_equals: [all]
- leading_not: [all]
- not_equals_after_evttype: [execve]
- not_after_evttype: [execve]
- leading_trailing_evttypes: [execve,open]
- leading_multtrailing_evttypes: [connect,execve,open]
- leading_multtrailing_evttypes_using_in: [connect,execve,open]
- not_equals_at_end: [all]
- not_at_end: [all]
- not_before_trailing_evttype: [all]
- not_equals_before_trailing_evttype: [all]
- not_equals_and_not: [all]
- not_equals_before_in: [all]
- not_before_in: [all]
- not_in_before_in: [all]
- evttype_in: [execve,open]
- evttype_in_plus_trailing: [connect,execve,open]
- leading_in_not_equals_before_evttype: [all]
- leading_in_not_equals_at_evttype: [all]
- not_with_evttypes: [all]
- not_with_evttypes_addl: [all]
- not_equals_before_evttype: [all]
- not_equals_before_in_evttype: [all]
- not_before_evttype: [all]
- not_before_evttype_using_in: [all]
- repeated_evttypes: [open]
- repeated_evttypes_with_in: [open]
- repeated_evttypes_with_separate_in: [open]
- repeated_evttypes_with_mix: [open]
rule_names_with_spaces:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_names_with_spaces.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_first_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_last_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/single_rule.yaml
- rules/empty_rules.yaml
trace_file: trace_files/cat_write.scap
multiple_rules:
detect: True
detect_level:
- WARNING
- INFO
- ERROR
rules_file:
- rules/single_rule.yaml
- 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
list_substring:
detect: False
rules_file:
- rules/list_substring.yaml
trace_file: trace_files/cat_write.scap
list_sub_front:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_front.yaml
trace_file: trace_files/cat_write.scap
list_sub_mid:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_mid.yaml
trace_file: trace_files/cat_write.scap
list_sub_end:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_end.yaml
trace_file: trace_files/cat_write.scap
list_sub_bare:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_bare.yaml
trace_file: trace_files/cat_write.scap
list_sub_whitespace:
detect: True
detect_level: WARNING
rules_file:
- rules/list_sub_whitespace.yaml
trace_file: trace_files/cat_write.scap
list_order:
detect: True
detect_level: WARNING
rules_file:
- rules/list_order.yaml
trace_file: trace_files/cat_write.scap
macro_order:
detect: True
detect_level: WARNING
rules_file:
- rules/macro_order.yaml
trace_file: trace_files/cat_write.scap
rule_order:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_order.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:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- open_from_cat
trace_file: trace_files/cat_write.scap
disabled_rules_using_regex:
detect: False
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- "open.*"
trace_file: trace_files/cat_write.scap
disabled_rules_using_enabled_flag:
detect: False
rules_file:
- rules/single_rule_enabled_flag.yaml
trace_file: trace_files/cat_write.scap
disabled_and_enabled_rules_1:
exit_status: 1
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
disable_tags: [a]
run_tags: [a]
rules_file:
- rules/single_rule.yaml
trace_file: trace_files/cat_write.scap
disabled_and_enabled_rules_2:
exit_status: 1
stderr_contains: "Runtime error: You can not specify both disabled .-D/-T. and enabled .-t. rules. Exiting."
disabled_rules:
- "open.*"
run_tags: [a]
rules_file:
- rules/single_rule.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
rules_file:
- rules/single_rule.yaml
conf_file: confs/file_output.yaml
trace_file: trace_files/cat_write.scap
outputs:
- /tmp/falco_outputs/file_output.txt: Warning An open was seen
program_output:
detect: True
detect_level: WARNING
rules_file:
- rules/single_rule.yaml
conf_file: confs/program_output.yaml
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
disabled_tags_a:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
disable_tags: [a]
detect_counts:
- open_1: 0
- open_2: 1
- open_3: 1
- open_4: 0
- open_5: 0
- open_6: 1
- open_7: 0
- open_8: 0
- open_9: 0
- open_10: 0
- open_11: 1
- open_12: 1
- open_13: 1
disabled_tags_b:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
disable_tags: [b]
detect_counts:
- open_1: 1
- open_2: 0
- open_3: 1
- open_4: 0
- open_5: 1
- open_6: 0
- open_7: 0
- open_8: 0
- open_9: 1
- open_10: 0
- open_11: 1
- open_12: 1
- open_13: 1
disabled_tags_c:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
disable_tags: [c]
detect_counts:
- open_1: 1
- open_2: 1
- open_3: 0
- open_4: 1
- open_5: 0
- open_6: 0
- open_7: 0
- open_8: 1
- open_9: 0
- open_10: 0
- open_11: 1
- open_12: 1
- open_13: 1
disabled_tags_ab:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
disable_tags: [a, b]
detect_counts:
- open_1: 0
- open_2: 0
- open_3: 1
- open_4: 0
- open_5: 0
- open_6: 0
- open_7: 0
- open_8: 0
- open_9: 0
- open_10: 0
- open_11: 1
- open_12: 1
- open_13: 1
disabled_tags_abc:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
disable_tags: [a, b, c]
detect_counts:
- open_1: 0
- open_2: 0
- open_3: 0
- open_4: 0
- open_5: 0
- open_6: 0
- open_7: 0
- open_8: 0
- open_9: 0
- open_10: 0
- open_11: 1
- open_12: 1
- open_13: 1
run_tags_a:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [a]
detect_counts:
- open_1: 1
- open_2: 0
- open_3: 0
- open_4: 1
- open_5: 1
- open_6: 0
- open_7: 1
- open_8: 1
- open_9: 1
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_b:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [b]
detect_counts:
- open_1: 0
- open_2: 1
- open_3: 0
- open_4: 1
- open_5: 0
- open_6: 1
- open_7: 1
- open_8: 1
- open_9: 0
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_c:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [c]
detect_counts:
- open_1: 0
- open_2: 0
- open_3: 1
- open_4: 0
- open_5: 1
- open_6: 1
- open_7: 1
- open_8: 0
- open_9: 1
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_ab:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [a, b]
detect_counts:
- open_1: 1
- open_2: 1
- open_3: 0
- open_4: 1
- open_5: 1
- open_6: 1
- open_7: 1
- open_8: 1
- open_9: 1
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_bc:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [b, c]
detect_counts:
- open_1: 0
- open_2: 1
- open_3: 1
- open_4: 1
- open_5: 1
- open_6: 1
- open_7: 1
- open_8: 1
- open_9: 1
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_abc:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [a, b, c]
detect_counts:
- open_1: 1
- open_2: 1
- open_3: 1
- open_4: 1
- open_5: 1
- open_6: 1
- open_7: 1
- open_8: 1
- open_9: 1
- open_10: 1
- open_11: 0
- open_12: 0
- open_13: 0
run_tags_d:
detect: True
detect_level: WARNING
rules_file:
- rules/tagged_rules.yaml
trace_file: trace_files/open-multiple-files.scap
run_tags: [d]
detect_counts:
- open_1: 0
- open_2: 0
- open_3: 0
- open_4: 0
- open_5: 0
- open_6: 0
- open_7: 0
- open_8: 0
- open_9: 0
- open_10: 0
- open_11: 1
- open_12: 0
- open_13: 0
list_append_failure:
exit_status: 1
stderr_contains: "List my_list has 'append' key but no list by that name already exists. Exiting"
rules_file:
- rules/list_append_failure.yaml
trace_file: trace_files/cat_write.scap
list_append:
detect: True
detect_level: WARNING
rules_file:
- rules/list_append.yaml
trace_file: trace_files/cat_write.scap
list_append_false:
detect: False
rules_file:
- rules/list_append_false.yaml
trace_file: trace_files/cat_write.scap

View File

@@ -1,183 +0,0 @@
trace_files: !mux
builtin_rules_no_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_warning: False
test_warnings:
detect: False
trace_file: trace_files/empty.scap
rules_file: rules/falco_rules_warnings.yaml
rules_warning:
- no_evttype
- evttype_not_equals
- leading_not
- not_equals_at_end
- not_at_end
- not_before_trailing_evttype
- not_equals_before_trailing_evttype
- not_equals_and_not
- not_equals_before_in
- not_before_in
- not_in_before_in
- leading_in_not_equals_before_evttype
- leading_in_not_equals_at_evttype
- not_with_evttypes
- not_with_evttypes_addl
- not_equals_before_evttype
- not_equals_before_in_evttype
- not_before_evttype
- not_before_evttype_using_in
rules_events:
- no_warnings: [execve]
- no_evttype: [all]
- evttype_not_equals: [all]
- leading_not: [all]
- not_equals_after_evttype: [execve]
- not_after_evttype: [execve]
- leading_trailing_evttypes: [execve,open]
- leading_multtrailing_evttypes: [connect,execve,open]
- leading_multtrailing_evttypes_using_in: [connect,execve,open]
- not_equals_at_end: [all]
- not_at_end: [all]
- not_before_trailing_evttype: [all]
- not_equals_before_trailing_evttype: [all]
- not_equals_and_not: [all]
- not_equals_before_in: [all]
- not_before_in: [all]
- not_in_before_in: [all]
- evttype_in: [execve,open]
- evttype_in_plus_trailing: [connect,execve,open]
- leading_in_not_equals_before_evttype: [all]
- leading_in_not_equals_at_evttype: [all]
- not_with_evttypes: [all]
- not_with_evttypes_addl: [all]
- not_equals_before_evttype: [all]
- not_equals_before_in_evttype: [all]
- not_before_evttype: [all]
- not_before_evttype_using_in: [all]
- repeated_evttypes: [open]
- repeated_evttypes_with_in: [open]
- repeated_evttypes_with_separate_in: [open]
- repeated_evttypes_with_mix: [open]
rule_names_with_spaces:
detect: True
detect_level: WARNING
rules_file:
- rules/rule_names_with_spaces.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_first_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
trace_file: trace_files/cat_write.scap
multiple_rules_last_empty:
detect: True
detect_level: WARNING
rules_file:
- rules/single_rule.yaml
- rules/empty_rules.yaml
trace_file: trace_files/cat_write.scap
multiple_rules:
detect: True
detect_level:
- WARNING
- INFO
- ERROR
rules_file:
- rules/single_rule.yaml
- 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:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- open_from_cat
trace_file: trace_files/cat_write.scap
disabled_rules_using_regex:
detect: False
rules_file:
- rules/empty_rules.yaml
- rules/single_rule.yaml
disabled_rules:
- "open.*"
trace_file: trace_files/cat_write.scap
disabled_rules_using_enabled_flag:
detect: False
rules_file:
- 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
rules_file:
- rules/single_rule.yaml
conf_file: confs/file_output.yaml
trace_file: trace_files/cat_write.scap
outputs:
- /tmp/falco_outputs/file_output.txt: Warning An open was seen
program_output:
detect: True
detect_level: WARNING
rules_file:
- rules/single_rule.yaml
conf_file: confs/program_output.yaml
trace_file: trace_files/cat_write.scap
outputs:
- /tmp/falco_outputs/program_output.txt: Warning An open was seen

203
test/falco_traces.yaml.in Normal file
View File

@@ -0,0 +1,203 @@
has_json_output: !mux
yes:
json_output: True
no:
json_output: False
traces: !mux
change-thread-namespace:
trace_file: traces-positive/change-thread-namespace.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Change thread namespace": 2
container-privileged:
trace_file: traces-positive/container-privileged.scap
detect: True
detect_level: INFO
detect_counts:
- "Launch Privileged Container": 1
container-sensitive-mount:
trace_file: traces-positive/container-sensitive-mount.scap
detect: True
detect_level: INFO
detect_counts:
- "Launch Sensitive Mount Container": 1
create-files-below-dev:
trace_file: traces-positive/create-files-below-dev.scap
detect: True
detect_level: ERROR
detect_counts:
- "Create files below dev": 1
db-program-spawned-process:
trace_file: traces-positive/db-program-spawned-process.scap
detect: True
detect_level: NOTICE
detect_counts:
- "DB program spawned process": 1
falco-event-generator:
trace_file: traces-positive/falco-event-generator.scap
detect: True
detect_level: [ERROR, WARNING, INFO, NOTICE]
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
installer-fbash-manages-service:
trace_file: traces-info/installer-fbash-manages-service.scap
detect: True
detect_level: INFO
detect_counts:
- "Installer bash manages service": 4
installer-bash-non-https-connection:
trace_file: traces-positive/installer-bash-non-https-connection.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Installer bash non https connection": 1
installer-fbash-runs-pkgmgmt:
trace_file: traces-info/installer-fbash-runs-pkgmgmt.scap
detect: True
detect_level: [NOTICE, INFO]
detect_counts:
- "Installer bash runs pkgmgmt program": 4
- "Installer bash non https connection": 4
installer-bash-starts-network-server:
trace_file: traces-positive/installer-bash-starts-network-server.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Installer bash starts network server": 2
- "Installer bash non https connection": 3
installer-bash-starts-session:
trace_file: traces-positive/installer-bash-starts-session.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Installer bash starts session": 1
- "Installer bash non https connection": 3
mkdir-binary-dirs:
trace_file: traces-positive/mkdir-binary-dirs.scap
detect: True
detect_level: ERROR
detect_counts:
- "Mkdir binary dirs": 1
modify-binary-dirs:
trace_file: traces-positive/modify-binary-dirs.scap
detect: True
detect_level: ERROR
detect_counts:
- "Modify binary dirs": 1
modify-package-repo-list-installer:
trace_file: traces-info/modify-package-repo-list-installer.scap
detect: True
detect_level: INFO
detect_counts:
- "Write below etc in installer": 1
non-sudo-setuid:
trace_file: traces-positive/non-sudo-setuid.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Non sudo setuid": 1
read-sensitive-file-after-startup:
trace_file: traces-positive/read-sensitive-file-after-startup.scap
detect: True
detect_level: WARNING
detect_counts:
- "Read sensitive file untrusted": 1
read-sensitive-file-untrusted:
trace_file: traces-positive/read-sensitive-file-untrusted.scap
detect: True
detect_level: WARNING
detect_counts:
- "Read sensitive file untrusted": 1
run-shell-untrusted:
trace_file: traces-positive/run-shell-untrusted.scap
detect: True
detect_level: DEBUG
detect_counts:
- "Run shell untrusted": 1
shell-in-container:
trace_file: traces-positive/shell-in-container.scap
detect: True
detect_level: NOTICE
detect_counts:
- "Run shell in container": 1
system-binaries-network-activity:
trace_file: traces-positive/system-binaries-network-activity.scap
detect: True
detect_level: NOTICE
detect_counts:
- "System procs network activity": 1
system-user-interactive:
trace_file: traces-positive/system-user-interactive.scap
detect: True
detect_level: INFO
detect_counts:
- "System user interactive": 1
user-mgmt-binaries:
trace_file: traces-positive/user-mgmt-binaries.scap
detect: True
detect_level: NOTICE
detect_counts:
- "User mgmt binaries": 1
write-binary-dir:
trace_file: traces-positive/write-binary-dir.scap
detect: True
detect_level: ERROR
detect_counts:
- "Write below binary dir": 4
write-etc:
trace_file: traces-positive/write-etc.scap
detect: True
detect_level: ERROR
detect_counts:
- "Write below etc": 1
write-etc-installer:
trace_file: traces-info/write-etc-installer.scap
detect: True
detect_level: INFO
detect_counts:
- "Write below etc in installer": 1
write-rpm-database:
trace_file: traces-positive/write-rpm-database.scap
detect: True
detect_level: ERROR
detect_counts:
- "Write below rpm database": 1

View File

@@ -0,0 +1,12 @@
- list: my_list
items: [not-cat]
- list: my_list
append: true
items: [cat]
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and proc.name in (my_list)
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,3 @@
- list: my_list
items: [not-cat]
append: true

View File

@@ -0,0 +1,12 @@
- list: my_list
items: [cat]
- list: my_list
append: false
items: [not-cat]
- rule: Open From Cat
desc: A process named cat does an open
condition: evt.type=open and proc.name in (my_list)
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,14 @@
- list: cat_binaries
items: [not_cat]
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,11 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name=cat_binaries
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,11 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (ls, cat_binaries)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,11 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries, ps)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,11 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (ls, cat_binaries, ps)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,11 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name= cat_binaries or proc.name=nopey
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,8 @@
- list: my_list
items: ['"one string"']
- rule: my_rule
desc: my description
condition: evt.type=open and fd.name in (file_my_list)
output: my output
priority: INFO

View File

@@ -0,0 +1,14 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (not_cat)
- macro: is_cat
condition: proc.name in (cat_binaries)
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,17 @@
- list: cat_binaries
items: [cat]
- macro: is_cat
condition: proc.name in (cat_binaries)
- 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
- rule: open_from_cat
desc: A process named cat does an open
condition: evt.type=open and is_cat
output: "An open was seen (command=%proc.cmdline)"
priority: WARNING

View File

@@ -0,0 +1,93 @@
- macro: open_read
condition: (evt.type=open or evt.type=openat) and evt.is_open_read=true and fd.typechar='f'
- rule: open_1
desc: open one
condition: open_read and fd.name=/tmp/file-1
output: Open one (file=%fd.name)
priority: WARNING
tags: [a]
- rule: open_2
desc: open two
condition: open_read and fd.name=/tmp/file-2
output: Open two (file=%fd.name)
priority: WARNING
tags: [b]
- rule: open_3
desc: open three
condition: open_read and fd.name=/tmp/file-3
output: Open three (file=%fd.name)
priority: WARNING
tags: [c]
- rule: open_4
desc: open four
condition: open_read and fd.name=/tmp/file-4
output: Open four (file=%fd.name)
priority: WARNING
tags: [a, b]
- rule: open_5
desc: open file
condition: open_read and fd.name=/tmp/file-5
output: Open file (file=%fd.name)
priority: WARNING
tags: [a, c]
- rule: open_6
desc: open six
condition: open_read and fd.name=/tmp/file-6
output: Open six (file=%fd.name)
priority: WARNING
tags: [b, c]
- rule: open_7
desc: open seven
condition: open_read and fd.name=/tmp/file-7
output: Open seven (file=%fd.name)
priority: WARNING
tags: [a, b, c]
- rule: open_8
desc: open eight
condition: open_read and fd.name=/tmp/file-8
output: Open eight (file=%fd.name)
priority: WARNING
tags: [b, a]
- rule: open_9
desc: open nine
condition: open_read and fd.name=/tmp/file-9
output: Open nine (file=%fd.name)
priority: WARNING
tags: [c, a]
- rule: open_10
desc: open ten
condition: open_read and fd.name=/tmp/file-10
output: Open ten (file=%fd.name)
priority: WARNING
tags: [b, c, a]
- rule: open_11
desc: open eleven
condition: open_read and fd.name=/tmp/file-11
output: Open eleven (file=%fd.name)
priority: WARNING
tags: [d]
- rule: open_12
desc: open twelve
condition: open_read and fd.name=/tmp/file-12
output: Open twelve (file=%fd.name)
priority: WARNING
tags: []
- rule: open_13
desc: open thirteen
condition: open_read and fd.name=/tmp/file-13
output: Open thirteen (file=%fd.name)
priority: WARNING

View File

@@ -2,7 +2,6 @@
SCRIPT=$(readlink -f $0)
SCRIPTDIR=$(dirname $SCRIPT)
MULT_FILE=$SCRIPTDIR/falco_tests.yaml
BRANCH=$1
function download_trace_files() {
@@ -19,56 +18,59 @@ function prepare_multiplex_fileset() {
dir=$1
detect=$2
detect_level=$3
json_output=$4
for trace in $SCRIPTDIR/$dir/*.scap ; do
[ -e "$trace" ] || continue
NAME=`basename $trace .scap`
cat << EOF >> $MULT_FILE
$NAME-detect-$detect-json-$json_output:
# falco_traces.yaml might already have an entry for this trace
# file, with specific detection levels and counts. If so, skip
# it. Otherwise, add a generic entry showing whether or not to
# detect anything.
grep -q "$NAME:" $SCRIPTDIR/falco_traces.yaml && continue
cat << EOF >> $SCRIPTDIR/falco_traces.yaml
$NAME:
detect: $detect
detect_level: $detect_level
detect_level: WARNING
trace_file: $trace
json_output: $json_output
EOF
done
}
function prepare_multiplex_file() {
cp $SCRIPTDIR/falco_tests.yaml.in $MULT_FILE
cp $SCRIPTDIR/falco_traces.yaml.in $SCRIPTDIR/falco_traces.yaml
prepare_multiplex_fileset traces-positive True WARNING False
prepare_multiplex_fileset traces-negative False WARNING True
prepare_multiplex_fileset traces-info True INFO False
prepare_multiplex_fileset traces-positive True
prepare_multiplex_fileset traces-negative False
prepare_multiplex_fileset traces-info True
prepare_multiplex_fileset traces-positive True WARNING True
prepare_multiplex_fileset traces-info True INFO True
echo "Contents of $MULT_FILE:"
cat $MULT_FILE
echo "Contents of $SCRIPTDIR/falco_traces.yaml:"
cat $SCRIPTDIR/falco_traces.yaml
}
function run_tests() {
rm -rf /tmp/falco_outputs
mkdir /tmp/falco_outputs
CMD="avocado run --multiplex $MULT_FILE --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py"
echo "Running: $CMD"
$CMD
TEST_RC=$?
}
function print_test_failure_details() {
echo "Showing full job logs for any tests that failed:"
jq '.tests[] | select(.status != "PASS") | .logfile' $SCRIPTDIR/job-results/latest/results.json | xargs cat
}
function run_tests() {
rm -rf /tmp/falco_outputs
mkdir /tmp/falco_outputs
TEST_RC=0
for mult in $SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml; do
CMD="avocado run --multiplex $mult --job-results-dir $SCRIPTDIR/job-results -- $SCRIPTDIR/falco_test.py"
echo "Running: $CMD"
$CMD
RC=$?
TEST_RC=$((TEST_RC+$RC))
if [ $RC -ne 0 ]; then
print_test_failure_details
fi
done
}
download_trace_files
prepare_multiplex_file
run_tests
if [ $TEST_RC -ne 0 ]; then
print_test_failure_details
fi
exit $TEST_RC

Binary file not shown.

View File

@@ -24,6 +24,10 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
falco_common::falco_common()
{
m_ls = lua_open();
if(!m_ls)
{
throw falco_exception("Cannot open lua");
}
luaL_openlibs(m_ls);
}

View File

@@ -40,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_next_ruleset_id(0),
m_sampling_ratio(1), m_sampling_multiplier(0),
m_replace_container_info(false)
{
luaopen_lpeg(m_ls);
@@ -55,6 +56,8 @@ falco_engine::falco_engine(bool seed_rng)
{
srandom((unsigned) getpid());
}
m_default_ruleset_id = find_ruleset_id(m_default_ruleset);
}
falco_engine::~falco_engine()
@@ -107,20 +110,52 @@ void falco_engine::load_rules_file(const string &rules_filename, bool verbose, b
load_rules(rules_content, verbose, all_events);
}
void falco_engine::enable_rule(string &pattern, bool enabled)
void falco_engine::enable_rule(const string &pattern, bool enabled, const string &ruleset)
{
m_evttype_filter->enable(pattern, enabled);
uint16_t ruleset_id = find_ruleset_id(ruleset);
m_evttype_filter->enable(pattern, enabled, ruleset_id);
}
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
void falco_engine::enable_rule(const string &pattern, bool enabled)
{
enable_rule(pattern, enabled, m_default_ruleset);
}
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled, const string &ruleset)
{
uint16_t ruleset_id = find_ruleset_id(ruleset);
m_evttype_filter->enable_tags(tags, enabled, ruleset_id);
}
void falco_engine::enable_rule_by_tag(const set<string> &tags, bool enabled)
{
enable_rule_by_tag(tags, enabled, m_default_ruleset);
}
uint16_t falco_engine::find_ruleset_id(const std::string &ruleset)
{
auto it = m_known_rulesets.lower_bound(ruleset);
if(it == m_known_rulesets.end() ||
it->first != ruleset)
{
it = m_known_rulesets.emplace_hint(it,
std::make_pair(ruleset, m_next_ruleset_id++));
}
return it->second;
}
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev, uint16_t ruleset_id)
{
if(should_drop_evt())
{
return unique_ptr<struct rule_result>();
}
if(!m_evttype_filter->run(ev))
if(!m_evttype_filter->run(ev, ruleset_id))
{
return unique_ptr<struct rule_result>();
}
@@ -155,6 +190,11 @@ unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
return res;
}
unique_ptr<falco_engine::rule_result> falco_engine::process_event(sinsp_evt *ev)
{
return process_event(ev, m_default_ruleset_id);
}
void falco_engine::describe_rule(string *rule)
{
return m_rules->describe_rule(rule);
@@ -182,10 +222,11 @@ void falco_engine::print_stats()
}
void falco_engine::add_evttype_filter(string &rule,
list<uint32_t> &evttypes,
set<uint32_t> &evttypes,
set<string> &tags,
sinsp_filter* filter)
{
m_evttype_filter->add(rule, evttypes, filter);
m_evttype_filter->add(rule, evttypes, tags, filter);
}
void falco_engine::clear_filters()

View File

@@ -20,6 +20,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#include <string>
#include <memory>
#include <set>
#include "sinsp.h"
#include "filter.h"
@@ -47,9 +48,24 @@ public:
void load_rules(const std::string &rules_content, bool verbose, bool all_events);
//
// Enable/Disable any rules matching the provided pattern (regex).
// Enable/Disable any rules matching the provided pattern
// (regex). When provided, enable/disable these rules in the
// context of the provided ruleset. The ruleset (id) can later
// be passed as an argument to process_event(). This allows
// for different sets of rules being active at once.
//
void enable_rule(std::string &pattern, bool enabled);
void enable_rule(const std::string &pattern, bool enabled, const std::string &ruleset);
// Wrapper that assumes the default ruleset
void enable_rule(const std::string &pattern, bool enabled);
//
// Enable/Disable any rules with any of the provided tags (set, exact matches only)
//
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled, const std::string &ruleset);
// Wrapper that assumes the default ruleset
void enable_rule_by_tag(const std::set<std::string> &tags, bool enabled);
struct rule_result {
sinsp_evt *evt;
@@ -58,12 +74,30 @@ public:
std::string format;
};
//
// Return the ruleset id corresponding to this ruleset name,
// creating a new one if necessary. If you provide any ruleset
// to enable_rule/enable_rule_by_tag(), you should look up the
// ruleset id and pass it to process_event().
//
uint16_t find_ruleset_id(const std::string &ruleset);
//
// Given an event, check it against the set of rules in the
// engine and if a matching rule is found, return details on
// the rule that matched. If no rule matched, returns NULL.
//
// the reutrned rule_result is allocated and must be delete()d.
// When ruleset_id is provided, use the enabled/disabled status
// associated with the provided ruleset. This is only useful
// when you have previously called enable_rule/enable_rule_by_tag
// with a ruleset string.
//
// the returned rule_result is allocated and must be delete()d.
std::unique_ptr<rule_result> process_event(sinsp_evt *ev, uint16_t ruleset_id);
//
// Wrapper assuming the default ruleset
//
std::unique_ptr<rule_result> process_event(sinsp_evt *ev);
//
@@ -78,11 +112,12 @@ public:
void print_stats();
//
// Add a filter, which is related to the specified list of
// Add a filter, which is related to the specified set of
// event types, to the engine.
//
void add_evttype_filter(std::string &rule,
list<uint32_t> &evttypes,
std::set<uint32_t> &evttypes,
std::set<std::string> &tags,
sinsp_filter* filter);
// Clear all existing filters.
@@ -120,6 +155,8 @@ private:
inline bool should_drop_evt();
falco_rules *m_rules;
uint16_t m_next_ruleset_id;
std::map<string, uint16_t> m_known_rulesets;
std::unique_ptr<sinsp_evttype_filter> m_evttype_filter;
//
@@ -146,6 +183,8 @@ private:
double m_sampling_multiplier;
std::string m_lua_main_filename = "rule_loader.lua";
std::string m_default_ruleset = "falco-default-ruleset";
uint32_t m_default_ruleset_id;
std::string m_extra;
bool m_replace_container_info;

View File

@@ -24,12 +24,14 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
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

View File

@@ -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;
};

View File

@@ -20,6 +20,7 @@ local compiler = {}
compiler.verbose = false
compiler.all_events = false
compiler.trim = parser.trim
function compiler.set_verbose(verbose)
compiler.verbose = verbose
@@ -58,14 +59,15 @@ end
definition uses another macro).
--]]
function expand_macros(ast, defs, changed)
function copy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
return res
end
function copy_ast_obj(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy_ast_obj(k)] = copy_ast_obj(v) end
return res
end
function expand_macros(ast, defs, changed)
if (ast.type == "Rule") then
return expand_macros(ast.filter, defs, changed)
@@ -74,7 +76,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.value.value] == nil) then
error("Undefined macro '".. ast.value.value .. "' used in filter.")
end
ast.value = copy(defs[ast.value.value])
ast.value = copy_ast_obj(defs[ast.value.value])
changed = true
return changed
end
@@ -86,7 +88,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.left.value] == nil) then
error("Undefined macro '".. ast.left.value .. "' used in filter.")
end
ast.left = copy(defs[ast.left.value])
ast.left = copy_ast_obj(defs[ast.left.value])
changed = true
end
@@ -94,7 +96,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.right.value] == nil) then
error("Undefined macro ".. ast.right.value .. " used in filter.")
end
ast.right = copy(defs[ast.right.value])
ast.right = copy_ast_obj(defs[ast.right.value])
changed = true
end
@@ -107,7 +109,7 @@ function expand_macros(ast, defs, changed)
if (defs[ast.argument.value] == nil) then
error("Undefined macro ".. ast.argument.value .. " used in filter.")
end
ast.argument = copy(defs[ast.argument.value])
ast.argument = copy_ast_obj(defs[ast.argument.value])
changed = true
end
return expand_macros(ast.argument, defs, changed)
@@ -281,7 +283,7 @@ function get_evttypes(name, ast, source)
return evttypes
end
function compiler.compile_macro(line, list_defs)
function compiler.compile_macro(line, macro_defs, list_defs)
for name, items in pairs(list_defs) do
line = string.gsub(line, name, table.concat(items, ", "))
@@ -300,6 +302,21 @@ function compiler.compile_macro(line, list_defs)
check_for_ignored_syscalls_events(ast, 'macro', line)
end
-- Simply as a validation step, try to expand all macros in this
-- macro's condition. This changes the ast, so we make a copy
-- first.
local ast_copy = copy_ast_obj(ast)
if (ast.type == "Rule") then
-- Line is a filter, so expand macro references
repeat
expanded = expand_macros(ast_copy, macro_defs, false)
until expanded == false
else
error("Unexpected top-level AST type: "..ast.type)
end
return ast
end
@@ -309,7 +326,12 @@ end
function compiler.compile_filter(name, source, macro_defs, list_defs)
for name, items in pairs(list_defs) do
source = string.gsub(source, name, table.concat(items, ", "))
local begin_name_pat = "^("..name..")([%s(),=])"
local mid_name_pat = "([%s(),=])("..name..")([%s(),=])"
local end_name_pat = "([%s(),=])("..name..")$"
source = string.gsub(source, begin_name_pat, table.concat(items, ", ").."%2")
source = string.gsub(source, mid_name_pat, "%1"..table.concat(items, ", ").."%3")
source = string.gsub(source, end_name_pat, "%1"..table.concat(items, ", "))
end
local ast, error_msg = parser.parse_filter(source)

View File

@@ -127,10 +127,11 @@ function trim(s)
if (type(s) ~= "string") then return s end
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
parser.trim = trim
local function terminal (tag)
-- Rather than trim the whitespace in this way, it would be nicer to exclude it from the capture...
return token(V(tag), tag) / function (tok) return { type = tag, value = trim(tok)} end
return token(V(tag), tag) / function (tok) val = tok; if tag ~= "String" then val = trim(tok) end; return { type = tag, value = val} end
end
local function unaryboolop (op, e)
@@ -237,7 +238,7 @@ local G = {
Identifier = V"idStart" * V"idRest"^0;
Macro = V"idStart" * V"idRest"^0 * -P".";
Int = digit^1;
PathString = (alnum + S'-_/*?')^1;
PathString = (alnum + S'.-_/*?')^1;
Index = V"Int" + V"PathString";
FieldName = V"Identifier" * (P"." + V"Identifier")^1 * (P"[" * V"Index" * P"]")^-1;
Name = C(V"Identifier") * -V"idRest";

View File

@@ -222,7 +222,24 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
end
end
state.lists_by_name[v['list']] = v
-- Possibly append to an existing list
append = false
if v['append'] then
append = v['append']
end
if append then
if state.lists_by_name[v['list']] == nil then
error ("List " ..v['list'].. " has 'append' key but no list by that name already exists")
end
for i, elem in ipairs(v['items']) do
table.insert(state.lists_by_name[v['list']]['items'], elem)
end
else
state.lists_by_name[v['list']] = v
end
elseif (v['rule']) then
@@ -243,6 +260,10 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
state.ordered_rule_names[#state.ordered_rule_names+1] = v['rule']
end
-- The output field might be a folded-style, which adds a
-- newline to the end. Remove any trailing newlines.
v['output'] = compiler.trim(v['output'])
state.rules_by_name[v['rule']] = v
else
@@ -283,7 +304,7 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
local v = state.macros_by_name[name]
local ast = compiler.compile_macro(v['condition'], state.lists)
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
state.macros[v['macro']] = ast.filter.value
end
@@ -308,8 +329,12 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
install_filter(filter_ast.filter.value)
if (v['tags'] == nil) then
v['tags'] = {}
end
-- Pass the filter and event types back up
falco_rules.add_filter(rules_mgr, v['rule'], evttypes)
falco_rules.add_filter(rules_mgr, v['rule'], evttypes, v['tags'])
-- Rule ASTs are merged together into one big AST, with "OR" between each
-- rule.

View File

@@ -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);
@@ -65,42 +66,56 @@ void falco_rules::clear_filters()
int falco_rules::add_filter(lua_State *ls)
{
if (! lua_islightuserdata(ls, -3) ||
! lua_isstring(ls, -2) ||
if (! lua_islightuserdata(ls, -4) ||
! lua_isstring(ls, -3) ||
! 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, -3);
const char *rulec = lua_tostring(ls, -2);
falco_rules *rules = (falco_rules *) lua_topointer(ls, -4);
const char *rulec = lua_tostring(ls, -3);
list<uint32_t> evttypes;
set<uint32_t> evttypes;
lua_pushnil(ls); /* first key */
while (lua_next(ls, -3) != 0) {
// key is at index -2, value is at index
// -1. We want the keys.
evttypes.insert(luaL_checknumber(ls, -2));
// Remove value, keep key for next iteration
lua_pop(ls, 1);
}
set<string> tags;
lua_pushnil(ls); /* first key */
while (lua_next(ls, -2) != 0) {
// key is at index -2, value is at index
// -1. We want the keys.
evttypes.push_back(luaL_checknumber(ls, -2));
tags.insert(lua_tostring(ls, -1));
// Remove value, keep key for next iteration
lua_pop(ls, 1);
}
std::string rule = rulec;
rules->add_filter(rule, evttypes);
rules->add_filter(rule, evttypes, tags);
return 0;
}
void falco_rules::add_filter(string &rule, list<uint32_t> &evttypes)
void falco_rules::add_filter(string &rule, set<uint32_t> &evttypes, set<string> &tags)
{
// While the current rule was being parsed, a sinsp_filter
// object was being populated by lua_parser. Grab that filter
// and pass it to the engine.
sinsp_filter *filter = m_lua_parser->get_filter(true);
m_engine->add_evttype_filter(rule, evttypes, filter);
m_engine->add_evttype_filter(rule, evttypes, tags, filter);
}
int falco_rules::enable_rule(lua_State *ls)
@@ -109,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);

View File

@@ -18,7 +18,7 @@ along with falco. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <list>
#include <set>
#include "sinsp.h"
@@ -42,7 +42,7 @@ class falco_rules
private:
void clear_filters();
void add_filter(string &rule, list<uint32_t> &evttypes);
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<string> &tags);
void enable_rule(string &rule, bool enabled);
lua_parser* m_lua_parser;

View File

@@ -31,20 +31,30 @@ token_bucket::~token_bucket()
{
}
void token_bucket::init(uint32_t rate, uint32_t max_tokens)
void token_bucket::init(double rate, double max_tokens, uint64_t now)
{
m_rate = rate;
m_max_tokens = max_tokens;
m_tokens = max_tokens;
m_last_seen = sinsp_utils::get_current_time_ns();
if(now == 0)
{
now = sinsp_utils::get_current_time_ns();
}
m_last_seen = now;
}
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);
return claim(1, now);
}
bool token_bucket::claim(double tokens, uint64_t now)
{
double tokens_gained = m_rate * ((now - m_last_seen) / (1000000000.0));
m_last_seen = now;
m_tokens += tokens_gained;
@@ -58,14 +68,24 @@ bool token_bucket::claim()
}
//
// If tokens is < 1, can't claim.
// If m_tokens is < tokens, can't claim.
//
if(m_tokens < 1)
if(m_tokens < tokens)
{
return false;
}
m_tokens--;
m_tokens -= tokens;
return true;
}
double token_bucket::get_tokens()
{
return m_tokens;
}
uint64_t token_bucket::get_last_seen()
{
return m_last_seen;
}

View File

@@ -31,30 +31,42 @@ public:
//
// Initialize the token bucket and start accumulating tokens
//
void init(uint32_t rate, uint32_t max_tokens);
void init(double rate, double max_tokens, uint64_t now = 0);
//
// Returns true if a token can be claimed. Also updates
// internal metrics.
// Try to claim tokens tokens from the token bucket, using a
// timestamp of now. Returns true if the tokens could be
// claimed. Also updates internal metrics.
//
bool claim(double tokens, uint64_t now);
// Simpler version of claim that claims a single token and
// uses the current time for now
bool claim();
// Return the current number of tokens available
double get_tokens();
// Return the last time someone tried to claim a token.
uint64_t get_last_seen();
private:
//
// The number of tokens generated per second.
//
uint64_t m_rate;
double m_rate;
//
// The maximum number of tokens that can be banked for future
// claim()s.
//
uint64_t m_max_tokens;
double m_max_tokens;
//
// The current number of tokens
//
uint64_t m_tokens;
double m_tokens;
//
// The last time claim() was called (or the object was created).

View File

@@ -19,7 +19,7 @@ target_link_libraries(falco
configure_file(config_falco.h.in config_falco.h)
install(TARGETS falco DESTINATION bin)
install(TARGETS falco DESTINATION ${FALCO_BIN_DIR})
install(DIRECTORY lua
DESTINATION share/falco
DESTINATION ${FALCO_SHARE_DIR}
FILES_MATCHING PATTERN *.lua)

View File

@@ -53,6 +53,7 @@ static void signal_callback(int signal)
static void usage()
{
printf(
"falco version " FALCO_VERSION "\n"
"Usage: falco [options]\n\n"
"Options:\n"
" -h, --help Print this page\n"
@@ -60,6 +61,7 @@ static void usage()
" -A Monitor all events, including those with EF_DROP_FALCO flag.\n"
" -d, --daemon Run as a daemon\n"
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
" Can not be specified with -t.\n"
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
" -k <url>, --k8s-api=<url>\n"
" Enable Kubernetes support by connecting to the API server\n"
@@ -85,6 +87,7 @@ static void usage()
" Marathon url is optional and defaults to Mesos address, port 8080.\n"
" The API servers can also be specified via the environment variable\n"
" FALCO_MESOS_API.\n"
" -M <num_seconds> Stop collecting after <num_seconds> reached.\n"
" -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
" <key> can be a two-part <key>.<subkey>\n"
" -p <output_format>, --print=<output_format>\n"
@@ -100,7 +103,12 @@ static void usage()
" 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"
" -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
" Can not be specified with -t.\n"
" -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
" Can not be specified with -T/-D.\n"
" -v Verbose output.\n"
" --version Print version number.\n"
"\n"
);
}
@@ -128,12 +136,14 @@ std::list<string> cmdline_options;
uint64_t do_inspect(falco_engine *engine,
falco_outputs *outputs,
sinsp* inspector,
uint64_t duration_to_tot_ns,
string &stats_filename)
{
uint64_t num_evts = 0;
int32_t res;
sinsp_evt* ev;
StatsFileWriter writer;
uint64_t duration_start = 0;
if (stats_filename != "")
{
@@ -177,6 +187,17 @@ uint64_t do_inspect(falco_engine *engine,
throw sinsp_exception(inspector->getlasterr().c_str());
}
if (duration_start == 0)
{
duration_start = ev->get_ts();
} else if(duration_to_tot_ns > 0)
{
if(ev->get_ts() - duration_start >= duration_to_tot_ns)
{
break;
}
}
if(!inspector->is_debug_enabled() &&
ev->get_category() & EC_INTERNAL)
{
@@ -227,6 +248,7 @@ int falco_init(int argc, char **argv)
string* mesos_api = 0;
string output_format = "";
bool replace_container_info = false;
int duration_to_tot = 0;
// Used for writing trace files
int duration_seconds = 0;
@@ -250,6 +272,7 @@ int falco_init(int argc, char **argv)
{"option", required_argument, 0, 'o'},
{"print", required_argument, 0, 'p' },
{"pidfile", required_argument, 0, 'P' },
{"version", no_argument, 0, 0 },
{"writefile", required_argument, 0, 'w' },
{0, 0, 0, 0}
@@ -259,12 +282,15 @@ int falco_init(int argc, char **argv)
{
set<string> disabled_rule_patterns;
string pattern;
string all_rules = ".*";
set<string> disabled_rule_tags;
set<string> enabled_rule_tags;
//
// Parse the args
//
while((op = getopt_long(argc, argv,
"hc:AdD:e:k:K:Ll:m:o:P:p:r:s:vw:",
"hc:AdD:e:k:K:Ll:m:M:o:P:p:r:s:T:t:vw:",
long_options, &long_index)) != -1)
{
switch(op)
@@ -305,6 +331,13 @@ int falco_init(int argc, char **argv)
case 'm':
mesos_api = new string(optarg);
break;
case 'M':
duration_to_tot = atoi(optarg);
if(duration_to_tot <= 0)
{
throw sinsp_exception(string("invalid duration") + optarg);
}
break;
case 'o':
cmdline_options.push_back(optarg);
break;
@@ -339,6 +372,12 @@ int falco_init(int argc, char **argv)
case 's':
stats_filename = optarg;
break;
case 'T':
disabled_rule_tags.insert(optarg);
break;
case 't':
enabled_rule_tags.insert(optarg);
break;
case 'v':
verbose = true;
break;
@@ -354,6 +393,13 @@ int falco_init(int argc, char **argv)
}
if(string(long_options[long_index].name) == "version")
{
printf("falco version %s\n", FALCO_VERSION);
return EXIT_SUCCESS;
}
inspector = new sinsp();
engine = new falco_engine();
engine->set_inspector(inspector);
@@ -421,12 +467,40 @@ int falco_init(int argc, char **argv)
falco_logger::log(LOG_INFO, "Parsed rules from file " + filename + "\n");
}
// You can't both disable and enable rules
if((disabled_rule_patterns.size() + disabled_rule_tags.size() > 0) &&
enabled_rule_tags.size() > 0) {
throw std::invalid_argument("You can not specify both disabled (-D/-T) and enabled (-t) rules");
}
for (auto pattern : disabled_rule_patterns)
{
falco_logger::log(LOG_INFO, "Disabling rules matching pattern: " + pattern + "\n");
engine->enable_rule(pattern, false);
}
if(disabled_rule_tags.size() > 0)
{
for(auto tag : disabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Disabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(disabled_rule_tags, false);
}
if(enabled_rule_tags.size() > 0)
{
// Since we only want to enable specific
// rules, first disable all rules.
engine->enable_rule(all_rules, false);
for(auto tag : enabled_rule_tags)
{
falco_logger::log(LOG_INFO, "Enabling rules with tag: " + tag + "\n");
}
engine->enable_rule_by_tag(enabled_rule_tags, true);
}
outputs->init(config.m_json_output, config.m_notifications_rate, config.m_notifications_max_burst);
if(!all_events)
@@ -610,6 +684,7 @@ int falco_init(int argc, char **argv)
num_evts = do_inspect(engine,
outputs,
inspector,
uint64_t(duration_to_tot*ONE_SECOND_IN_NS),
stats_filename);
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;

View File

@@ -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)