landlock: Add documentation for capability and namespace restrictions

Document the two new Landlock permission categories, introduced with ABI
12, in the userspace API guide, admin guide, trace-event guide, UAPI
comments, and kernel security documentation.

The kernel security documentation generalizes the existing per-object
access-rights framing into three restriction models (handled_access_*,
handled_permissions, scoped) and states criteria for choosing among
them, so future permission or scope additions have a documented place to
land.

Cc: Christian Brauner <brauner@kernel.org>
Cc: Günther Noack <gnoack@google.com>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Link: https://patch.msgid.link/20261014082718.1435842-9-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Mickaël Salaün 2026-10-14 10:27:14 +02:00
parent b3dafbe7ed
commit 5d2c5cd87a
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2
6 changed files with 502 additions and 58 deletions

View File

@ -7,7 +7,7 @@ Landlock: system-wide management
================================
:Author: Mickaël Salaün
:Date: August 2026
:Date: October 2026
Landlock can leverage the audit framework to log events.
@ -20,10 +20,12 @@ Audit
Denied access requests are logged by default for a sandboxed program if `audit`
is enabled. This default behavior can be changed with the
sys_landlock_restrict_self() flags (cf.
Documentation/userspace-api/landlock.rst), or suppressed on a per-object
basis by using ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+). Landlock logs can
also be masked thanks to audit rules. Landlock can generate 2 audit
record types.
Documentation/userspace-api/landlock.rst), suppressed on a per-object
basis by using ``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+), or suppressed for
specific capability or namespace members with the corresponding permission
rule's ``quiet_*`` field (ABI 12+). Quiet rules only suppress audit submission;
they do not suppress denial trace events. Landlock logs can also be masked
thanks to audit rules. Landlock can generate two audit record types.
Record types
------------
@ -65,14 +67,28 @@ AUDIT_LANDLOCK_ACCESS
- scope.abstract_unix_socket - Abstract UNIX socket connection denied
- scope.signal - Signal sending denied
**namespace.*** - Namespace restrictions (ABI 12+):
- namespace.use - Namespace use was denied (creation via
:manpage:`unshare(2)`, :manpage:`clone(2)`, :manpage:`clone3(2)`,
:manpage:`open_tree(2)`, or :manpage:`fsmount(2)`, or joining via
:manpage:`setns(2)`);
``namespace_type`` indicates the type (hex ``CLONE_NEW*`` bitmask),
and ``namespace_id`` identifies the target namespace for
:manpage:`setns(2)` operations (zero for creation)
**capability.*** - Capability restrictions (ABI 12+):
- capability.use - Capability use was denied;
``capability`` indicates the capability number
Multiple blockers can appear in a single event (comma-separated) when
multiple access rights are missing. For example, creating a regular file
in a directory that lacks both ``make_reg`` and ``refer`` rights would show
``blockers=fs.make_reg,fs.refer``.
The object identification fields (path, dev, ino for filesystem; opid,
ocomm for signals) depend on the type of access being blocked and provide
context about what resource was involved in the denial.
The object identification fields depend on the type of access being blocked:
``path``, ``dev``, ``ino`` for filesystem; ``opid``, ``ocomm`` for signals;
``namespace_type`` and ``namespace_id`` for namespace operations;
``capability`` for capability use.
AUDIT_LANDLOCK_DOMAIN
@ -178,8 +194,9 @@ If you get spammed with audit logs related to Landlock, this is either an
attack attempt or a bug in the security policy. We can put in place some
filters to limit noise with two complementary ways:
- with sys_landlock_restrict_self()'s flags, or
``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+) if we can fix the sandboxed
- with sys_landlock_restrict_self()'s flags,
``LANDLOCK_ADD_RULE_QUIET`` (ABI 10+), or the permission rules'
per-member ``quiet_*`` fields (ABI 12+) if we can fix the sandboxed
programs,
- or with audit rules (see :manpage:`auditctl(8)`).
@ -243,8 +260,10 @@ with these exceptions:
- **NOAUDIT hooks**: Some LSM hooks suppress logging for speculative
permission probes (e.g., reading ``/proc/<pid>/status`` uses
``PTRACE_MODE_NOAUDIT``). When NOAUDIT is set, neither audit records
nor trace events are emitted, and the denial is not counted in
``PTRACE_MODE_NOAUDIT``, and :manpage:`listxattr(2)` probes
``CAP_SYS_ADMIN`` with ``CAP_OPT_NOAUDIT`` to decide whether to list
``trusted.*`` extended attributes). When NOAUDIT is set, neither audit
records nor trace events are emitted, and the denial is not counted in
``denials``. The denial is still enforced. This avoids performance
overhead and noise from speculative probes that test permissions
without performing an actual access.
@ -269,7 +288,8 @@ Observability security considerations
Both audit records and trace events expose information about all
Landlock-sandboxed processes on the system, including filesystem paths
being accessed, network ports, and process identities. System
being accessed, network ports, capability numbers, namespace types and IDs,
and process identities. System
administrators must ensure that access to audit logs (controlled by the
audit subsystem configuration) and to trace events (requiring
``CAP_SYS_ADMIN`` or ``CAP_BPF`` + ``CAP_PERFMON``) is restricted to

View File

@ -8,7 +8,7 @@ Landlock LSM: kernel documentation
==================================
:Author: Mickaël Salaün
:Date: August 2026
:Date: October 2026
Landlock's goal is to create scoped access-control (i.e. sandboxing). To
harden a whole system, this feature should be available to any process,
@ -130,6 +130,166 @@ The reasoning is:
restrictions, because access within the same scope is already
allowed based on ``LANDLOCK_ACCESS_FS_RESOLVE_UNIX``.
Composability with user namespaces
----------------------------------
Landlock domain-based scoping and the kernel's user-namespace-based capability
scoping enforce isolation over independent hierarchies. Landlock checks domain
ancestry; the kernel's ``ns_capable()`` checks user namespace ancestry. These
hierarchies are orthogonal: Landlock enforcement is deterministic with respect
to its own configuration, regardless of namespace or capability state, and vice
versa. This orthogonality is a design invariant that must hold for all Landlock
access controls.
Design philosophy
-----------------
Landlock's goal is to restrict a sandboxed process's access to three kinds of
resources: data (files, sockets, pipes), other processes (signals, ptrace), and
kernel-internal resources whose use widens the kernel attack surface
(capabilities, namespace types). Each access right or permission gates one or
more operations that grant such access; restricting the operations is how
Landlock restricts the underlying access.
When designing a new access control, identify the protected resource kind
first (data, processes, or kernel-internal resources). The operations to
restrict follow from the protected resource, by identifying which kernel code
paths grant access to the resource and at which place in the code the access to
the resource can be gated. Do not design a permission around
"restrict the unshare(2) syscall" or similar mechanism-centric framings; design
it around "restrict the process from acquiring access to namespace types" (the
protected resource), letting the operation set follow.
Ruleset restriction models
--------------------------
Landlock provides three restriction models that differ in how rules identify the
resource being restricted.
In general, the ``struct landlock_ruleset_attr`` specifies the operations to be
denied by default under the enforced policy. The *rules* added to the ruleset
define the exceptions to these restrictions, allow-listing specific conditions
under which these operations are still permitted.
Per-object access rights (``handled_access_*``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Per-object access rights control operations on a specific resource instance,
identified in the rule key by a value drawn from an open-ended space: a file
hierarchy referenced by ``parent_fd``, or a network port identified by its
16-bit number.
Each ``handled_access_*`` field declares a set of access rights, operations
which are to be denied by default once the ruleset is enforced.
The rule body declares which of the multiple distinct operations on that object
instance are allowed (open, read, write, truncate; bind, connect).
Operations are grouped by object type in the respective ``handled_access_*``
field: a new operation on an existing type extends that field, and a new object
type gets its own ``handled_access_*`` field.
Per-category permissions (``handled_permissions``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Per-category permissions control the process's exercise of category members,
where the category is a small kernel-defined enumeration (a Linux capability
number ``CAP_*``, a namespace type ``CLONE_NEW*``). Unlike per-object access
rights, which restrict specific operations on a single resource instance,
per-category permissions gate the prerequisite operation itself (exercising a
capability, acquiring access to a namespace), so gating it transitively covers a
broad set of downstream operations (e.g. denying ``CAP_SYS_ADMIN`` blocks all
admin operations such as mounts).
These category members are the LSM-level access-control objects (the entities
the process is authorized against) even though they are enum values rather than
externally-instantiated kernel data structures. Per-category permissions apply
where the controlled operation collapses to "may the process use this category
member at all" (use a capability; acquire access to a namespace), so the rule
body lists which category members the process may exercise.
Each ``LANDLOCK_PERMISSION_*`` flag maps to its own rule type and covers every
kernel path that exercises a member. When a ruleset handles a permission, all
uses of category members are denied unless explicitly allowed by a rule.
Logging of a denied member can be suppressed at the same granularity as the
restriction itself: per rule and per member. A rule names the members whose
denial should not be audited; suppression only affects the audit record, never
the denial or its trace event, which reports ``logged=0``. Suppression only
applies when the layer owning the rule is the one that denied the member.
See Documentation/userspace-api/landlock.rst for the concrete syscall paths
covered by each permission.
The category enum is owned by the corresponding kernel subsystem (capabilities,
namespaces, etc.). Userspace policy authors query category member availability
via the relevant non-Landlock interfaces:
* For capabilities: ``<linux/capability.h>``,
``/proc/sys/kernel/cap_last_cap``, ``prctl(PR_CAPBSET_READ)``.
* For namespaces: ``<linux/sched.h>``, ``/proc/$$/ns/*``,
:manpage:`unshare(2)` runtime probe.
The Landlock ABI version does not encode this availability; ABI versioning
describes which Landlock features (rule types, access rights, scopes,
permissions) the kernel implements, not which category members the kernel knows
about.
Forward compatibility for new category members follows a simple rule set:
* New members in future kernels are automatically denied: rules whitelist
specific values, and a member not in any rule is denied.
* Kernel-side compatibility for split categories is handled by the owning
subsystem (e.g., when ``CAP_BPF`` was split from ``CAP_SYS_ADMIN``, either
capability became sufficient for the affected operations, so a rule allowing
``CAP_SYS_ADMIN`` continues to allow operations now gated by
``CAP_SYS_ADMIN || CAP_BPF``).
* Unknown values in the rule body are silently accepted rather than rejected.
Rejecting them would tie Landlock policy semantics to the running kernel's
category-member set: a rule built against future headers would fail to load
on older kernels, forcing policy authors to know each kernel's enumeration.
Acceptance is fail-safe in both directions: a rule referring to a value the
running kernel does not yet know has no effect (deny-by-default still applies
to that operation), and a rule written against future headers loads
identically across kernels so the same policy keeps the same restrictions.
When a value becomes real on a future kernel, the policy activates as written
by the author.
* In contrast, unknown ``LANDLOCK_PERMISSION_*`` flags in
``handled_permissions`` are rejected (``-EINVAL``), since Landlock owns that
bit space.
Cross-domain scopes (``scoped``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Scopes restrict **cross-domain interactions** categorically, without rules.
Setting a scope flag (e.g. ``LANDLOCK_SCOPE_SIGNAL``) denies the operation to
targets outside the Landlock domain or its children. Like per-category
permissions, scopes provide complete coverage of the controlled operation.
Choosing a model for a new feature
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* If the new feature controls operations on resource objects supplied by the
sandbox author, extend or add a per-object access right
(``handled_access_*``).
* If the new feature controls a per-category operation gated by an enum (a
Linux capability, a namespace type, a socket family, etc.), use a
per-category permission (``handled_permissions``). When several such enums
could classify the operation, prefer the enum the originating subsystem
already uses for capability/access checks (e.g. ``CAP_*`` for ``capable()``
hooks, ``CLONE_NEW*`` for namespace hooks).
* When an operation is gated by multiple kernel-defined enums (a classic
example being ``CAP_SYS_ADMIN`` plus a ``CLONE_NEW*`` flag for non-user
namespace creation), define one per-category permission per enum dimension.
Sandbox authors handle each dimension's permission in ``handled_permissions``
and add rules for each; the kernel enforces each dimension at its own LSM
hook. ``LANDLOCK_PERMISSION_NAMESPACE_USE`` and
``LANDLOCK_PERMISSION_CAPABILITY_USE`` follow this pattern.
* If the new feature restricts a categorical cross-domain interaction with no
per-target granularity, use a cross-domain scope (``scoped``).
* For all three models, confirm a single LSM hook (or small set of related
hooks) covers every kernel path that exercises the operation.
Tests
=====
@ -151,6 +311,18 @@ Filesystem
.. kernel-doc:: security/landlock/fs.h
:identifiers:
Namespace
---------
.. kernel-doc:: security/landlock/ns.h
:identifiers:
Capability
----------
.. kernel-doc:: security/landlock/cap.h
:identifiers:
Process credential
------------------
@ -198,7 +370,8 @@ whether or not the kernel is built with audit support, so a
tracepoints-only build reports the selection audit would make. A quiet
rule (``LANDLOCK_ADD_RULE_QUIET`` with the access in the ``quiet_*``
fields of ``struct landlock_ruleset_attr``) suppresses logging by
setting ``logged=0`` the same way.
setting ``logged=0`` the same way, as do the per-member ``quiet_*``
masks of capability and namespace rules.
See Documentation/admin-guide/LSM/landlock.rst for audit record format,
tracepoint usage, and filtering examples.

View File

@ -6,7 +6,7 @@ Landlock Trace Events
=====================
:Author: Mickaël Salaün
:Date: September 2026
:Date: October 2026
Landlock emits trace events for sandbox lifecycle operations and access
denials. These events can be consumed by ftrace (for human-readable
@ -35,6 +35,8 @@ Landlock trace events are organized in four categories:
- ``landlock_add_rule_net_port``: a network port rule is added to a ruleset
- ``landlock_create_domain``: a new domain is created from a ruleset
- ``landlock_enforce_domain``: a domain is enforced on a thread
- ``landlock_add_rule_namespace``: a namespace rule is added to a ruleset
- ``landlock_add_rule_capability``: a capability rule is added to a ruleset
**Denial events** are emitted when an access is denied:
@ -44,6 +46,8 @@ Landlock trace events are organized in four categories:
- ``landlock_deny_scope_signal``: signal delivery denied
- ``landlock_deny_scope_abstract_unix_socket``: abstract unix socket
access denied
- ``landlock_deny_permission_namespace``: namespace use denied
- ``landlock_deny_permission_capability``: capability use denied
**Rule evaluation events** are emitted during rule matching:
@ -84,7 +88,7 @@ Landlock, not from regular file permissions::
$ LC_ALL=C LL_FS_RO=/usr:/lib:/lib64:/bin:/etc/ld.so.cache LL_FS_RW=/tmp \
./sandboxer cat /etc/passwd
$ cat /sys/kernel/tracing/trace_pipe
cat-127 [...] landlock_create_ruleset: ruleset=195cc6b76.0 handled_fs=execute|write_file|read_file|read_dir|remove_dir|remove_file|make_char|make_dir|make_reg|make_sock|make_fifo|make_block|make_sym|refer|truncate|ioctl_dev|resolve_unix handled_net= scoped=
cat-127 [...] landlock_create_ruleset: ruleset=195cc6b76.0 handled_fs=execute|write_file|read_file|read_dir|remove_dir|remove_file|make_char|make_dir|make_reg|make_sock|make_fifo|make_block|make_sym|refer|truncate|ioctl_dev|resolve_unix handled_net= scoped= handled_permissions=
cat-127 [...] landlock_create_domain: domain=195cc6b7c parent=0 ruleset=195cc6b76.6
cat-127 [...] landlock_enforce_domain: domain=195cc6b7c complete=1 process_wide=1 no_new_privs=1
cat-127 [...] landlock_deny_access_fs: domain=195cc6b7c same_exec=0 logged=0 blockers=read_file dev=0:17 ino=5901179 path=/etc/passwd
@ -119,10 +123,13 @@ in some field formats:
Audit uses string ``dev="<s_id>"``. Numeric format is more precise
for machine parsing.
- **Denied access field**: The ``deny_access_fs`` and ``deny_access_net``
tracepoints use the ``blockers=`` field name (same as audit). Both
render the blocked access rights as names: audit prefixes the category
and separates with commas (e.g., ``blockers=fs.read_file``), while the
- **Blocker field**: Denial tracepoints that carry a blocker use the
``blockers=`` field name (same as audit). The ``deny_access_fs`` and
``deny_access_net`` tracepoints render the blocked access rights, the
``deny_permission_namespace`` and ``deny_permission_capability``
tracepoints render the blocked permission, and a ``change_topology``
denial renders its request type. Audit prefixes the category and
separates with commas (e.g., ``blockers=fs.read_file``), while the
tracepoints omit the category (carried by the event name) and separate
with ``|`` (e.g., ``blockers=read_file``). Scope and ptrace
tracepoints omit ``blockers`` because the event name identifies the
@ -160,10 +167,34 @@ Ruleset versioning
==================
Syscall events include a ruleset version (``ruleset=<hex_id>.<version>``)
that tracks the number of rules added to the ruleset. The version is
incremented on each ``landlock_add_rule()`` call and frozen at
``landlock_restrict_self()`` time. This enables trace consumers to
correlate a domain with the exact set of rules it was created from.
that tracks the number of successful add-rule calls on the ruleset. The
version is incremented on each successful ``landlock_add_rule()`` call and
frozen at ``landlock_restrict_self()`` time. Namespace and capability calls
increment it even when their effective known masks are empty or already
present, so the event stream preserves every successful call. This enables
trace consumers to correlate a domain with the exact rule history from which
it was created.
Permission rule and denial events
=================================
``landlock_create_ruleset`` reports handled permissions as symbolic names in
``handled_permissions=``. ``landlock_add_rule_namespace`` and
``landlock_add_rule_capability`` report ``permissions=`` plus the effective
known allowed and quiet member masks. Capability masks and the raw
``CLONE_NEW*`` namespace masks are printed in hexadecimal. Unknown-only input
therefore appears as zero while still advancing the ruleset version, and adding
a member already present still emits an event.
``landlock_deny_permission_namespace`` reports the denied raw
``CLONE_NEW*`` value. ``namespace_id`` is zero for namespace creation and is
the exact target namespace ID for :manpage:`setns(2)`.
``landlock_deny_permission_capability`` reports the denied ``CAP_*`` number.
Like every denial event, both include the denying domain, ``same_exec``, and
``logged``.
Per-member quiet masks suppress audit submission but never these trace events,
which report ``logged=0``.
Domain enforcement
==================
@ -290,8 +321,8 @@ per-domain state in BPF maps:
``domain=`` key (join to the ``create_domain`` recorded in step 1),
building the per-domain thread set; filter ``complete==1`` for a
one-event-per-operation summary.
3. On ``landlock_deny_access_*``: look up the domain, decide whether
to count, alert, or ignore the denial based on custom policy.
3. On ``landlock_deny_*``: look up the domain, decide whether to count,
alert, or ignore the denial based on custom policy.
4. On ``landlock_free_domain``: clean up the per-domain state, log
final statistics.
@ -304,11 +335,12 @@ reconcile incomplete state.
Audit filtering equivalence
===========================
The ``logged`` field reflects the domain's log policy but not the global
``audit_enabled`` toggle, so it does not change when audit is turned on
or off. When audit is enabled, ``logged==1`` selects the denials the
domain submits to audit (audit-side rate-limiting and exclude rules may
still drop some), so a stateless ftrace filter can select them::
The ``logged`` field reflects the domain's log policy and per-request quiet
selection, but not the global ``audit_enabled`` toggle, so it does not
change when audit is turned on or off. When audit is enabled, ``logged==1``
selects the denials the domain submits to audit (audit-side rate-limiting and
exclude rules may still drop some), so a stateless ftrace filter can select
them::
# Show only denials that audit would also log:
echo 'logged==1' > \

View File

@ -8,7 +8,7 @@ Landlock: unprivileged access control
=====================================
:Author: Mickaël Salaün
:Date: August 2026
:Date: October 2026
The goal of Landlock is to enable restriction of ambient rights (e.g. global
filesystem or network access) for a set of processes. Because Landlock
@ -29,21 +29,30 @@ If Landlock is not currently supported, we need to
Landlock rules
==============
A Landlock rule describes an action on an object which the process intends to
perform. A set of rules is aggregated in a ruleset, which can then restrict
the thread enforcing it, and its future children.
A Landlock rule describes the actions a process is allowed to perform on a
specific resource. A set of rules is aggregated in a ruleset, which can then
restrict the thread enforcing it, and its future children.
The two existing types of rules are:
The existing types of rules are:
Filesystem rules
For these rules, the object is a file hierarchy,
and the related filesystem actions are defined with
`filesystem access rights`.
The rule key is a file hierarchy, and the actions it allows are
defined with `filesystem access rights`.
Network rules (since ABI v4 for TCP and v10 for UDP)
For these rules, the object is a TCP or UDP port,
and the related actions are defined with `network access rights`.
Capability rules (since ABI v12)
The rule body lists which Linux capabilities (see
:manpage:`capabilities(7)`) the process may exercise; the action is
defined with `permission flags`.
Namespace rules (since ABI v12)
The rule body lists which namespace types (see
:manpage:`namespaces(7)`) the process may use; the action is defined
with `permission flags`.
Defining and enforcing a security policy
----------------------------------------
@ -87,6 +96,9 @@ to be explicit about the denied-by-default access rights.
.scoped =
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
LANDLOCK_SCOPE_SIGNAL,
.handled_permissions =
LANDLOCK_PERMISSION_NAMESPACE_USE |
LANDLOCK_PERMISSION_CAPABILITY_USE,
};
Because we may not know which kernel version an application will be executed
@ -140,6 +152,13 @@ version, and only use the available subset of access rights:
ruleset_attr.handled_access_net &=
~(LANDLOCK_ACCESS_NET_BIND_UDP |
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
__attribute__((fallthrough));
case 10:
case 11:
/* Removes LANDLOCK_PERMISSION_* for ABI < 12 */
ruleset_attr.handled_permissions &=
~(LANDLOCK_PERMISSION_NAMESPACE_USE |
LANDLOCK_PERMISSION_CAPABILITY_USE);
}
This enables the creation of an inclusive ruleset that will contain our rules.
@ -242,6 +261,56 @@ the program explicitly called :manpage:`bind(2)` on port 0.
err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
&udp_bind, 0);
Capability and namespace rules use a different attribute layout:
``permissions`` identifies the permission category (a single
``LANDLOCK_PERMISSION_*`` flag) and a type-specific value field carries the
bitmask to allow within it. See `Capability and namespace restrictions`_ for
the model.
For capability access-control, we can add rules that allow specific
capabilities (see :manpage:`capabilities(7)` for the list of ``CAP_*``
values). For instance, to allow ``CAP_SYS_CHROOT`` (so the sandboxed
process can call :manpage:`chroot(2)` inside a user namespace):
.. code-block:: c
struct landlock_capability_attr cap_attr = {
.permissions = LANDLOCK_PERMISSION_CAPABILITY_USE,
.allowed_capabilities = (1ULL << CAP_SYS_CHROOT),
};
cap_attr.permissions &= ruleset_attr.handled_permissions;
if (cap_attr.permissions)
err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_CAPABILITY,
&cap_attr, 0);
For namespace access-control, we can add rules that allow using specific
namespace types (see :manpage:`namespaces(7)` for the list of ``CLONE_NEW*``
values): creating them via :manpage:`unshare(2)` / :manpage:`clone(2)` /
:manpage:`clone3(2)`, joining them via :manpage:`setns(2)`, or acquiring an fd
reference via :manpage:`open_tree(2)` / :manpage:`fsmount(2)`. For instance,
to allow creating user namespaces (which grants all capabilities inside the new
namespace):
.. code-block:: c
struct landlock_namespace_attr ns_attr = {
.permissions = LANDLOCK_PERMISSION_NAMESPACE_USE,
.allowed_namespace_types = CLONE_NEWUSER,
};
ns_attr.permissions &= ruleset_attr.handled_permissions;
if (ns_attr.permissions)
err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NAMESPACE,
&ns_attr, 0);
Together, these two rules allow an unprivileged process to create a user
namespace and call :manpage:`chroot(2)` inside it, while denying all other
capabilities and namespace types. User namespace creation is the one operation
that does not require ``CAP_SYS_ADMIN``, so no capability rule is needed for it.
See `Capability and namespace restrictions`_ for details on capability
requirements.
When passing a non-zero ``flags`` argument to ``landlock_restrict_self()``, a
similar backwards compatibility check is needed for the restrict flags
(see sys_landlock_restrict_self() documentation for available flags):
@ -442,9 +511,139 @@ The operations which can be scoped are:
A :manpage:`sendto(2)` on a socket which was previously connected will not
be restricted. This works for both datagram and stream sockets.
IPC scoping does not support exceptions via :manpage:`landlock_add_rule(2)`.
If an operation is scoped within a domain, no rules can be added to allow access
to resources or processes outside of the scope.
Scoping does not support exceptions via :manpage:`landlock_add_rule(2)`. If an
operation is scoped within a domain, no rules can be added to allow access to
resources or processes outside of the scope.
Capability and namespace restrictions
-------------------------------------
``handled_permissions`` declares per-category permissions: each permission
selects which members of a kernel-defined category (CAP_* capabilities,
CLONE_NEW* namespace types) the process may use. Unlike per-object access
rights (``handled_access_*``) or cross-domain scopes (``scoped``), per-category
permissions constrain the sandboxed process's own use of these enums; members
not allowed by a rule are denied by default.
``LANDLOCK_PERMISSION_NAMESPACE_USE`` gates *acquisition of access* to
namespaces: creation via :manpage:`unshare(2)` / :manpage:`clone(2)`
/ :manpage:`clone3(2)`, entry via :manpage:`setns(2)`, and fd-reference
acquisition via :manpage:`open_tree(2)` / :manpage:`fsmount(2)`. Namespaces
the process is already a member of when the domain is enforced are implicitly
allowed (the process could not continue running otherwise); rules describe
which new namespace types the process may acquire.
``LANDLOCK_PERMISSION_CAPABILITY_USE`` gates every exercise of a capability
after the domain is enforced, regardless
of how the capability was obtained (inherited credentials, ``CLONE_NEWUSER``
grant, ``setuid``/file-cap-bearing :manpage:`execve(2)`, etc.). Configuring
both together restricts what privileges are available *and* the namespaces in
which they take effect, which matters because user namespace creation has no
capability check and grants all capabilities within the new namespace: gating
only one of the two leaves a kernel attack-surface widening path open. When a
domain handles both permissions, an operation is allowed only if each
independently allows it: :manpage:`unshare(2)` with ``CLONE_NEWNET`` then
requires both a rule allowing ``CLONE_NEWNET`` and a rule allowing
``CAP_SYS_ADMIN``.
``LANDLOCK_PERMISSION_CAPABILITY_USE`` complements :manpage:`prctl(2)`
``PR_SET_NO_NEW_PRIVS`` but does not replace it. ``PR_SET_NO_NEW_PRIVS``
prevents privilege *acquisition* via :manpage:`execve(2)` (setuid, file
capability xattrs, privilege-elevating LSM transitions) and is a prerequisite
for unprivileged Landlock self-sandboxing.
``LANDLOCK_PERMISSION_CAPABILITY_USE`` restricts *exercise* of capabilities the
process already holds, including those
gained via ``CLONE_NEWUSER`` which ``PR_SET_NO_NEW_PRIVS`` does not block.
Sandboxes typically set both.
Rules are added with ``LANDLOCK_RULE_CAPABILITY`` and &struct
landlock_capability_attr (each rule lists ``CAP_*`` values to allow), and with
``LANDLOCK_RULE_NAMESPACE`` and &struct landlock_namespace_attr (each rule
lists ``CLONE_NEW*`` flags to allow). Landlock is purely restrictive: it can
only deny what the traditional check would have allowed, never grant additional
privileges.
Denials of specific capabilities or namespace types can be excluded from audit
logging per rule, by listing them in the ``quiet_capabilities`` or
``quiet_namespace_types`` field of the rule (independently of the allowed
members). Quieting requires the permission to be handled, is member-granular,
and only suppresses the audit record of a denial attributed to the rule's own
layer; it never changes the denial itself or suppresses its trace event, which
reports ``logged=0``. Sandboxes should quiet only specific members known to be
requested but expected to be denied (for example for compatibility with older
callers), never a blanket set: a blanket set follows the running kernel's known
members and would hide surprising or future denials from audit.
Rule bodies silently accept values unknown to the current kernel (capabilities
above ``CAP_LAST_CAP``, unrecognised ``CLONE_NEW*`` bits): they have no runtime
effect, so a rule compiled against future kernel headers loads without error on
older kernels. Future kernels gain new members denied by default until a rule
explicitly allows them.
The single ``LANDLOCK_PERMISSION_NAMESPACE_USE`` bit gates every kernel path
that grants the calling process access to a namespace of the controlled types,
whether by becoming a member of the namespace or by holding a file descriptor
that references it. The covered syscall paths are:
* :manpage:`unshare(2)` with ``CLONE_NEW*``: the caller becomes a member of a
newly-created namespace.
* :manpage:`clone(2)` (or :manpage:`clone3(2)`) with ``CLONE_NEW*``: the
child becomes a member of a newly-created namespace.
* :manpage:`setns(2)`: the caller becomes a member of an existing namespace
referenced by file descriptor.
* :manpage:`open_tree(2)` with ``OPEN_TREE_NAMESPACE``: the caller obtains a
file descriptor referring to a newly-created mount namespace.
* :manpage:`open_tree(2)` with ``OPEN_TREE_CLONE``: the caller obtains a file
descriptor referring to a newly-created anonymous mount namespace.
* :manpage:`fsmount(2)` with ``FSMOUNT_NAMESPACE``: the caller obtains a file
descriptor referring to a newly-created mount namespace.
* :manpage:`fsmount(2)` (default): the caller obtains a file descriptor
referring to a newly-created anonymous mount namespace.
Anonymous mount namespaces (created by ``open_tree(OPEN_TREE_CLONE)`` and the
default :manpage:`fsmount(2)`) are intentionally covered by the bit even though
the calling process does not become a member of them. Without this coverage, a
sandboxed process could combine ``open_tree(OPEN_TREE_CLONE)`` with
:manpage:`move_mount(2)` to graft mounts from a freshly-allocated mount
namespace into its current namespace, bypassing a ``CLONE_NEWNS`` restriction
(these mount operations require ``CAP_SYS_ADMIN``, which a sandboxed process
typically obtains by first creating a user namespace).
In practice, unprivileged processes first create a user namespace (which
requires no capability and grants all capabilities within it), then use those
capabilities to create other namespace types. All non-user namespace types
require ``CAP_SYS_ADMIN`` for both creation and :manpage:`setns(2)` entry; mount
namespace entry additionally requires ``CAP_SYS_CHROOT``. For
:manpage:`setns(2)`, capabilities are checked relative to the target namespace,
so a process in an ancestor user namespace naturally satisfies them; this
includes joining user namespaces, which requires ``CAP_SYS_ADMIN``. When
``LANDLOCK_PERMISSION_CAPABILITY_USE`` is also handled, each of these
capabilities must be explicitly allowed by a rule.
Creating a non-user namespace always requires ``CAP_SYS_ADMIN``, and
``LANDLOCK_PERMISSION_CAPABILITY_USE`` (when handled) gates that check
regardless of which user namespace it targets. This is true whether the
namespace is created
on its own, combined with ``CLONE_NEWUSER`` in a single :manpage:`unshare(2)`
call, or in a separate :manpage:`unshare(2)` call after a user namespace: in
all cases the kernel checks ``CAP_SYS_ADMIN`` against the owning user
namespace, and Landlock denies it unless a rule allows
``CAP_SYS_ADMIN``. Combining ``CLONE_NEWUSER`` with another ``CLONE_NEW*``
flag therefore does not avoid the capability check; only user namespace
creation itself needs no capability rule.
When creating child user namespaces, it is recommended to also create a
dedicated Landlock domain with restrictions relevant to each namespace context.
Note that ``LANDLOCK_PERMISSION_CAPABILITY_USE`` restricts the *use* of
capabilities, not their presence in the process's credential. Capability sets
can change
after a domain is enforced through user namespace entry or :manpage:`capset(2)`;
privileged sandboxes that did not set ``PR_SET_NO_NEW_PRIVS`` may also gain
capabilities through :manpage:`execve(2)` of binaries with file capabilities.
In all cases, :manpage:`capget(2)` will report the credential's capability sets,
but any denied capability will fail with ``EPERM`` when exercised. Do not rely
on :manpage:`capget(2)` to determine whether the policy permits a given
capability; only the actual operation will return ``EPERM`` upon denial.
Truncating files
----------------
@ -610,7 +809,7 @@ Access rights
-------------
.. kernel-doc:: include/uapi/linux/landlock.h
:identifiers: fs_access net_access scope
:identifiers: fs_access net_access scope permission
Creating a new ruleset
----------------------
@ -629,7 +828,8 @@ Extending a ruleset
.. kernel-doc:: include/uapi/linux/landlock.h
:identifiers: landlock_rule_type landlock_path_beneath_attr
landlock_net_port_attr
landlock_net_port_attr landlock_capability_attr
landlock_namespace_attr
Enforcing a ruleset
-------------------
@ -834,6 +1034,23 @@ with ``LANDLOCK_RESTRICT_SELF_TSYNC``, no_new_privs is set on all threads
of the process. As explained in the tutorial above, leaving no_new_privs
unset is risky even when Landlock does not require it.
Capability (ABI < 12)
---------------------
Starting with the Landlock ABI version 12, it is possible to restrict
:manpage:`capabilities(7)` with the new ``LANDLOCK_PERMISSION_CAPABILITY_USE``
permission flag and ``LANDLOCK_RULE_CAPABILITY`` rule type.
Namespace (ABI < 12)
--------------------
Starting with the Landlock ABI version 12, it is possible to restrict namespace
use (see :manpage:`namespaces(7)`) across creation (:manpage:`unshare(2)`,
:manpage:`clone(2)`, :manpage:`clone3(2)`), entry (:manpage:`setns(2)`), and
fd-reference acquisition (:manpage:`open_tree(2)`, :manpage:`fsmount(2)`) with
the new ``LANDLOCK_PERMISSION_NAMESPACE_USE`` permission flag and
``LANDLOCK_RULE_NAMESPACE`` rule type.
.. _kernel_support:
Kernel support

View File

@ -278,11 +278,12 @@ static inline const char *__trace_landlock_print_layers(
* Every denial event shares three fields. domain is the ID of the
* innermost domain that blocked the access. same_exec tells whether the
* current task is the same executable that entered that domain. logged is
* the domain's audit-logging decision for this denial (its log_status is
* enabled and the per-execution flag selected by same_exec is set); a
* stateless ftrace filter can select the denials the domain submits to
* audit with logged==1, without reconstructing it from the per-execution
* log flags. Denial events order their shared fields as domain, same_exec,
* the complete audit-submission selection for this denial: the domain's
* log_status and per-execution flag, per-object or per-member quiet rules,
* and request-specific no-audit options. This excludes global audit state
* and audit-side filters. A stateless ftrace filter can select the denials
* the domain submits to audit with logged==1, without reconstructing those
* inputs. Denial events order their shared fields as domain, same_exec,
* and logged. Access and permission denial events follow these with the
* blockers verdict input, type-specific fields, and any variable-length field.
*

View File

@ -33,14 +33,14 @@
* (and that they have tested with a kernel that supported them all).
*
* @quiet_access_fs and @quiet_access_net are bitmasks of actions for which a
* denial by this layer will not trigger a log if the corresponding object (or
* its children, for filesystem rules) is marked with the "quiet" bit via
* %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per
* denial by this layer will not trigger an audit record if the corresponding
* object (or its children, for filesystem rules) is marked with the "quiet" bit
* via %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per
* landlock_restrict_self() flags. @quiet_scoped is similar, except that it
* does not require marking any objects as quiet - if the ruleset is created
* with any bits set in @quiet_scoped, then denial of such scoped resources will
* not trigger any log. These 3 fields are available since Landlock ABI version
* 10.
* not trigger an audit record. These three fields are available since Landlock
* ABI version 10.
*
* @quiet_access_fs, @quiet_access_net and @quiet_scoped must be a subset of
* @handled_access_fs, @handled_access_net and @scoped respectively.
@ -66,16 +66,17 @@ struct landlock_ruleset_attr {
__u64 scoped;
/**
* @quiet_access_fs: Bitmask of filesystem actions which should not be
* logged if per-object quiet flag is set.
* submitted to audit if the per-object quiet flag is set.
*/
__u64 quiet_access_fs;
/**
* @quiet_access_net: Bitmask of network actions which should not be
* logged if per-object quiet flag is set.
* submitted to audit if the per-object quiet flag is set.
*/
__u64 quiet_access_net;
/**
* @quiet_scoped: Bitmask of scoped actions which should not be logged.
* @quiet_scoped: Bitmask of scoped actions which should not be
* submitted to audit.
*/
__u64 quiet_scoped;
/**