Skip to content

ROX-30296: track POSIX ACL changes via inode_set_acl LSM hook#878

Merged
Stringy merged 15 commits into
mainfrom
giles/ROX-30296-track-acl
Jul 7, 2026
Merged

ROX-30296: track POSIX ACL changes via inode_set_acl LSM hook#878
Stringy merged 15 commits into
mainfrom
giles/ROX-30296-track-acl

Conversation

@Stringy

@Stringy Stringy commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Adds ACL tracking via inode_set_acl hook. Due to lack of path info in this hook, it is only possible to monitor inode-tracked files (i.e. those on the host file system) this is similar to how xattrs are tracked.

An extension to the BPF checks.c file has been added, because this hook does not exist on older kernels (where ACLs were applied via xattrs, so we still have coverage on those old kernels, just through a different mechanism.)

Tests factor in this optional nature of the hook, and will only run when we know the kernel supports it.

Relies on stackrox/stackrox#21357

Checklist

  • Patch has a change log entry OR does not need one.
  • Investigated and inspected CI test results
  • Updated documentation accordingly

Automated testing

  • Added unit tests
  • Added integration tests
  • Added regression tests

If any of these don't apply, please comment below.

Testing Performed

Tested locally, with setfacl, and the integration tests.

Summary by CodeRabbit

  • New Features
    • Added end-to-end support for recording POSIX ACL set activity (access vs default) and decoded ACL entries.
    • Extended kernel metrics collection to count ACL set events.
  • Bug Fixes
    • Improved capability detection and made startup resilient when the optional ACL hook isn’t available.
  • Tests
    • Added dedicated ACL event decoding tests and expanded ignored/monitored coverage.
  • Chores
    • Updated event-wait filtering to support including/excluding ACL events and refreshed the changelog/third-party pin.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds inode_set_acl ACL tracking across eBPF capture, userspace decoding, metrics, kernel capability checks, and Python test coverage. The PR also updates the changelog and the vendored StackRox submodule pin.

Changes

ACL Event Tracking

Layer / File(s) Summary
Kernel ACL data shapes and metrics fields
fact-ebpf/src/bpf/types.h
Adds ACL constants, acl_entry_t, the ACL event payload union variant, FILE_ACTIVITY_ACL_SET, and the inode_set_acl metrics field.
eBPF LSM hook and event submission
fact-ebpf/src/bpf/checks.c, fact-ebpf/src/bpf/main.c, fact-ebpf/src/bpf/events.h, fact-ebpf/src/lib.rs
Adds the inode_set_acl BPF LSM stub, the trace probe with monitoring/metrics logic, ACL event submission, and macro updates for metric accumulation.
Kernel capability detection for inode_set_acl
fact/src/bpf/checks.rs, fact/src/bpf/mod.rs
Adds the supports_inode_set_acl check and conditionally skips loading/attaching the hook when unsupported.
Userspace ACL decoding and protobuf conversion
fact/src/event/mod.rs
Adds FileData::AclSet, ACL model types, event accessors, kernel decoding, equality, and protobuf conversion for ACL file activity.
Userspace metrics registration
fact/src/metrics/kernel_metrics.rs
Registers the new inode_set_acl counter in generated kernel metrics.
ACL test support and event filtering
tests/event.py, tests/server.py, tests/utils.py
Adds ACL-aware event modeling, configurable skip categories, and a BTF symbol lookup helper.
ACL integration tests and xattr updates
tests/test_acl.py, tests/test_xattr.py
Adds ACL integration tests and updates xattr tests to use skip=().
Changelog and submodule bump
CHANGELOG.md, third_party/stackrox
Adds the changelog note and updates the pinned third-party revision.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Kernel
  participant check_inode_set_acl
  participant trace_inode_set_acl
  participant submit_acl_event
  participant Ringbuffer
  participant Userspace

  Kernel->>check_inode_set_acl: inode_set_acl LSM hook
  check_inode_set_acl-->>Kernel: return 0
  Kernel->>trace_inode_set_acl: inode_set_acl tracepoint
  trace_inode_set_acl->>trace_inode_set_acl: inode_is_monitored check
  trace_inode_set_acl->>submit_acl_event: submit_acl_event(args, acl_name, kacl)
  submit_acl_event->>Ringbuffer: reserve_event + submit FILE_ACTIVITY_ACL_SET
  Ringbuffer->>Userspace: FileData::new decodes ACL payload
  Userspace->>Userspace: FileData::AclSet -> FileAclChange protobuf
Loading

Possibly related PRs

  • stackrox/fact#806: Builds on the same macro-driven metrics plumbing extended here for inode_set_acl.

Suggested reviewers: Molter73

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: tracking POSIX ACL changes via the inode_set_acl LSM hook.
Description check ✅ Passed The description is detailed, matches the template sections, includes checklist items, and provides testing performed.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch giles/ROX-30296-track-acl

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 116 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.65%. Comparing base (0116956) to head (9b0fc8f).
⚠️ Report is 18 commits behind head on main.

Files with missing lines Patch % Lines
fact/src/event/mod.rs 0.00% 87 Missing ⚠️
fact/src/bpf/checks.rs 0.00% 17 Missing ⚠️
fact/src/bpf/mod.rs 0.00% 12 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #878      +/-   ##
==========================================
+ Coverage   32.23%   32.65%   +0.42%     
==========================================
  Files          21       21              
  Lines        2736     2915     +179     
  Branches     2736     2915     +179     
==========================================
+ Hits          882      952      +70     
- Misses       1851     1960     +109     
  Partials        3        3              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Stringy Stringy force-pushed the giles/ROX-30296-track-acl branch 4 times, most recently from 46d84a9 to 11683ba Compare June 29, 2026 09:45
@Stringy Stringy force-pushed the giles/ROX-30296-track-acl branch from 67a3b12 to be7fb22 Compare June 30, 2026 12:28
@Stringy Stringy force-pushed the giles/ROX-30296-track-acl branch from eae45f0 to fc73ed7 Compare June 30, 2026 14:02
@Stringy Stringy marked this pull request as ready for review July 1, 2026 10:40
@Stringy Stringy requested a review from a team as a code owner July 1, 2026 10:40
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
fact/src/event/mod.rs (1)

538-555: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the missing Chown equality arm.

While extending this manual matcher, FileData::Chown still falls through to _ => false, so two identical ownership-change events compare unequal.

Proposed fix
             (FileData::Unlink(this), FileData::Unlink(other)) => this == other,
             (FileData::Chmod(this), FileData::Chmod(other)) => this == other,
+            (FileData::Chown(this), FileData::Chown(other)) => this == other,
             (FileData::Rename(this), FileData::Rename(other)) => this == other,
             (FileData::SetXattr(this), FileData::SetXattr(other)) => this == other,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/event/mod.rs` around lines 538 - 555, The manual PartialEq
implementation for FileData is missing the FileData::Chown arm, so identical
ownership-change events currently fall through to false. Update the eq method in
FileData’s PartialEq match to add a Chown(this), Chown(other) branch that
compares the contained values the same way as the other variants, keeping the
fallback _ => false only for truly different variants.
🧹 Nitpick comments (1)
fact/src/bpf/checks.rs (1)

34-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider logging the underlying error before treating any load failure as "unsupported."

probe_hook returns false for missing programs, wrong program type, and load failures alike, discarding the error from prog.load(...). This is fine for genuinely unsupported kernels, but it also silently masks a real bug (e.g. a verifier failure in checks.c) as "capability not supported," making such regressions hard to diagnose.

♻️ Suggested diagnostic logging
     fn probe_hook(obj: &mut aya::Ebpf, prog_name: &str, hook: &str, btf: &Btf) -> bool {
         let Some(prog) = obj.program_mut(prog_name) else {
             return false;
         };
         let Ok(prog): Result<&mut Lsm, _> = prog.try_into() else {
             return false;
         };
-        prog.load(hook, btf).is_ok()
+        match prog.load(hook, btf) {
+            Ok(()) => true,
+            Err(e) => {
+                debug!("Failed to probe {hook} support via {prog_name}: {e:#?}");
+                false
+            }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact/src/bpf/checks.rs` around lines 34 - 42, `probe_hook` is swallowing real
load errors by returning false for every failure case. Update the function to
log the underlying error from `prog.load(hook, btf)` before mapping it to
unsupported, while keeping the existing false behavior for missing programs and
wrong program types. Use the `probe_hook` function and the `Lsm` load path to
locate the change, and make sure the log clearly distinguishes load failures
from genuinely unsupported kernels.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fact-ebpf/src/bpf/events.h`:
- Around line 193-199: The ACL copy loop in events.h is always assigning
entry.e_uid.val to args->event->acl.entries[i].e_id, but posix_acl_entry.e_uid
is only valid for ACL_USER and ACL_GROUP. Update the logic in the ACL entry
population path to branch on entry.e_tag and set e_id from e_uid.val only for
those tags; for ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_MASK, and ACL_OTHER, assign
ACL_UNDEFINED_ID instead. Keep the change localized to the ACL event-building
loop so the rest of the field copying in args->event->acl.entries remains
unchanged.

---

Outside diff comments:
In `@fact/src/event/mod.rs`:
- Around line 538-555: The manual PartialEq implementation for FileData is
missing the FileData::Chown arm, so identical ownership-change events currently
fall through to false. Update the eq method in FileData’s PartialEq match to add
a Chown(this), Chown(other) branch that compares the contained values the same
way as the other variants, keeping the fallback _ => false only for truly
different variants.

---

Nitpick comments:
In `@fact/src/bpf/checks.rs`:
- Around line 34-42: `probe_hook` is swallowing real load errors by returning
false for every failure case. Update the function to log the underlying error
from `prog.load(hook, btf)` before mapping it to unsupported, while keeping the
existing false behavior for missing programs and wrong program types. Use the
`probe_hook` function and the `Lsm` load path to locate the change, and make
sure the log clearly distinguishes load failures from genuinely unsupported
kernels.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 5b916de2-f11e-48d7-b345-c3ed9b660223

📥 Commits

Reviewing files that changed from the base of the PR and between 3a4a860 and 4be488a.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • fact-ebpf/src/bpf/checks.c
  • fact-ebpf/src/bpf/events.h
  • fact-ebpf/src/bpf/main.c
  • fact-ebpf/src/bpf/types.h
  • fact-ebpf/src/lib.rs
  • fact/src/bpf/checks.rs
  • fact/src/bpf/mod.rs
  • fact/src/event/mod.rs
  • fact/src/metrics/kernel_metrics.rs
  • tests/event.py
  • tests/server.py
  • tests/test_acl.py
  • tests/test_xattr.py
  • tests/utils.py
  • third_party/stackrox

Comment thread fact-ebpf/src/bpf/events.h

@Molter73 Molter73 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall looks good! Thanks for tackling this one!

There's a couple comments, mostly questions so I expect we should be able to merge this soon.

Comment thread fact-ebpf/src/bpf/events.h
Comment thread fact-ebpf/src/bpf/events.h
Comment thread fact-ebpf/src/bpf/events.h Outdated
Comment thread fact-ebpf/src/bpf/types.h
Comment thread fact/src/event/mod.rs Outdated
Comment thread fact/src/event/mod.rs Outdated
Comment thread fact/src/event/mod.rs Outdated
Comment thread tests/test_acl.py Outdated
Comment thread tests/test_acl.py Outdated
Comment thread tests/test_acl.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
fact-ebpf/src/bpf/types.h (1)

82-103: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider fixed-width types for wire-format enum fields.

acl_type_t and acl_tag_t are plain C enums with implementation-defined underlying size, but they're used directly as fields in acl_entry_t/the acl union member (Line 100, Line 149) that gets copied byte-for-byte into a ring-buffer event later decoded by a separate Rust binary. If the enum's compiled width ever diverges from what the Rust decoder assumes, the ACL payload would be silently misparsed. Using an explicit fixed-width type (e.g., unsigned char) for e_tag/acl_type at the struct-field level (while keeping the enum for symbolic constants elsewhere) would make the cross-language layout contract explicit and robust.

♻️ Proposed fix
 struct acl_entry_t {
-  acl_tag_t e_tag;
+  unsigned char e_tag; // acl_tag_t value
   unsigned short e_perm;
   unsigned int e_id;
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/types.h` around lines 82 - 103, `acl_type_t` and
`acl_tag_t` are being used as wire-format field types in `acl_entry_t` and the
`acl` union, but plain enums have implementation-defined size. Update the struct
fields that are serialized across the ring buffer to use an explicit fixed-width
type such as `unsigned char`, while keeping `acl_type_t` and `acl_tag_t` only as
symbolic enums for constants. Make sure the `acl_entry_t` definition and the
`acl` union member stay layout-compatible with the Rust decoder.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@fact-ebpf/src/bpf/types.h`:
- Around line 82-103: `acl_type_t` and `acl_tag_t` are being used as wire-format
field types in `acl_entry_t` and the `acl` union, but plain enums have
implementation-defined size. Update the struct fields that are serialized across
the ring buffer to use an explicit fixed-width type such as `unsigned char`,
while keeping `acl_type_t` and `acl_tag_t` only as symbolic enums for constants.
Make sure the `acl_entry_t` definition and the `acl` union member stay
layout-compatible with the Rust decoder.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: ba942558-b1cd-4991-8566-24551f09d46b

📥 Commits

Reviewing files that changed from the base of the PR and between 4be488a and 77c7d4a.

📒 Files selected for processing (6)
  • fact-ebpf/src/bpf/events.h
  • fact-ebpf/src/bpf/types.h
  • fact/src/bpf/checks.rs
  • fact/src/bpf/mod.rs
  • fact/src/event/mod.rs
  • tests/test_acl.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • fact/src/bpf/checks.rs
  • fact-ebpf/src/bpf/events.h
  • tests/test_acl.py
  • fact/src/bpf/mod.rs
  • fact/src/event/mod.rs

@Stringy Stringy requested a review from Molter73 July 6, 2026 09:20

@Molter73 Molter73 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just a couple minor nits, but overall LGTM!

Comment thread fact/src/bpf/checks.rs Outdated
Comment thread fact/src/bpf/mod.rs Outdated
@Stringy Stringy merged commit 17d94b4 into main Jul 7, 2026
52 of 60 checks passed
@Stringy Stringy deleted the giles/ROX-30296-track-acl branch July 7, 2026 13:23
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.

3 participants