Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/linux-port.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ jobs:
run: sudo -E env "PATH=$PATH" go test -tags ebpf_integration -count=1 -run TestPrerequisitesAreMet -v ./internal/ebpf
- name: Privileged process source
run: sudo -E env "PATH=$PATH" make test-integration
- name: Package
run: make pkg
- uses: actions/upload-artifact@v4
with:
name: fibratus-linux-packages
path: build/pkg/*
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ build/msi/*.msi

.idea
filaments/__pycache__

# Linux packages
build/pkg/
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ SHELL := /bin/bash

BPF2GO_VERSION := v0.20.0
GOLANGCI_LINT_VERSION := v2.9.0
NFPM_VERSION := v2.43.0

# The committed objects are byte-compared in CI, and clang encodes its version
# into BTF, so generating with a different major produces a spurious diff.
Expand Down Expand Up @@ -138,6 +139,17 @@ LINT_PKGS := \
lint:
golangci-lint run $(LINT_PKGS)

VERSION ?= 0.0.0
PKG_DIR := build/pkg
NFPM ?= go run github.com/goreleaser/nfpm/v2/cmd/nfpm@$(NFPM_VERSION)

.PHONY: pkg
pkg: build
@mkdir -p $(PKG_DIR)
VERSION=$(VERSION) $(NFPM) package --config build/linux/nfpm.yaml --packager deb --target $(PKG_DIR)
VERSION=$(VERSION) $(NFPM) package --config build/linux/nfpm.yaml --packager rpm --target $(PKG_DIR)

.PHONY: clean
clean:
rm -f cmd/fibratus/fibratus
rm -rf $(PKG_DIR)
44 changes: 44 additions & 0 deletions build/linux/fibratus.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[Unit]
Description=Fibratus
Documentation=https://www.fibratus.io
After=network.target

[Service]
Type=simple
Comment thread
rabbitstack marked this conversation as resolved.
Resolved
ExecStart=/usr/bin/fibratus run
Restart=on-failure
RestartSec=5s

# Fibratus starts as root because attaching a syscall tracepoint reads its id
# from /sys/kernel/tracing, which is root-only on stock distributions. The
# bounding set is what actually constrains it: everything below is dropped, so
# the process keeps only what the backend and the kill action need.
#
# CAP_BPF load programs, create and update maps
# CAP_PERFMON attach to tracepoints
# CAP_SYS_PTRACE read /proc/<pid>/exe and cmdline of other users' processes
# CAP_KILL signal a target, needed only by the kill rule action
#
# CAP_BPF and CAP_PERFMON were split out of CAP_SYS_ADMIN in 5.8. On a kernel
# that predates the split, replace both with CAP_SYS_ADMIN.
CapabilityBoundingSet=CAP_BPF CAP_PERFMON CAP_SYS_PTRACE CAP_KILL
AmbientCapabilities=CAP_BPF CAP_PERFMON CAP_SYS_PTRACE CAP_KILL
NoNewPrivileges=yes

# full rather than strict: strict would also mount /run read-only and the API
# socket lives there.
ProtectSystem=full
ProtectHome=yes
PrivateTmp=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes

# Process enrichment reads other processes' procfs entries, so this must stay
# permissive. Hiding them would silently empty ps.exe and ps.cmdline.
ProtectProc=default

[Install]
WantedBy=multi-user.target
46 changes: 46 additions & 0 deletions build/linux/nfpm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: fibratus
Comment thread
rabbitstack marked this conversation as resolved.
Resolved
arch: amd64
platform: linux
version: ${VERSION}
section: admin
priority: optional
maintainer: Nedim Sabic Sabic <https://fibratus.io>
description: |
Security sensor for realtime threat detection and protection
vendor: Fibratus
homepage: https://fibratus.io
license: Apache-2.0

# The BPF objects are embedded in the binary at compile time, so there is
# nothing to ship alongside it.
contents:
- src: ./cmd/fibratus/fibratus
dst: /usr/bin/fibratus

- src: ./configs/fibratus-linux.yml
dst: /etc/fibratus/fibratus.yml
type: config|noreplace

- src: ./rules/linux/*.yml
dst: /etc/fibratus/rules/

- src: ./rules/linux/macros/macros.yml
dst: /etc/fibratus/rules/macros/macros.yml

- src: ./build/linux/fibratus.service
dst: /lib/systemd/system/fibratus.service

overrides:
rpm:
contents:
- src: ./cmd/fibratus/fibratus
dst: /usr/bin/fibratus
- src: ./configs/fibratus-linux.yml
dst: /etc/fibratus/fibratus.yml
type: config|noreplace
- src: ./rules/linux/*.yml
dst: /etc/fibratus/rules/
- src: ./rules/linux/macros/macros.yml
dst: /etc/fibratus/rules/macros/macros.yml
- src: ./build/linux/fibratus.service
dst: /usr/lib/systemd/system/fibratus.service
28 changes: 27 additions & 1 deletion cmd/fibratus/app/stats/stats_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,30 @@
package stats

// Stats stores runtime statistics that are retrieved from the expvar endpoint.
type Stats struct{}
// Field order is the order the table renders in, so counters are grouped by the
// stage they describe: capture, then the kernel-side drops worth alerting on,
// then the startup handover, then the rule engine.
type Stats struct {
EventsProcessed int `json:"ebpf.events.processed"`
EventsExcluded int `json:"ebpf.events.excluded"`
EventsUnknown int `json:"ebpf.events.unknown"`
EventsParseErrs int `json:"ebpf.events.parse.errors"`

RingbufDrops int `json:"ebpf.ringbuf.drops"`
ApproverDrops int `json:"ebpf.approver.drops"`

EnrichmentMiss int `json:"ebpf.enrichment.miss"`

StartupPendingQueued int `json:"ebpf.startup.pending.queued"`
StartupPendingDropped int `json:"ebpf.startup.pending.dropped"`
StartupReplayApplied int `json:"ebpf.startup.replay.applied"`
StartupSnapshotUpserts int `json:"ebpf.startup.snapshot.upserts"`
StartupSnapshotLate int `json:"ebpf.startup.snapshot.late"`

FiltersCount int `json:"filter.filters.count"`
FilterMatches map[string]int `json:"filter.matches"`

AggregatorBatchEvents int `json:"aggregator.batch.events"`
AggregatorFlushesCount int `json:"aggregator.flushes.count"`
AggregatorEventErrors int `json:"aggregator.event.errors"`
}
132 changes: 132 additions & 0 deletions configs/fibratus-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Fibratus configuration for Linux.
#
# Only the settings that apply to the eBPF backend are listed here. Windows-only
# sections, such as filaments, PE introspection, YARA scanning, and handle or
# evasion telemetry, have no effect on this platform and are left out rather
# than shipped inert.

# =============================== Event source =========================================

eventsource:
# Process telemetry is always collected. Execve, exit, and clone build the
# process state every other event is correlated against, so they cannot be
# disabled.

# Collects openat, unlink, and rename. This is the highest-volume family by a
# wide margin on a busy host.
enable-fileio: true

# Collects connect and accept.
enable-net: true

# Collects mmap, process_vm_readv, and process_vm_writev.
enable-mem: true

# Drops events before they reach the aggregator, by event name or by the
# image name of the process that produced them.
#blacklist:
# events:
# - openat
# images:
# - fibratus

# =============================== Aggregator ===========================================

aggregator:
# Determines the flush period that triggers the flushing of the event batches to output sinks
flush-period: 500ms

# Represents the max time to wait before announcing failed flushing of enqueued events when fibratus
# is stopped
flush-timeout: 4s

# =============================== Filters and rules ====================================

filters:
rules:
# Enables the rule engine. With no rules loaded every event reaches the
# outputs; with rules loaded only matching events do.
enabled: true

# Rule definitions. The package installs the shipped Linux rules here.
from-paths:
- /etc/fibratus/rules/*.yml

macros:
from-paths:
- /etc/fibratus/rules/macros/*.yml

# =============================== API ==================================================

api:
# The transport for the API HTTP server. A unix:// path keeps the API off the
# network and leaves access to the socket's filesystem permissions. A host:port
# value exposes it over TCP instead, e.g. 192.168.1.32:8084.
transport: unix:///var/run/fibratus.sock

# Represents the timeout interval for the HTTP server responses.
timeout: 5s

# =============================== Logging ==============================================

logging:
# Specifies the minimum allowed log level. Anything logged below this log level will
# not get dumped to a file or stdout stream
level: info

# Represents the log file format
#formatter: json

# Under systemd, logging to stdout routes through the journal
log-stdout: true

# Alternative directory for log files
#path: /var/log/fibratus

# =============================== Outputs ==============================================

output:
# Console output writes the event to the standard output stream.
console:
enabled: true

# Indicates if the console output is colorized
colorize: true

# The "pretty" format replaces specifiers in the template, "json" emits the
# event as a raw JSON string
format: pretty

# Elasticsearch output indexes event bulks into Elasticsearch clusters.
elasticsearch:
enabled: false
#servers:
# - http://localhost:9200

# HTTP output posts event batches to an HTTP endpoint.
http:
enabled: false
#endpoint: http://localhost:8081

# AMQP output publishes events to a message broker.
amqp:
enabled: false
#url: amqp://localhost:5672

# =============================== Alert senders ========================================

alertsenders:
mail:
enabled: false
#host: smtp.gmail.com
#port: 587

slack:
enabled: false
#url: https://hooks.slack.com/services/...

# =============================== General ==============================================

# Forwards events to the output sinks without running the rule engine or
# building the full process state.
forward: false
63 changes: 61 additions & 2 deletions docs/setup/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,64 @@

On Linux, Fibratus collects telemetry through eBPF CO-RE programs attached to syscall and scheduler tracepoints, instead of ETW. The rule engine, filters, actions, and outputs are the same on both platforms.

## Installing

Packages are built for `x86_64` and carry the binary, a configuration, the shipped rules, and a systemd unit. The BPF objects are compiled into the binary, so there is nothing else to install and no compiler is needed on the host.

```
# Debian, Ubuntu
sudo dpkg -i fibratus_<version>_amd64.deb

# RHEL, Fedora, Rocky, openSUSE
sudo rpm -i fibratus-<version>-1.x86_64.rpm
```

| Path | Contents |
| --- | --- |
| `/usr/bin/fibratus` | The binary |
| `/etc/fibratus/fibratus.yml` | Configuration, preserved across upgrades |
| `/etc/fibratus/rules/` | Shipped detection rules |
| `/etc/fibratus/rules/macros/` | Macros the rules expand |
| `/lib/systemd/system/fibratus.service` | Service unit, `/usr/lib/...` on RPM distributions |

## Quick start

Check the host before starting anything. This refuses to run and names every unmet requirement at once, rather than failing later inside the capture:

```
sudo fibratus rules validate
```

Run in the foreground to see events as they arrive:

```
sudo fibratus run
```

Narrow it with a filter expression, using the same language the rules use:

```
sudo fibratus run "evt.name = 'openat' and file.path startswith '/etc'"
```

Then run it as a service:

```
sudo systemctl enable --now fibratus
sudo systemctl status fibratus
sudo journalctl -u fibratus -f
```

The unit logs to the journal. With the rule engine enabled, only events matching a rule reach the outputs, so an idle host is expected to be quiet; drop the rules or set `filters.rules.enabled` to `false` to see the raw stream.

Inspect a running instance over the API socket:

```
sudo fibratus config # the configuration as loaded
sudo fibratus stats # capture, drop, and rule engine counters
sudo fibratus list events
```

## Runtime prerequisites

These are hard requirements. Fibratus probes each one at startup and refuses to run if any is missing, rather than degrading to partial telemetry.
Expand All @@ -19,7 +77,7 @@ Nothing needs to be compiled at install time. The BPF objects are generated ahea

## Capabilities

Running as root works. To run unprivileged, grant:
The packaged unit starts Fibratus as root, because attaching a syscall tracepoint reads its id from `/sys/kernel/tracing`, which is root-only on stock distributions. What limits it is the bounding set in the unit, which drops everything except the four capabilities below. Granting these to an unprivileged invocation works too, provided tracefs is readable:

| Capability | Needed for |
| --- | --- |
Expand Down Expand Up @@ -80,7 +138,8 @@ Counters are exported as expvars and available through `fibratus stats`.
Ordinary builds consume the committed objects:

```
make
make # the binary
make pkg # deb and rpm into build/pkg
```

Regenerating them needs clang, pinned to the major the committed objects were built with, because clang records its version in BTF and a different major rewrites every object:
Expand Down
Loading
Loading