Project guest post originally published on the Falco blog by Mark Stemm

One of the upcoming features in Falco that we’re really excited about is the ability to extend Falco’s functionality by using plugins. We’ll be demoing this capability during Kubecon North America 2021. Specifically, we’ll be showing the support for AWS Cloudtrail logs in Falco, with a Cloudtrail plugin and related falco rules that can do the following:

  • Read Cloudtrail logs and return them as events
  • Identify suspicious or notable activities in Cloudtrail logs.

The feature itself is still in very early access, and we expect it will be officially released early next year. In the meantime, we’d like to get potential plugin developers to take a look at the APIs and provide feedback while we move forward towards an official release.

What Are Plugins?

Plugins are shared libraries that conform to a documented API and come in two flavors:

  • Source Plugins add new event sources/filter fields that can be evaluated using filtering expressions/Falco rules.
  • Extractor Plugins add the ability to define new fields that can extract information from either core Falco events or events generated by other plugins.

These two flavours of plugins are expected to dramatically extend the functionality of Falco, allowing users to write rules for almost any kind of streaming data.

A plugin can be written in any language, as long as the language supports exposing C function interfaces that can be called from a C/C++ program like Falco. We’ve written plugins in C, C++, and Go using this interface. For Go, we’ve also written a module that handles some of the work of translation between C pseudo-types and Go types, as well as providing convenience methods that make it easier to write plugins in Go.

Source Plugins

A source plugin provides a new event source. It has the ability to “open” and “close” a session that provides events. It also has the ability to return an event to the plugin framework via a next() method.

Source plugins also have the ability to extract information from events based on fields. For example, given a syscall event, a field proc.name can extract a process name such as ngnix. The plugin returns a set of supported fields, and there are functions to extract a value given an event and field. The plugin framework can then build filtering expressions/Falco rule conditions based on these fields combined with relational and/or logical operators. For example, let us consider an expression, ct.name=root and ct.region=us-east-1. The plugin framework handles parsing the expression, calling the plugin to extract values for fields ct.name/ct.region for a given event, and determining the result of the expression. In a Falco output string, such as An EC2 Node was created (name=%ct.name region=%ct.region), the plugin framework handles parsing the output string, calling the plugin to extract values for fields, building the resolved string, and replacing the template field names (e.g. %ct.region) with values (e.g. us-east-1).

Extractor Plugins

An extractor plugin focuses only on field extraction from events generated by other plugins or by the core libraries. It does not provide an event source, but can extract fields from other event sources. An example is json field extraction, where a plugin might be able to extract fields from arbitrary json payloads.

We’ve written a JSON extractor plugin that can extract arbitrary data from json payloads, using JSON Pointers.

Plugin Events In Capture Files

Plugin events are compatible with .scap capture files and can be read by Falco and read/written by userspace tools like sysdig that use the Falco libraries. A new event type “pluginevent” contains a plugin id and an arbitrary payload returned by the plugin. This allows leveraging existing support for capture files in the Falco libraries. A capture file can potentially contain a mix of system call and plugin events in the same file. You can even read capture files generated by plugins in tools like wireshark!

Plugin Event Sources and Interoperability

With the introduction of plugins, there’s the potential for many new kinds of events and many new fields that can extract information from events. With that in mind, we’ve formalized how rules are matched to events in Falco.

Events returned by source plugins have an “event source” which describes the information in the event. This is distinct from the plugin name to allow for multiple kinds of plugins to generate the same kind of events. For example, there might be plugins gke-audit-bridge, eks-audit-bridge, ibmcloud-audit-bridge, and so on that can fetch K8s Audit information. The plugins would be distinct with their own shared libraries and implementations, but would have the same event source k8s_audit.

When Falco loads rules, it reads the source: property and tries to match the event source with a loaded source and/or extractor plugin. If a source plugin that supports the event source is loaded, all rules with that particular source will be evaluated for events coming from that source plugin.

Extractor plugins are handled slightly differently. An extractor plugin optionally provides a set of compatible event sources. When the framework receives an event with an event source that is in the compatible event sources list, fields in expressions/Falco outputs will be extracted from events using the extractor plugin. An extractor plugin can also not name a set of event sources. In this case, all events will be presented to the extractor plugin, regardless of source. In this case, the extractor plugin must detect the format of arbitrary payloads and be able to return NULL/no value when the payload is not supported.

Configuring Plugins in Falco

Plugins are configured in yaml via new falco.yaml properties plugins and load_plugins. Here’s an example:

plugins:
  - name: cloudtrail
    library_path: cloudtrail/plugin.so
    init_config: "..."
    open_params: "..."
  - name: json
    library_path: json/plugin.so
    init_config: ""
    open_params: ""

# Optional
load_plugins: [cloudtrail, http_json]

The plugins property in falco.yaml defines the set of plugins that can be loaded by Falco. The property contains a list of plugin definitions. Each plugin definition has:

  • name: The name of the plugin.
  • library_path: The path to the shared library of the plugin.
  • init_config: A configuration string (typically JSON) that is passed to the plugin when the plugin is initialized.
  • open_params: A configuration string that is passed to the plugin when the plugin open()s a new stream of events.

By default, every plugin in the plugins list will be loaded. If present, the load_plugins property contains specific name entries from plugins that are loaded instead.

Running Plugins in Falco

For now, when plugin support is enabled, Falco’s core support for syscalls is disabled. Additionally, only a single source plugin can be loaded at once. If multiple source plugins are loaded, falco will return an error. Any number of extractor plugins can be loaded at once.

We intend to relax these restrictions to allow running multiple source plugins at once. It will happen when we tackle the concurrency, fairness, and resource utilization issues that result from having multiple streams of events being processed by a single Falco rules engine.

Cloudtrail Plugin

In addition to the introduction of plugins support, we’ve written a source plugin cloudtrail that can read Cloudtrail logs and pass them to the rules engine as events.

The plugin can be configured to obtain log files in a number of ways, depending on the open_params value in falco.yaml:

  • A S3 bucket: s3://<S3 Bucket Name>[/<Optional Prefix>].
  • A SQS queue that passes along SNS notifications about new log files: sqs://<SQS Queue Name>
  • A local filesystem path: Any path.

The plugin also exports fields that extract information from a cloudtrail event, such as the event time, the aws region, S3 bucket/EC2 instance name, etc.

The plugin uses the same authentication mechanisms used by the AWS Go SDK:

  • Environment Variables: Specify the AWS region with AWS_REGION=xxx, the access key id with AWS_ACCESS_KEY_ID=xxx, and the secret key with AWS_SECRET_ACCESS_KEY=xxx.
  • Shared Configuration Files: Specify the AWS region in a file at $HOME/.aws/config and the credentials in a file at $HOME/.aws/credentials.

New Rules for AWS Cloudtrail

In addition to the plugin, we’ve created new Falco rules that look for suspicious, notable, and interesting activities in the Cloudtrail logs. Here is an example rule for Multi-Factor Authentication, Console Login Without MFA:

- rule: Console Login Without MFA
  desc: Detect a console login without MFA.
  condition:
    ct.name="ConsoleLogin" and ct.error=""
    and json.value[/userIdentity/type]!="AssumedRole" and json.value[/responseElements/ConsoleLogin]="Success"
    and json.value[/additionalEventData/MFAUsed]="No"
  output:
    Detected a console login without MFA (requesting user=%ct.user, requesting IP=%ct.srcip, AWS region=%ct.region)
  priority: CRITICAL
  source: aws_cloudtrail

The rule source is aws_cloudtrail. When Falco is run with a loaded Cloudtrail plugin, it will evaluate these rules because the plugin event source matches the source in the rule. The fields ct.name, ct.error, ct.srcip, etc are resolved by the plugin via a call to the plugin’s extract fields function. For example, matching ct.name with “ConsoleLogin” indicates a console login attempt. The full set of fields supported by the Cloudtrail plugin are in the README.

The JSON plugin can also be used because the underlying event payload is JSON with this format. The JSON plugin can use its json.value field, which takes a JSON pointer as argument, to extract any part of the JSON object. For example, matching json.value[/responseElements/ConsoleLogin] with “Success” represents a successful login attempt, and json.value[/additionalEventData/MFAUsed] with “No” represents a console login where MFA was not used.

When an event matches this rule, Falco will emit an alert that can be sent to a file, stdout, syslog, etc. as well as Falcosidekick:

00:40:32.000000000: Critical Detected a console login without MFA (requesting user=bob.user, requesting IP=192.0.2.24, AWS region=us-east-1)
Current Set of Cloudtrail Rules

Here is the full set of rules we’ve written so far. When the feature is fully released, they will be in a file aws_cloudtrail_rules.yaml in the falco release. The rules cover detecting any change (“Create Group”, “Create Lambda Function”), detecting specific misconfigurations/bad practices (“Console Login Without MFA”, “Delete Bucket Encryption”), and checking against lists of allowed/disallowed values (“Run Instances in Non-approved Region”). We would love additional rule contributions from the community!

  • Console Login Through Assume Role
  • Console Login Without MFA
  • Console Root Login Without MFA
  • Deactivate MFA for Root User
  • Create AWS user
  • Create Group
  • Delete Group
  • ECS Service Created
  • ECS Task Run or Started
  • Create Lambda Function
  • Update Lambda Function Code
  • Update Lambda Function Configuration
  • Run Instances
  • Run Instances in Non-approved Region
  • Delete Bucket Encryption
  • Delete Bucket Public Access Block
  • List Buckets
  • Put Bucket ACL
  • Put Bucket Policy
  • CloudTrail Trail Created
  • CloudTrail Logging Disabled

Learn More

Hopefully by now you’re excited to learn more about plugins and how to author your own plugin. During this early access period, you can take a look at the preview docs, which go into greater detail on the plugins architecture and implementation details. Also, we’ve written a developer’s guide that documents each API function, walks through the code of two example plugins, and discusses some best practices for writing plugins.

Have fun and let us know what you think!