Skip to content

Landlock update v1.0.0 - #16005

Open
regit wants to merge 27 commits into
OISF:mainfrom
regit:landlock-update-v1.0.0
Open

Landlock update v1.0.0#16005
regit wants to merge 27 commits into
OISF:mainfrom
regit:landlock-update-v1.0.0

Conversation

@regit

@regit regit commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

This PR updates Landlock support to latest Landlock capabilities and introduces mechanism to have plugins and output module to declare themselves to Landlock. A new Github workflow has been added to run S-V with Landlock enable. This has been instrumental in finding issues in the Landlock sandboxing.

Contribution style:

Our Contribution agreements:

Changes (if applicable):

Link to ticket: https://redmine.openinfosecfoundation.org/issues/8606

Describe changes:

  • Add new Landlock capabilities
  • Plugins and output modules can now sandbox themselves
  • Improvement in option handling
  • Stricter sandboxing overall

SV_BRANCH=OISF/suricata-verify#3269

regit added 27 commits August 11, 2026 09:40
In the case of --set=foo.0=bar, the 0 was not creating a sequence
if ever the sequence is not existing.

Ticket: 8606
A YAML sequence of single key maps, such as `outputs`, stores its
entries at outputs.<n>.<name> so a direct lookup of outputs.<name>
never matches. Add SCConfNodeLookupInSequence() to walk such a
sequence and return the entries matching a given name.

Ticket: 8606
The landlock read and write path helpers were static and only usable
from the sandboxing function itself. Rename them to the SC prefixed
public API and declare them in the header so that other parts of the
engine, and later plugins and output modules, can declare the paths
they need before the sandbox is enforced.

The ruleset is passed as an opaque void pointer to avoid exposing the
landlock internal structure to callers, and the helpers are no-op when
landlock is not available so they can be called unconditionally.

Ticket: 8606
Landlock gained network access control in ABI version 4 (Linux 6.7),
allowing to restrict TCP bind and connect to a set of ports.

Handle the network access rights in the ruleset and add two helpers,
SCLandlockGrantNetBindTCP() and SCLandlockGrantNetConnectTCP(), so that
the components needing to bind or connect a socket can declare the port
they use before the sandbox is enforced.

The ABI check is turned into a switch as the number of features
depending on the kernel version is growing: the network access rights
are removed from the ruleset when running on a kernel with an ABI lower
than 4.

Ticket: 8606
Recent kernels added filesystem access rights that were not handled by
the ruleset: LANDLOCK_ACCESS_FS_TRUNCATE (ABI 3), IOCTL_DEV (ABI 5) and
RESOLVE_UNIX (ABI 9). As they are not part of the handled access, they
were simply allowed, weakening the sandbox.

Add them to the write access set and extend the ABI switch to remove
the ones that are not known by the running kernel. Asking for an access
right the kernel does not know makes the ruleset creation fail, so a
missing case here would disable the sandbox instead of loosening it.

Ticket: 8606
A plugin opening a file or a socket is denied by the sandbox as it has
no way to declare the accesses it needs. Add an optional LandlockEnable
callback to the SCPlugin structure: it is called on every loaded plugin
just before the ruleset is enforced and the plugin can use the
SCLandlockGrant* helpers to declare what it needs to work properly.

The plugin API version is bumped as the SCPlugin structure is modified.

Ticket: 8606
Output modules write to files or sockets that are not necessarily under
the default log directory, and the sandbox has no way to know about
them. Add an optional LandlockEnable callback to OutputModule which is
called on every registered output module just before the ruleset is
enforced, so a module can grant the accesses it needs with the
SCLandlockGrant* helpers.

Ticket: 8606
Iterating over the outputs sequence to find every enabled instance of a
given output is needed by each module implementing a LandlockEnable
callback. Provide it once in util-landlock.c so no module has to walk
the sequence itself and get the outputs.<n>.<name> shape wrong.

Ticket: 8606
As the plugin may not implement correctly landlock, user has a way
to override the configuration.

Ticket: 8606
Some paths in dev, sys, proc were needed to get Suricata function
properly. Fallback was working but we were loosing some features
there.

These are the paths glibc, jemalloc and the Rust standard library read
during startup and runtime. Granting them read access avoids spurious
EACCES errors and the Landlock audit noise coming with them without
widening the sandbox in a meaningful way as any process on the system
can already read them.

Ticket: 8606
This patch triggers Landlock sandboxing earlier. This is useful
as the sandboxing is now applied before output is initiated which
triggers network and file access. Thus restriction apply from the
start and not at file reopening. This is safer and should allow
better debugging in case of problem.

Ticket: 8606
In the case the implementation of plugins is not trusted, the
new `plugin-setup` option can be used to disallow plugin to set
up their sandboxing.

Ticket: 8606
This patch adds the function needed to add to Landlock ruleset
what is needed for the module to function properly.

Ticket: 8606
This patch adds configuration for Landlock in eve including
for new network capabilities linked to redis.

Ticket: 8606
In profiling modules, the setting `append: no` is using truncate()
call which are prevented by landlock.

This patch adds a new utility SCLandlockGrantFile which allows a
module to ask for truncate on a specific file. By using a file as
parameter, we will avoid any unwanted truncation (for example of
eve.json).

It also add a new helper to register file permission at init before
landlock sandbox is created.

Ticket: 8606
The classification and reference files need read access.

The access is granted per file as classification.config,
reference.config and threshold.config can be set to a path outside of
any of the granted directories. When the configuration key is not set,
Suricata falls back to the compiled-in CONFIG_DIR default which is
granted too if the file exists.

Ticket: 8606
The file store writes its files under a temporary directory and renames
them into the final tree, which requires LANDLOCK_ACCESS_FS_REFER on the
file store directory. As the rules are attached to an existing inode,
the directory is also created if it does not exist yet.

Ticket: 8606
It is using write that does an implicit truncate so we need to add
truncate capability in the dataset directory.

Datasets declared in the configuration have a known state file so the
truncate permission is set on this file only. Rules can declare their
own dataset and their files are not known when the sandbox is set up,
so the permission is set on the data directory. This is only done if
datasets.rules.allow-write is not disabled as it is the setting
deciding whether such rules are loaded at all.

Ticket: 8606
This write permission was too lax as it was including the right
to remove a file. As we don't want Suricata to be able to remove
files that it has written (like eve.json) to cover traces we should
not allow this.

As a result, a new function providing write and remove has been
added. It is, for example, necessary to use this for log-pcap
or PID file.

Ticket: 8606
The access is asked for on the file itself and not on the parent
directory as `filename: /dev/null` is a common way to enable the module
while discarding its output and granting write access on the whole /dev
would widen the sandbox for no reason.

Ticket: 8606
The firewall rule file (--firewall-rules-exclusive) is loaded from the
path as provided so an absolute one can be outside of every directory
granted by the sandbox. It is thus granted per file. A relative path is
resolved against firewall.rule-path which is already granted as a
directory.

Ticket: 8606
This is needed to be able to get for example dataset saving state
in custom directories.

Truncation is not part of the default write access as zeroing a file is
an anti-forensics primitive, so it has to be asked for explicitly on a
directory owned by the caller.

Ticket: 8606
Run S-V with landlock enable.

Ticket: 8606
struct landlock_ruleset_attr gained the scoped field in Linux 6.12
(Landlock ABI 6) and handled_access_net in Linux 6.7 (ABI 4). Missing
constants can be defined by hand, a struct member cannot, so both need
their own configure check and the code using them has to be guarded.

Ticket: 8606
A directory listed in the configuration that does not exist on the
system is not an error, there is simply nothing to grant. This is for
example the case of the sysconfdir when Suricata is run from a build
tree. Anything else than ENOENT is still reported as a warning.

Ticket: 8606
Also just allow sockets usage in socket dirs.

File removal, truncation and socket creation are not part of the default
write access anymore. Removal and truncation are classic anti-forensics
primitives, unlinking or zeroing logs and state files to erase traces,
and socket creation is only needed by the unix command socket as
connecting to an existing socket does not require it. The subsystems
legitimately needing one of them now ask for it on the directory or the
file they own.

Directory creation stays in as creating a subdirectory on the fly is
common enough (tls-store certs directory, a log filename holding a path,
the Hyperscan cache) that removing it would only push the same grant
into most of the callers.

Ticket: 8606
@regit
regit requested a review from a team as a code owner August 11, 2026 08:23
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 4.79616% with 397 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.88%. Comparing base (03bc717) to head (b50a3b2).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #16005      +/-   ##
==========================================
- Coverage   83.01%   82.88%   -0.13%     
==========================================
  Files        1001     1002       +1     
  Lines      276611   277000     +389     
==========================================
- Hits       229616   229600      -16     
- Misses      46995    47400     +405     
Flag Coverage Δ
fuzzcorpus 61.52% <4.34%> (-0.13%) ⬇️
livemode 18.41% <4.58%> (-0.07%) ⬇️
netns 22.87% <4.58%> (-0.07%) ⬇️
pcap 45.29% <4.10%> (-0.15%) ⬇️
suricata-verify 66.96% <4.57%> (-0.16%) ⬇️
unittests 58.39% <4.83%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@regit

regit commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Regarding coverage, I think we can improve it if the S-V run with landlock added in this PR is in coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant