feat(linux): Initial Linux support - #739
Merged
Merged
Conversation
Comment on lines
+20
to
+72
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Validate PR title | ||
| if: github.event_name == 'pull_request' | ||
| uses: amannn/action-semantic-pull-request@v5 | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Install Go | ||
| uses: actions/setup-go@v5 | ||
| with: | ||
| go-version: ${{ env.GO_VERSION }} | ||
| # Deliberately before clang is installed: a checkout must build from the | ||
| # committed objects alone. | ||
| - name: Build without generation tools | ||
| run: make | ||
| - name: Install clang | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y clang-${{ env.CLANG_VERSION }} llvm-${{ env.CLANG_VERSION }} | ||
| clang-${{ env.CLANG_VERSION }} --version | ||
| # The versioned packages ship no unversioned symlinks, so name both tools. | ||
| - name: Check generation drift | ||
| run: make ebpf-drift CLANG=clang-${{ env.CLANG_VERSION }} LLVM_STRIP=llvm-strip-${{ env.CLANG_VERSION }} | ||
| - name: Install golangci-lint | ||
| run: | | ||
| curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${{ env.GOLANGCI_LINT_VERSION }} | ||
| - name: Lint | ||
| run: | | ||
| export PATH=$(go env GOPATH)/bin:$PATH | ||
| make lint | ||
| - name: Unit tests | ||
| run: make test | ||
| - name: Race tests | ||
| run: make test-race | ||
| - name: Validate rules | ||
| run: | | ||
| ./cmd/fibratus/fibratus rules validate \ | ||
| --filters.rules.from-paths="rules/linux/*.yml" \ | ||
| --filters.macros.from-paths="rules/linux/macros/*.yml" | ||
| # Fails, rather than skips, when the runner cannot host the backend, so a | ||
| # green integration run always means programs actually loaded. | ||
| - name: Probe eBPF prerequisites | ||
| 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/* |
rabbitstack
force-pushed
the
linux-port
branch
4 times, most recently
from
September 27, 2026 18:38
48e8bd1 to
ab368f5
Compare
Pull in cilium/ebpf v0.20.0 for the Linux CO-RE instrumentation spike and future event source work.
Encode the hard Linux runtime contract (kernel >=5.9, runtime BTF, ringbuf, tracing/iter support) and prototype ProcessKey-based startup reconciliation with bounded pending-queue and drop metrics.
Prove sched_process_exec ringbuf capture, iter/task baseline without bpf_d_path, shared-map replacement across separately generated objects, best-effort /proc enrichment, and race-safe startup on a real kernel.
Record validated runtime/tooling prerequisites, spike proofs, Windows coupling hotspots, and the ps.Snapshotter consumer migration needed for a Linux build.
Ignore late iterator snapshots after live switch, avoid double-closing MapReplacement-owned maps, and back off on persistent ringbuf read errors.
Keep github.com/cilium/ebpf as a direct go.mod require so Linux packages resolve in CI, and use the libebpf import alias requested in review.
Move shared catalogs out of GOOS-auto-gated filenames, tag remaining Windows packages and tests, and split colorizer/ntstatus so Linux builds no longer pull golang.org/x/sys/windows.
Drop the old _windows.go param and field catalogs now that the shared names live in platform-neutral files.
Provide Linux implementations for event helpers, parameter construction/formatting, callstack basics, and PE section stubs so shared packages compile without the Windows ETW path.
Add the in-memory Linux process snapshotter, ProcessKey identity, and PS fields needed by shared formatters and filter foreach helpers.
Switch the rule engine, compiler, sequence state, and filter options to the narrow Resolver interface, and add Linux filter/action stubs so rules compile and evaluate on Linux.
Credit newly authored Linux and platform-split files correctly. Keep Nedim's copyright on adapted splits and add a second line for the Linux port work.
Align copyright headers on the Linux eBPF feasibility spike with the rest of the Linux port work.
Restore the platform-agnostic CLI entrypoints and keep only the Stats struct Linux/Windows specific. Add Linux DialPipe and hostname helpers so the shared commands compile.
Those sections are Windows-specific and should not appear in the Linux capture version constants.
Drop the shared Resolver composition. Linux Snapshotter uses uint64 PIDs, Windows keeps uint32, and event.PID becomes a platform type alias so shared rule/filter call sites stay typed correctly.
Keep mail and Slack loading shared, and move systray/eventlog loading into the Windows-specific path. Linux Config now stores Alertsenders for the shared loader.
Move common config, event-source, output, schema, transformer, and validation behavior into shared files while retaining platform-specific hooks. Add Linux schema coverage and preserve Windows callers through explicit platform field assignment.
Separate Linux/Windows event types, parameters, queues, formatting, and marshalling while retaining shared behavior. Remove Windows-only Linux stubs, distinguish process/thread clones via clone flags, and add Linux coverage.
Split field/accessor/function registries and compiler behavior by platform, remove Linux-only Windows stubs, and preserve Windows-specific tests behind file suffixes. Use platform PID types and add Linux compiler coverage.
Add one CO-RE program per family for openat/unlink/rename, connect/accept, mmap/process_vm_readv/writev, and kill/ptrace/prctl. Optional families follow the existing enable-fileio, enable-net, and enable-mem switches, process-control events stay on, and accept reads the peer address on syscall exit because the kernel fills it then.
Add a checked matrix that every Linux event type decodes with its documented parameters, plus mmap process-state and live open/rename/unlink/kill coverage on a real kernel.
tp_btf/sys_exit supplies pt_regs, so kill/ptrace/prctl no longer need an enter probe or scratch slot. Rename filename2 to aux, document the truncation bits and padding, and keep only file-backed mmaps in process state until munmap is hooked.
Keep deprecated kevt aliases on Windows so they never enter the Linux catalog, and register process, file, network, memory, and thread fields for the captured syscalls.
…ad fields Wire accessors for the syscall event matrix, gate file/net/mem on the existing enable flags, and expose syscall return, number, and truncation on evt fields.
…uncation Prove shared-name semantics, missing-value defaults, truncation bits, and that Windows-only and kevt fields cannot compile.
Drop first-class truncated fields so callers inspect the truncated parameter through evt.arg.
The Makefile script automates the process of compiling the eBPF programs, Fibratus binary, running tests, formatting, etc. The bpf2go now drops all generated Go files + ebpf bytecode inside a separate bpf package making it easier to navigate and distinguish the generated code.
Make looked for bindings next to the C sources, so every CI run rebuilt and failed the clang 18 drift check.
Revalidate PID plus start boot time from /proc before SIGKILL so a reused PID cannot be terminated.
… connect Ship an initial Linux detection set in a dedicated tree so Windows packaging and rule validation stay on the Windows catalog.
Prove Linux types are indexed, shipped rules fire, sequence lifecycle matches, and shared field fixtures compile on both platforms.
The kill, ptrace, and process_vm target was truncated through uint32 and widened as unsigned, so kill(-1) surfaced as 4294967295. Carry the argument as a signed value and expose ps.target.pid and mem.target.pid as signed, so a process group or broadcast target stays distinguishable from a process identifier.
Let the matches operator narrow the in-kernel prefilter instead of forcing default-allow, by rewriting the patterns that have an exact kernel equivalent rather than matching globs in BPF. A pattern with no wildcard becomes an exact lookup, and one ending in a single star becomes an LPM prefix. Both rewrites are exact, not merely conservative, because a trailing star spans every remaining byte including a path separator, exactly as wildcard.matchCaseSensitive does. Runs of stars collapse first, so /tmp/** arrives here as /tmp/*. A star anywhere else, or any '?', leaves the event type default-allow. Matching globs in the kernel was the obvious approach and does not survive the verifier. A faithful port of matchCaseSensitive backtracks, so every byte comparison forks a path the verifier has to walk: at eight patterns it exceeded the 8192 jump-sequence limit, and cutting it to two patterns over 64 steps still burned the full 1M instruction budget across 29668 states. Directory-aware globbing would verify more cheaply but reject paths the operator accepts in userspace, which is the silent false negative this prefilter exists to avoid. imatches stays unsupported: folding in the kernel would have to agree with unicode.ToLower on every rune.
Pin that a list of literal paths resolves entirely in the kernel's exact-match hash, since nothing about it needs the wildcard machinery. Run the privileged prefilter test twice, once with no approver and once with one. The first pass establishes that the capture path reports the open at all, so a failure in the second pass is about the prefilter and not about an enter/exit correlation that lost its scratch entry.
Several helpers in shared files are reachable only from Windows code, so a Linux build sees them as dead. Move containsEventTypes and containsFieldMatch next to their only caller in compiler_windows.go, and isNumber next to the field definitions that validate with it. Drop the Linux framePID and threadpool accessor stubs, which nothing calls now that callstack and threadpool fields are Windows-only. event.PID is an alias for the same integer the params accessor already returns on both platforms, so the conversions around MustGetPid never converted anything. This makes the packages the Linux port owns lint clean, which the next commit turns into a CI gate.
check-clang refuses to generate with the wrong clang major. The committed objects are byte-compared in CI and clang records its version in BTF, so generating with a different major rewrites every object and fails the drift check with a diff that looks like a source change. ebpf-drift turns that comparison into something runnable locally rather than a pair of CI steps, and build-linux cross-compiles from the committed objects to prove a checkout needs no clang. lint covers a narrower set than the tests. Packages outside it hold helpers only Windows reaches, so `unused` flags them on every Linux run with nothing to fix on this side.
Build before clang is installed, so the step fails if anything starts depending on generation tools at build time rather than on the committed objects. Probe the runtime contract as its own step. Without it a runner that lost runtime BTF or fell below the kernel floor produces a green suite that never loaded a program, because every test would fail the same way for a reason buried in a wrapped error. Pin clang to the major the objects were generated with, otherwise the drift check reports a diff on every unrelated change.
Covers what the backend refuses to start without, which capabilities replace running as root, and how to read the probe output. Calls out the two cases capabilities cannot fix, kernel lockdown and the pre-5.11 memlock accounting, because both surface as permission errors that look like a missing capability. Documents which attachment warnings are expected, since the optional legacy tracepoints log a permission denial on kernels that refuse a perf link on them and that is not a fault.
The shared default bound the API to localhost:8080, so a Linux install listened on the network before anyone asked it to. Defaulting to a socket under /var/run leaves reachability to filesystem permissions, and a TCP address still works when it is set explicitly. Windows keeps its existing default through the same platform hook the config file and rule paths already use.
Ships the binary, a Linux configuration, the rules, and a service unit. The BPF objects need no separate packaging because bpf2go embeds them in the binary. The unit starts as root, since attaching a syscall tracepoint reads its id from /sys/kernel/tracing and that is root-only on stock kernels. What constrains it is the bounding set, which drops everything except loading programs, attaching to tracepoints, reading other users' procfs entries, and signalling a target for the kill action. ProtectSystem is full rather than strict because strict also mounts /run read-only and the API socket lives there, and ProtectProc stays permissive because hiding other processes would silently empty ps.exe and ps.cmdline. The shipped configuration omits the Windows-only sections instead of carrying them inert, so what is in the file is what the platform reads.
The Linux Stats struct was an empty placeholder, so the command rendered a table with no rows against a sensor that publishes a dozen counters. It now carries the capture, drop, startup handover, and rule engine expvars, grouped in the order they are useful to read. Covers the endpoints the config and stats commands call over a socket and over TCP, including a socket left behind by an unclean shutdown, which would otherwise need manual cleanup before a restart. The privileged test drives the same path against a live capture, since unit tests can only prove the counters exist, not that a running sensor moves them.
Covers the package layout, running in the foreground against a filter, and the service. Notes that an idle host with rules enabled is expected to be quiet, since only matching events reach the outputs and silence otherwise reads as a broken install. Records why the unit runs as root and what the bounding set leaves it.
rabbitstack
force-pushed
the
linux-port
branch
from
September 28, 2026 16:47
ab368f5 to
639c8b1
Compare
Apply conflict resolutions and other activities to foment a stricter, cleaner, and more idiomatic code for multi platform support.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the purpose of this PR / why it is needed?
This PR brings the initial Linux runtime detection capabilities, with support for file system, process, memory and network telemetry.
Kudos to @mostafa for the hard work.
What type of change does this PR introduce?
/kind feature (non-breaking change which adds functionality)
Any specific area of the project related to this PR?
/area instrumentation
/area telemetry
Special notes for the reviewer
Does this PR introduce a user-facing change?
This is still in experimental stage, so the public announcement will follow after extensive testing and rule coverage.