Skip to content

Commit 3f1b2c5

Browse files
committed
Initial commit
0 parents  commit 3f1b2c5

27 files changed

Lines changed: 39016 additions & 0 deletions

Cargo.lock

Lines changed: 2990 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
[package]
2+
name = "edera_falco_plugin"
3+
description = "An Edera plugin for the Falco runtime security system"
4+
version = "0.0.1"
5+
license = "Apache-2.0"
6+
homepage = "https://github.com/edera-dev/falco_plugin"
7+
repository = "https://github.com/edera-dev/falco_plugin"
8+
edition = "2024"
9+
keywords = ["falco", "libscap", "eBPF", "plugin", "bindings"]
10+
exclude = [
11+
".github",
12+
".release-plz.toml",
13+
"**/*.orig"
14+
]
15+
16+
[lib]
17+
# NOTE - this plugin MUST be built as
18+
# as `cdylib` to be used. Unfortunately,
19+
# `cdylib` crate types are incompatible with
20+
# `x86_64-unknown-linux-musl` rust targets.
21+
#
22+
# Therefore, this crate's release builds MUST
23+
# currently be built against a dynamically-linked GNU target
24+
# to produce usable `.so` binaries.
25+
#
26+
# See: https://github.com/rust-lang/rust/issues/59302 for details.
27+
#
28+
# RUSTFLAGS="-C target-feature=-crt-static" is a bad workaround,
29+
# because when used with a musl target it not only results in
30+
# a non-loadable plugin binary, it applies to the other crates
31+
# in the workspace as well, which we do not want.
32+
#
33+
# To hack around this for dev builds, we specify multiple crate types:
34+
# `rlib` and `cdylib`. If you specify just `cdylib`, `musl` target builds
35+
# will error out. If you specify both types, then `musl` target builds will
36+
# generate an (unusable/invalid) `rlib` binary, but will just warn instead of
37+
# error on the `cdylib` build not being supported, which is what we want.
38+
crate-type = ["rlib", "cdylib"]
39+
40+
[dependencies]
41+
anyhow = "1.0.99"
42+
async-stream = "0.3.6"
43+
aya = "0.13"
44+
caps = "0.5.5"
45+
dns-lookup = "3.0.1"
46+
falco_plugin = { version = "0.5.1" }
47+
falco_plugin_api = { version = "0.5.1" }
48+
falco_event = { version = "0.5.1" }
49+
env_logger = "0.11.8"
50+
hyper = "1.8.0"
51+
hyper-util = "0.1.18"
52+
libc = "0.2.175"
53+
libscap-bindings = { version = "0.0.1", default-features = false }
54+
log = "0.4.27"
55+
nix = { version = "0.30.1", features = ["signal"] }
56+
pin-project-lite = "0.2.16"
57+
prost = "0.14.1"
58+
strum_macros = "0.27"
59+
tokio = { version = "1.48.0", features = ["full"] }
60+
tokio-stream = { version="0.1.17", features = ["sync"] }
61+
tonic = "0.14.2"
62+
tower = "0.5.2"
63+
url = "2.5.7"
64+
uuid = { version = "1.18.1", features = ["v4"] }
65+
pbjson = "0.8.0"
66+
pbjson-types = "0.8.0"
67+
serde = { version = "1.0.219", features = ["derive"] }
68+
tonic-prost = { version = "0.14.2" }

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Edera Falco plugin
2+
3+
This crate implements a [Falco plugin](https://falco.org/docs/concepts/plugins/) for Edera, using Falco's Rust plugin SDK.
4+
5+
The purpose of this plugin is to stream `libscap`-encoded syscall events out of Edera zones, and into the Falco rules engine, so Falco rules can be written against syscall events and other state from inside of Edera zones.
6+
7+
This is necessary because Edera zones are microvms running under a hypervisor, and without this plugin, syscall events from inside Edera zones would be invisible to the host-side Falco runtime.
8+
9+
> [!NOTE]
10+
> This plugin **must** be built as a `cdylib` explicitly, so the Falco runtime can load it as though it were a C library. Note that currently upstream Rust does not support building `cdylib` with the `x86_64-unknown-linux-musl` target, see: https://github.com/rust-lang/rust/issues/59302 and https://github.com/edera-dev/protect/pull/1823 - building this crate with the `x86_64-unknown-linux-musl` target will result in a (useless to Falco) `rlib` output, only building with `x86_64-unknown-linux-gnu` is supported.
11+
12+
13+
## Architecture
14+
15+
Falco's plugin APIs exposes several kinds of plugins, each of which own a unique step (or "capability") in the event sourcing chain. It is not uncommon for "a Falco plugin" to actually be composed of a chain of smaller plugins that each implement some or all of these capabilities, and this plugin is no exeption.
16+
17+
Currently, we implement:
18+
19+
- The `base` plugin: This is the core `EderaPlugin`, and it maintains state to all the capability plugins.
20+
- The `source` plugin. This plugin capability is responsible for talking to a running Edera daemon over `/var/lib/edera/protect/daemon.socket`, and watching for Edera zones. When a zone is discovered, the `source` plugin sends a message to the zone over the Edera IDM channel, asking the zone to begin streaming syscall events from the local zone kernel, encoded in [`libscap`'s binary format](https://falco.org/docs/concepts/event-sources/kernel/architecture/), back to it over the same channel. The zone replies with a snapshot of its local process state, including all current threads and their open file descriptors, and the plugin begins maintaining internal state for that zone by combining that initial state with the ongoing syscall events it sees. See [libscap-rs](../libscap-rs) for details on how these syscall events and initial state snapshots are scraped in-zone. Note that the state tracking is invalidated if a zone undergoes a CPU hotplug event - this is a core Falco limitation that the Edera plugin also shares. Falco will terminate if a CPU hotplug event is detected, but in our case, if a CPU hotplug event is detected, we simply disconnect from the zone and reconnect, reseeding the state, and carry on capturing.
21+
- The `parse` plugin. The `parse` plugin is responsible for hydrating the raw `scap`-encoded events into internal plugin state, for consumption by plugins later on in the chain.
22+
- The `extract` plugin. The `extract` plugin is responsible for exposing the list of valid "queryable properties" about each event and its context to the Falco rules engine. The goal is to expose analogs for every field that "regular Falco" would expose on host-generated syscall events. See https://docs.edera.dev/guides/observability/falco-integration/#available-event-fields for the currently-supported list.
23+
24+
25+
## Usage and installation
26+
27+
The `cdylib` generated by this crate is currently only packaged with the Edera installer, and deployed to `/var/lib/edera/protect/falco/libedera_falco_plugin.so`. Please see https://docs.edera.dev/guides/observability/falco-integration/ for details on how to install Falco and configure it to use this plugin.

hack/proto/buf.gen.control.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# See https://buf.build/docs/configuration/v2/buf-gen-yaml/ for documentation on the fields
2+
version: v2
3+
managed:
4+
enabled: true
5+
plugins:
6+
- local: protoc-gen-prost
7+
out: crates/proto/src/generated/protect/control
8+
opt:
9+
- compile_well_known_types
10+
- extern_path=.google.protobuf=::pbjson_types
11+
- local: protoc-gen-prost-serde
12+
out: crates/proto/src/generated/protect/control
13+
- local: protoc-gen-tonic
14+
out: crates/proto/src/generated/protect/control
15+
inputs:
16+
- module: buf.build/edera-dev/protect

hack/proto/gen.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env sh
2+
set -e
3+
4+
# shellcheck source-path=SCRIPTDIR source=versions.sh
5+
. "$(dirname "${0}")/versions.sh"
6+
7+
FAIL=0
8+
9+
if ! command -v buf >/dev/null 2>&1; then
10+
echo "Generating the protobuf files requires buf. Install it using \"${INSTALL_BUF}\""
11+
FAIL=1
12+
elif [ "v$(buf --version)" != "${BUF_VERSION}" ]; then
13+
echo "Generating the protobuf files requires buf ${BUF_VERSION}, you have v$(buf --version) installed. Install it using \"${INSTALL_BUF}\""
14+
FAIL=1
15+
fi
16+
17+
if ! command -v protoc-gen-prost >/dev/null 2>&1; then
18+
echo "Generating the protobuf files requires protoc-gen-prost v${PROTOC_GEN_PROST_VERSION}. Install it using \"${INSTALL_PROTOC_GEN_PROST}\""
19+
FAIL=1
20+
elif [ "$(protoc-gen-prost --version)" != "${PROTOC_GEN_PROST_VERSION}" ]; then
21+
echo "Generating the protobuf files requires protoc-gen-prost v${PROTOC_GEN_PROST_VERSION}, you have v$(protoc-gen-prost --version) installed. Install it using \"${INSTALL_PROTOC_GEN_PROST}\""
22+
FAIL=1
23+
fi
24+
25+
if ! command -v protoc-gen-tonic >/dev/null 2>&1; then
26+
echo "Generating the protobuf files requires protoc-gen-tonic v${PROTOC_GEN_TONIC_VERSION}. Install it using \"${INSTALL_PROTOC_GEN_TONIC}\""
27+
FAIL=1
28+
elif [ "$(protoc-gen-tonic --version)" != "${PROTOC_GEN_TONIC_VERSION}" ]; then
29+
echo "Generating the protobuf files requires protoc-gen-tonic v${PROTOC_GEN_TONIC_VERSION}, you have v$(protoc-gen-tonic --version) installed. Install it using \"${INSTALL_PROTOC_GEN_TONIC}\""
30+
FAIL=1
31+
fi
32+
33+
if ! command -v protoc-gen-prost-serde >/dev/null 2>&1; then
34+
echo "Generating the protobuf files requires protoc-gen-prost-serde v${PROTOC_GEN_PROST_SERDE_VERSION}. Install it using \"${INSTALL_PROTOC_GEN_PROST_SERDE}\""
35+
FAIL=1
36+
elif [ "$(protoc-gen-prost-serde --version)" != "${PROTOC_GEN_PROST_SERDE_VERSION}" ]; then
37+
echo "Generating the protobuf files requires protoc-gen-prost-serde v${PROTOC_GEN_PROST_SERDE_VERSION}, you have v$(protoc-gen-prost-serde --version) installed. Install it using \"${INSTALL_PROTOC_GEN_PROST_SERDE}\""
38+
FAIL=1
39+
fi
40+
41+
[ "${FAIL}" = "1" ] && exit 1
42+
43+
SCRIPTS_DIR="$(cd -- "$(dirname -- "${0}")" >/dev/null && pwd)"
44+
REPO_ROOT="$(dirname "$(dirname "${SCRIPTS_DIR}")")"
45+
46+
cd "${REPO_ROOT}" >/dev/null || exit
47+
48+
# We need to clean the directories where the protos are generated into
49+
# so that files that should no longer be generated are deleted.
50+
rm -rf crates/proto/src/generated
51+
rm -rf crates/proto-vendor/src/generated
52+
53+
# One generate command per protobuf package. This allows us to create the protobuf hierarchy
54+
# that the prost generated protobuf files assume, instead of having all the files
55+
# generated into the same folder.
56+
57+
buf generate --template "${SCRIPTS_DIR}/buf.gen.control.yaml"

hack/proto/versions.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env sh
2+
# shellcheck disable=SC2034
3+
set -e
4+
5+
PROTOC_GEN_PROST_REPOSITORY="https://github.com/edera-dev/protoc-gen-prost.git"
6+
PROTOC_GEN_PROST_COMMIT=94b56ce1d898398167a0e38b35e79658a3622972
7+
BUF_COMMIT=69a3227530199878cc50df6ce889b176b498e077
8+
9+
BUF_VERSION=v1.56.0
10+
PROTOC_GEN_PROST_VERSION=0.4.1
11+
PROTOC_GEN_TONIC_VERSION=0.4.2
12+
PROTOC_GEN_PROST_SERDE_VERSION=0.3.2
13+
14+
INSTALL_BUF="go install github.com/bufbuild/buf/cmd/buf@${BUF_COMMIT}"
15+
INSTALL_PROTOC_GEN_PROST="cargo install --git ${PROTOC_GEN_PROST_REPOSITORY} --rev ${PROTOC_GEN_PROST_COMMIT} protoc-gen-prost"
16+
INSTALL_PROTOC_GEN_TONIC="cargo install --git ${PROTOC_GEN_PROST_REPOSITORY} --rev ${PROTOC_GEN_PROST_COMMIT} protoc-gen-tonic"
17+
INSTALL_PROTOC_GEN_PROST_SERDE="cargo install --git ${PROTOC_GEN_PROST_REPOSITORY} --rev ${PROTOC_GEN_PROST_COMMIT} protoc-gen-prost-serde"

0 commit comments

Comments
 (0)