Linux Landlock Sandboxing: How the Kernel API Works

Updated on
10 min read

Linux Landlock is a kernel feature for adding access restrictions to a process without requiring an administrator to write a system-wide security policy. It is useful to developers building launchers, runtimes, and applications that should limit their own access to files or selected network operations. This guide explains how Landlock rules are composed, what a small sandbox launcher looks like, and where the mechanism fits alongside containers and other Linux security controls.

What Is Linux Landlock?

Landlock is a Linux Security Module (LSM) interface that lets a process restrict itself and its future child processes. A program describes actions it wants to constrain, adds rules for the objects where those actions are allowed, and asks the kernel to enforce the resulting ruleset. The process can add restrictions to its own execution without first asking for elevated privilege.

The Linux kernel Landlock documentation describes the interface and its supported access rights. Landlock currently covers filesystem actions and selected network operations; the available operations depend on the Landlock ABI supported by the running kernel. The Landlock project site provides project information, while the Landlock manual page summarizes the rules and system calls.

Landlock is a restriction mechanism, not a permission-granting system. A rule cannot make a file accessible if ordinary Unix permissions, another LSM, or a mount boundary already denies access. Conversely, granting access in a Landlock rule does not give the process new privileges; it only exempts matching actions from the restrictions in that particular ruleset.

Why Does Landlock Exist?

Traditional Linux security controls often depend on a policy configured by an administrator, a container runtime, or a service manager. Those controls are important, but an application author may not be able to install or modify a host policy for every environment where the program runs. A command-line tool that processes untrusted input, for example, may need to ensure a parser can write only to a designated output directory.

Landlock provides a way for the process itself to request an additional kernel-enforced boundary. It is stackable with the system’s existing access controls, so a program can narrow its own authority without replacing the host’s policy. This is particularly helpful for portable application-level sandboxing and for defense in depth inside a container.

The model also has a deliberate limit: a process can make its own situation stricter, but it cannot use Landlock to confine unrelated processes or escape a restriction already applied to it. That makes it usable by unprivileged programs while keeping the interface oriented around least privilege rather than administration.

How Landlock Works

A typical Landlock setup has three stages:

  1. Create a ruleset. The process specifies which access rights Landlock should handle. Handled rights are the actions that the ruleset can deny unless a rule allows them.
  2. Add rules. The process associates a set of allowed rights with an object, such as a directory hierarchy or a network port. For filesystem rules, a file descriptor identifies the parent directory.
  3. Enforce the ruleset. The process calls landlock_restrict_self() to apply the rules to the calling thread. The restrictions are inherited by child processes, and additional Landlock restrictions can be layered on top.

For an unprivileged process, the usual setup sets no_new_privs with PR_SET_NO_NEW_PRIVS before enforcement. This prevents later execution from gaining privilege through mechanisms such as set-user-ID programs. Each stage can fail: the running kernel may not support the requested ABI or access rights, the process may lack access to the directory it is trying to use, or a syscall may be blocked by another policy.

The key distinction is between handled and allowed rights. If a right is handled by a ruleset but no matching rule allows it, Landlock denies that action. Rights that the ruleset does not handle are outside that ruleset’s control; they are still subject to normal permissions and other security layers. A policy must therefore handle every relevant action it intends to restrict, and must check what the running kernel supports rather than assuming that headers on the build machine describe the deployment kernel.

Filesystem rules apply to a hierarchy below the directory file descriptor named in the rule. Network rules can restrict supported operations on ports when the kernel ABI provides them. Network coverage has expanded over time, so applications should query the ABI and add only features supported by the current kernel. Landlock is not a general-purpose firewall and does not replace network namespaces or host firewall policy.

Core Concepts and Access Rights

Ruleset. This is the policy container that lists the filesystem or network actions to handle. A ruleset grants nothing by itself; rules identify where handled actions remain permitted.

Access rights. Filesystem rights describe operations such as opening files for writing, removing entries, or creating objects. Later ABI versions add rights for additional operations. If a newer right matters to the security boundary, the program should add it only when the queried ABI supports it and fail closed when required protection is unavailable.

Path-beneath rule. A filesystem rule grants selected handled rights for a directory tree. The kernel evaluates the path relative to the directory file descriptor, rather than relying on a string prefix in the application. Correctly selecting the directory and the actions to handle is essential: a rule for one directory does not automatically cover sibling paths.

Thread and child-process scope. landlock_restrict_self() restricts the calling thread and its descendants. In a multithreaded program, calling it on one thread does not automatically mean that all existing threads have joined the same restrictions. A program must enforce the domain at a safe point before creating worker threads, or use the supported thread-synchronization behavior and check its ABI requirements.

ABI version. The Landlock ABI determines which access rights and flags the kernel understands. A successful version query is a useful runtime capability check, not proof that a policy is complete. Review the supported rights, handle errors explicitly, and choose whether to refuse startup or continue with reduced functionality when a required feature is absent.

Landlock can complement the Linux namespaces used for container isolation, but the mechanisms control different things. Namespaces change a process’s view of resources; Landlock restricts selected operations on resources. Neither one alone turns a process into a virtual machine.

Real-World Uses

Document converters and media tools can run parsers with write access limited to a temporary or output directory. If an input file triggers a bug, this can reduce the locations the compromised process can modify, provided the process applies the policy before opening sensitive resources.

Build systems and package tools can isolate individual build steps from parts of a source tree or host filesystem that are not needed for that step. The exact policy needs to account for compilers, interpreters, temporary files, and the toolchain’s expected reads and writes.

Application launchers can apply a common restriction before starting an application, then pass the restrictions to its child processes. This can be useful where a system-wide profile is unavailable, although a launcher still needs a trustworthy policy and careful error handling.

Containers and services can use Landlock as one layer in a larger boundary. The Linux container security guide covers other controls such as capabilities, namespaces, and runtime policy. For centrally administered mandatory access controls, compare the Landlock model with AppArmor profiles; these approaches can coexist rather than being mutually exclusive.

Landlock is not a substitute for memory-safe code, input validation, process isolation, resource limits, or patching. A sandbox can reduce the consequences of a vulnerability, but it does not make vulnerable code safe to run with access to secrets or privileged interfaces.

Getting Started: Build a Small Landlock Launcher

This Linux C example restricts common filesystem mutations to one directory and then executes a command. It is a learning example, not a complete production policy: it does not handle every filesystem right or network operation. It checks the running ABI and includes optional rights when both the headers and kernel support them.

Install a C compiler and Linux UAPI headers on a Debian- or Ubuntu-based test system:

sudo apt update
sudo apt install build-essential linux-libc-dev

Save this as landlock-run.c:

#define _GNU_SOURCE
#include <fcntl.h>
#include <linux/landlock.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int abi;
    int ruleset_fd = -1;
    int directory_fd = -1;
    __u64 handled =
        LANDLOCK_ACCESS_FS_WRITE_FILE |
        LANDLOCK_ACCESS_FS_REMOVE_DIR |
        LANDLOCK_ACCESS_FS_REMOVE_FILE |
        LANDLOCK_ACCESS_FS_MAKE_CHAR |
        LANDLOCK_ACCESS_FS_MAKE_DIR |
        LANDLOCK_ACCESS_FS_MAKE_REG |
        LANDLOCK_ACCESS_FS_MAKE_SOCK |
        LANDLOCK_ACCESS_FS_MAKE_FIFO |
        LANDLOCK_ACCESS_FS_MAKE_BLOCK |
        LANDLOCK_ACCESS_FS_MAKE_SYM;

    if (argc < 3) {
        fprintf(stderr, "usage: %s WRITABLE_DIRECTORY COMMAND [ARG...]\n",
                argv[0]);
        return 2;
    }

    abi = syscall(SYS_landlock_create_ruleset, NULL, 0,
                  LANDLOCK_CREATE_RULESET_VERSION);
    if (abi < 1) {
        perror("Landlock ABI query");
        return 1;
    }

#ifdef LANDLOCK_ACCESS_FS_REFER
    if (abi >= 2)
        handled |= LANDLOCK_ACCESS_FS_REFER;
#endif
#ifdef LANDLOCK_ACCESS_FS_TRUNCATE
    if (abi >= 3)
        handled |= LANDLOCK_ACCESS_FS_TRUNCATE;
#endif

    struct landlock_ruleset_attr ruleset = {
        .handled_access_fs = handled,
    };
    ruleset_fd = syscall(SYS_landlock_create_ruleset, &ruleset,
                         sizeof(ruleset), 0);
    if (ruleset_fd < 0) {
        perror("landlock_create_ruleset");
        goto fail;
    }

    directory_fd = open(argv[1], O_PATH | O_CLOEXEC);
    if (directory_fd < 0) {
        perror("open writable directory");
        goto fail;
    }

    struct landlock_path_beneath_attr rule = {
        .allowed_access = handled,
        .parent_fd = directory_fd,
    };
    if (syscall(SYS_landlock_add_rule, ruleset_fd,
                LANDLOCK_RULE_PATH_BENEATH, &rule, 0) < 0) {
        perror("landlock_add_rule");
        goto fail;
    }

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        perror("PR_SET_NO_NEW_PRIVS");
        goto fail;
    }
    if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0) < 0) {
        perror("landlock_restrict_self");
        goto fail;
    }

    close(directory_fd);
    close(ruleset_fd);
    execvp(argv[2], &argv[2]);
    perror("execvp");
    return 127;

fail:
    if (directory_fd >= 0)
        close(directory_fd);
    if (ruleset_fd >= 0)
        close(ruleset_fd);
    return 1;
}

Compile the launcher and prepare a disposable directory:

cc -Wall -Wextra -O2 landlock-run.c -o landlock-run
mkdir -p /tmp/landlock-allowed

Try a write inside the allowed tree and one outside it:

./landlock-run /tmp/landlock-allowed sh -c \
  'echo allowed > /tmp/landlock-allowed/result; echo denied > /tmp/landlock-outside'

The first redirection should succeed; the second should fail for the handled mutations. Confirm the file was created with cat /tmp/landlock-allowed/result, then inspect the denial from the shell. The program reports a clear error if Landlock is unavailable or if rule installation fails rather than silently launching without the requested restriction.

For a real policy, decide which operations the workload needs, handle all relevant rights supported by the target ABI, and add least-privilege rules for each required path. Set the policy before opening sensitive files and before starting worker threads. Test normal operation and expected denials in a disposable environment; a ruleset that blocks required runtime files can prevent the program from starting.

Common Misconceptions

Landlock does not require root, but it does require kernel support. Unprivileged use is a core property, not a promise that every kernel exposes every feature. A kernel configuration, distribution policy, seccomp filter, or ABI mismatch can make a syscall unavailable or deny it.

A ruleset does not grant access. It only describes restrictions for actions it handles. Unix permissions, capabilities, mounts, and other LSM decisions still apply, and unhandled actions are not denied by that ruleset.

Landlock is not a complete container or application sandbox by itself. It does not provide a separate kernel, automatically create namespaces, limit CPU or memory, or control every syscall and resource. Combine it with appropriate isolation, resource limits, and system policy.

Changelog

  • Initial publication; reviewed against the Linux kernel Landlock documentation.
TBO Editorial

About the Author

TBO Editorial writes about the latest updates about products and services related to Technology, Business, Finance & Lifestyle. Do get in touch if you want to share any useful article with our community.