a bit closer:
- 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: syscall.type = setns and not proc.name in (docker, sysdig, dragent) output: "Namespace change (setns) by unexpected program (user=%user.name command=%proc.cmdline container=%container.id)" priority: WARNING
This rule pays close attention to a container moving between namespaces. The setns
syscall that is marked as important is used to change namespace. The rule, however, ignores the event if docker
, sysdig
, or dragent
initiate it.
Another example is a case study that Sysdig wrote about to help explain how a CVE could be mitigated using Falco, at the end of 2019. It was CVE-2019-14287 (cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14287
) that allowed a simple command to be run to make the sudo
command run commands as the root
user. To exploit the CVE, it was apparently as simple as using the sudo
command as follows:
$ sudo -u#-1
In Listing 3.2 we can see the rule that the Sysdig team concocted to detect and then block the exploit within the CVE.
Listing 3.2: Detecting and Blocking the “sudo” CVE
- rule: Sudo Potential bypass of Runas user restrictions (CVE-2019-14287) desc: […snip…] This can be used by a user with sufficient sudo privileges to run commands as root even if the Runas specification explicitly disallows root access as long as the ALL keyword is listed first in the Runas specification condition:> spawned_process and proc.name="sudo" and (proc.cmdline contains "-u#-1" or proc.cmdline contains "-u#4294967295") output: "Detect sudo exploit (CVE-2019-14287) (user=%user.name command=%proc.cmdline container=%container.info)" priority: CRITICAL tags: [filesystem, mitre_privilege_escalation]
Source: sysdig.com/blog/detecting-cve-2019-14287
Changing Rules
If you run the Docker command to check the help output, you are offered a number of useful choices to remember, as shown here:
$ docker run -it falcosecurity/falco-no-driver:latest falco --help
And you can list all the rules currently loaded up into Falco with this syntax:
$ docker run -it falcosecurity/falco-no-driver:latest falco -L
The output includes the rule name along with a description such as this example:
Rule Description ---- ----------- Create Symlink Over Sensitive Files Detect symlink over sensitive files
Although there are certainly more graceful ways of editing rules and configuration for Falco (which we will look at in a moment), if you use the container approach to run Falco, it is possible to extract the rules file that you want to change and save it to the local machine. Then you can simply mount the local machine volume when Falco fires up, and your changing configuration and rules will be loaded up.
You can initially copy the files out of the container with this command, where a docker ps
command has given you the hash ID of your container previously:
$ docker cp 1f7591607c7d:/etc/falco/falco_rules.yaml .
Simply repeat the previous command for these files:
falco_rules.local.yaml, falco.yaml, k8s_audit_rules.yaml
As you might imagine, you should place your own custom rules within the falco_rules.local.yaml
file, which, barring comments, is mostly empty and not overwritten with version upgrades.
To load up changes, mount your volume as so with the additional -v $(pwd):/etc/falco/
option that mounts the /etc/falco
directory from inside the container to your current working directory on your local machine:
$ docker run --rm -it --security-opt apparmor:unconfined \ --cap-add SYS_PTRACE \ --pid=host $(ls /dev/falco* | xargs -I {} echo --device {}) -v $(pwd):/etc/falco/ \ -v /var/run/docker.sock:/var/run/docker.sock \ falcosecurity/falco-no-driver:latest
The bundled rules are impressive and well worth a look. Listing 3.3 shows a Kubernetes example.
Listing 3.3: Unwanted Service Exposure via a NodePort Is Captured in Kubernetes
- rule: Unexpected K8s NodePort Connection desc: Detect attempts to use K8s NodePorts from a container condition: (inbound_outbound) and fd.sport>= 30000 and fd.sport <= 32767 and container and not nodeport_containers output: Unexpected K8s NodePort Connection (command=%proc.cmdline connection=%fd.name container_id=%container.id image=%container.image.repository) priority: NOTICE tags: [network, k8s, container, mitre_port_knocking]
It's not 100% clear, but the commercial, enterprise method used to update rules appears to be connecting to a back end. Rather than extracting the configuration and rules files from a running container, Falco also offers the ability to install rules in a different way. On Docker Hub (hub.docker.com/r/sysdig/falco_rules_installer
), there is a container image created by Sysdig that will allow you to update rules via a running container. Its purpose is to first validate existing rules and then inspect any custom rules and deploy them to a suitable back end. The command would look like this, for example:
$ docker run --rm --name falco-rules-installer -it \ -e DEPLOY_HOSTNAME=https://my-sysdig-backend.com \ -e [email protected] \ -e DEPLOY_USER_PASSWORD=<my password> \ -e VALIDATE_RULES=yes -e DEPLOY_RULES=yes \ -e CREATE_NEW_POLICIES=no \ -e SDC_SSL_VERIFY=True sysdig/falco_rules_installer:latest
For our purposes, though, copying rules out of a running container makes sense. For the host-installed version, you can also pass the -c
switch to point the daemon at a different configuration file. It is also quite possible to point, multiple times, at directories where your rules reside with the -r
switch, too.
Macros
Falco also employs the concept of using macros. The example that their documentation offers for a simple macro is as follows:
- macro: in_container condition: container.id != host and proc.name = sh
This example could be reused across multiple rules without having to explicitly rewrite it each time and offer significant time-savings.
Lists
It is also possible to use lists so that collections of items can be grouped together more easily to make them more repeatable. The following is one example:
- list: common_binaries items: [netcat, iftop, ngrep]
Here, we can avoid explicitly writing all the binaries for Linux shells and instead just refer to a list of shell_binaries
.
Getting Your Priorities Right
The following are categories for rule priorities:
EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFORMATIONAL, DEBUG
These categories will allow you to sort alerts into a more meaningful set of results and allow the ability to react accordingly. As we saw in the other rules, within your rules, you would add a line such as this within the following example pseudocode stanza:
- rule: A custom rule desc: Rule description condition: container.privileged=true priority: WARNING
Tagging Rulesets
You can also group rules and alerts with tags to help with identifying issues more clearly. The tagging also offers the ability to explicitly run only certain rules with the relevant tags, for example. The previous example is shown expanded next to include tags. The -T