Skip to content

feat(rules): Add Linux detection rules and a signal-based kill action - #735

Merged
rabbitstack merged 4 commits into
rabbitstack:linux-portfrom
mostafa:feat/linux-detection
Sep 21, 2026
Merged

rabbitstack merged 4 commits into
rabbitstack:linux-portfrom
mostafa:feat/linux-detection

Conversation

@mostafa

@mostafa mostafa commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

What is the purpose of this PR / why it is needed?

Linux capture emits process, file, network, memory and signal events, and the filter catalog can express them, but nothing acts on them yet. The rule engine had no Linux ruleset to load and the kill action returned "not implemented". This PR closes that gap so a Linux deployment can detect and respond.

It ships three things:

A signal-based kill action. Terminating by PID alone is unsafe because the identifier can be recycled between the moment a rule matches and the moment the signal is delivered. The action opens a pidfd first, which pins the identifier for the lifetime of the descriptor, then compares the live /proc/<pid>/stat start time against the start boot time captured on the event, and only then delivers SIGKILL through pidfd_send_signal. A process that has already exited is treated as success, mirroring the Windows behaviour on ERROR_INVALID_PARAMETER. A mismatched start time refuses to signal and reports why. pidfd_open arrived in 5.3, below the runtime floor the loader already enforces.

action.Kill now takes the ActionContext rather than a pid slice, because the Linux implementation needs the process start time that only the events carry. The Windows implementation keeps resolving pids exactly as before. This also removes a MustGetPid call from the engine's logging path, which could panic on an event that carries no pid parameter.

An initial Linux ruleset. Four rules covering execution from world-writable directories, ptrace attach, SIGKILL against another process, and a script interpreter opening an outbound connection after execution, plus the macros they share. They live under rules/linux/ because every rule under rules/ is compiled against the Windows event catalog by fibratus rules validate during packaging, and Linux event names would fail there. The MSI packaging step excludes the directory for the same reason.

A signed target identifier. The kill, ptrace and process_vm_* target was truncated through uint32 and widened as unsigned, so kill(-1, SIGKILL) was recorded as 4294967295. pid_t is signed and its sign selects the scope of the signal, so the value is now carried signed and ps.target.pid and mem.target.pid are exposed as signed. Process group and broadcast targets stay distinguishable from a process identifier, and ps.target.pid < 0 selects them. Note that the filter lexer does not accept negative literals, so ps.target.pid = -1 will not parse; the relational form is the one to use, and the field documentation says so.

What type of change does this PR introduce?


/kind feature (non-breaking change which adds functionality)

/kind bug-fix (non-breaking change which fixes an issue)

Any specific area of the project related to this PR?


/area rule-engine

/area rules

/area event

/area tests

Special notes for the reviewer

The engine already indexed Linux event types and categories through NameToTypes, so no change was needed there. The tests assert it rather than assuming it.

Two decisions worth a second opinion:

  • The execve macro requires evt.retval = 0, so a failed execution does not alert. The ptrace, kill and connect macros deliberately do not gate on the return value, because a denied ptrace attach or a refused connection carries the same intent as a successful one. Each macro says which behaviour it has.
  • The temporary-directory rule uses matches rather than imatches. Linux paths are case-sensitive and case-insensitive globbing would widen the rule beyond what it claims.

The Linux workflow now runs make test, so the package list has a single definition shared with local runs, and it gained pkg/rules/action, pkg/rules, pkg/filter and pkg/config. A rules validate step runs the built binary against the shipped Linux ruleset and reports no warnings.

Verified on Linux with the full unit suite, including an end-to-end test that spawns a real process, terminates it through the pidfd path, and asserts a process whose captured start time disagrees is left alone. GOOS=windows go build ./... and the Windows rule packaging path are unaffected.

Does this PR introduce a user-facing change?


The kill action works on Linux, where it sends SIGKILL after confirming the target is still the process the rule matched on. Linux installations gain an initial ruleset covering execution from world-writable directories, ptrace attach, SIGKILL against another process, and interpreter outbound connections.

ps.target.pid and mem.target.pid are now signed. Filters comparing them against a process identifier are unaffected; a target that denotes a process group or a broadcast now reads as a negative value instead of a large unsigned one.

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.

@rabbitstack rabbitstack left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@rabbitstack
rabbitstack merged commit 3ec5d55 into rabbitstack:linux-port Sep 21, 2026
1 check passed
@mostafa
mostafa deleted the feat/linux-detection branch September 21, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants