Extend Falco inputs by creating a Plugin: the basics
This post is is part of a series of articles about
How to develop Falco plugins
. It's adressed to anybody who would like to understand how plugins are written and want to contribute. See other articles:
- What are Plugins?
- Developers Guide
- Our plugin
- Configuration
- Rules
- Test and Results
- Sources
- To Go further
- Conclusion
What are Plugins?
Before starting, you should take look at these posts to know more about what Plugins are, what they can do and what concepts are behind them:
- Falco Plugins Early Access
- Falco 0.31.0 a.k.a. "the Gyrfalcon"
- Announcing Plugins and Cloud Security with Falco
Developers Guide
This post has not for the purpose to replace the official documentation, it's a step-by-step example to get you to know minimal requirements for having a running plugin. For details, please read the developers guide.
Our plugin
For this example, we'll create a plugin for docker events
from a local docker daemon
. It is a basic example of an event stream
with a basic format and without specific authentication.
See an example of events we'll be able to gather and apply Falco
rules over:
2022-02-08T10:58:56.370816183+01:00 container create e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6 (image=alpine, name=confident_kirch)
2022-02-08T10:58:56.371818906+01:00 container attach e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6 (image=alpine, name=confident_kirch)
2022-02-08T10:58:56.482094215+01:00 network connect 5864a44bccca4e0963dfe9c3087919bf8f8e5c3aa7db33dd6d9ae7138c5ee3f3 (container=e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6, name=bridge, type=bridge)
2022-02-08T10:58:56.804166856+01:00 container start e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6 (image=alpine, name=confident_kirch)
2022-02-08T10:58:56.831912702+01:00 container die e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6 (exitCode=0, image=alpine, name=confident_kirch)
2022-02-08T10:58:57.072125878+01:00 network disconnect 5864a44bccca4e0963dfe9c3087919bf8f8e5c3aa7db33dd6d9ae7138c5ee3f3 (container=e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6, name=bridge, type=bridge)
2022-02-08T10:58:57.132390363+01:00 container destroy e327f1fa52a90d79421e416aed60e6de6872231f31101a1cc63401e90cef4bd6 (image=alpine, name=confident_kirch)
For reducing the complixity to communicate with docker daemon
, we'll use the official docker sdk
.
Requirements
For this post and following ones, we'll develop in Go
, because it's the most common language for that purpose, a lot of member of the Falco
Community and tools for Falco
are already using it. We'll also use the Go Plugin SDK the maintainer provide for enhancing the experience with plugins.
The only requirements for this examples are:
- a
docker daemon
running in your local system falco 0.31
installed in your local systemgo
>= 1.17
Pieces of code
The imports
Despite basic Go
modules, we'll have to import the different modules from plugin-sdk-go
and for retrieving docker events
:
We'll import these different components of plugin-sdk-go
in almost every plugin we'll write. They're really convenient and provide a much easier way to deal with the Falco plugin framework.
The structures
Two structures are mandatory and must respect interface
s of the SDK:
DockerPlugin
represents our plugin that will be loaded by the framework. Embeddingplugins.BasePlugin
allows respecting thePlugin interface
of the SDKDockerInstance
represents a stream of events opened by the framework with the plugin. Embeddingsource.BaseInstance
allows to respect theInstance interface
of the SDK.
We can add extra attributes for both structs
for any purpose we need, like for configuration. In this example, we have FlushInterval
that represents the frequency of events sent to the instance
by the docker
client we'll create. This attribute will have a default value that can be overridden by init_config
in plugins
section.
The functions and methods
main()
The main()
function is mandatory for any go
program, but because we'll build the plugin
as a library for the Falco plugin framework which is written in C
, we can let it empty.
init()
The init()
function is used for registering our plugin to the Falco plugin framework
, as a source
and extractor
.
Info()
This method is mandatory, and all plugins must respect that. It allows the Falco plugin framework
to have all intel about the plugin itself:
Here some details:
ID
: must be unique among all plugins, it's used by the framework in captures to know whichplugin
is thesource
of events. It's also important for avoiding collisions if you want to share your plugin in the registry. See documentation for more details.Name
: the name of our plugin, will be used inplugins
section offalco.yaml
EventSource
: this represents the value we'll set inFalco
rules for mapping, in our case, all rules we'll set will havesource: docker
Init()
This method (:warning: different from the function init()
) will be the first one called by the Falco plugin framework, we use it for setting default values for DockerPlugin
attributes. In our case, these default values are overridden by the value of init_config:
from falco.yaml
config file, see .
The string argument config
of the method is the content of init_config
, we use JSON syntax in this example for leveraging the Go
capacity to map JSON fields with a structure attribute with tags. A simple string may also work, as long as your code parses it and correctly sets the attributes.
Fields()
This method declares all to the Falco plugin framework all fields
that will be available for the rules, with their names and their types.
String()
Even if this method is mandatory, it's not used by Falco
for now but must be set up for future usage. It simply retrieves the events, it can be JSON or any format as long it contains the whole content of the source event.
Extract()
This method is called by the Falco plugin framework for getting the values of fields
:
:warning: try to not overlap the
fields
created by other plugins, for eg, in this example we can usedocker.
prefix becauseFalco
libs usecontainer.
fields which are more generic, so we've not to conflict.
For this plugin, we use the modules provided by docker sdk
, all retrieved events will be Unmarshaled into the events.Message
struct which simplifies the mapping.
Open()
This methods is used by the Falco plugin framework for opening a new stream
of events, what is called an instance
. The current implementation creates only one instance
per plugin but it's possible in future that same plugin
allows to open several streams, and so several instances
at once.
NextBatch()
The Falco plugin framework will call this method to get a batch of events collected by our plugin
.
:warning: this blog post concerns the creation of a plugin, we'll not describe the logic to get the events from the
docker daemon
with thedocker sdk
.
- this methods returns the number of events in the batch and an error
- the max size for a batch is
evts.Len()
- the plugin configuration can be retrieved with
pState.(*DockerPlugin)
- for each "slot" of the batch, we have to get it
evt := evts.Get(n)
and then set its valueevt.Writer().Write(s)
Complete plugin
Build
The plugin is built as c-shared
library, to get a .so
:
Configuration
Now we have our plugin, we must declare it to Falco
in falco.yaml
:
For more details about this configuration, the documentation is here.
Rules
We create a simple rule, for checking that fields
and source
work as expected:
Test and Results
Let's run Falco
with our configuration and rules files:
:tada: It works!
Sources
All files of this post can be found on this repo.
To Go further
Once your plugin is done, you can share it with the community, by registrating it. The next post Extend Falco inputs by creating a Plugin: Register the plugin will guide guide through the process.
Conclusion
With this first post, you should have now all basics for creating your own plugin. The following posts will describe more advanced use-cases like collecting events from Cloud Services. Stay tuned :wink:.
You can find us in the Falco community. Please feel free to reach out to us for any questions, suggestions, or even for a friendly chat!
If you would like to find out more about Falco:
- Get started in Falco.org
- Plugin Documentation
- Plugin Developer Guide
- Plugin registry
- Check out the Falco project in GitHub
- Get involved in the Falco community
- Meet the maintainers on the Falco Slack
- Follow @falco_org on Twitter