landlock

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: MIT Imports: 9 Imported by: 41

Documentation

Overview

Package landlock enforces sandboxing policies using Linux's Landlock LSM.

Restricting file access

The following invocation will restrict all goroutines so that they can only read from /usr, /bin and /tmp, and only write to /tmp:

err := landlock.V8.BestEffort().RestrictPaths(
    landlock.RODirs("/usr", "/bin"),
    landlock.RWDirs("/tmp"),
)

This will restrict file access using Landlock V8, if available. If unavailable, it will attempt using earlier Landlock versions than the one requested. If no Landlock version is available, it will still succeed, without restricting file accesses.

Restricting networking

The following invocation will restrict all goroutines so that they can only bind to TCP port 8080 and only connect to TCP port 53:

err := landlock.V8.BestEffort().RestrictNet(
    landlock.BindTCP(8080),
    landlock.ConnectTCP(53),
)

This functionality is available since Landlock V4.

**IMPORTANT:** Landlock's TCP restrictions only apply to "classic" TCP sockets, not to Multipath TCP sockets, which can also serve non-multipath clients. Since Go 1.24, Multipath TCP is the default for net.Listen, and therefore, net.Listen can not be restricted with Landlock.

The bug needs to be fixed in the Linux kernel and is tracked here: https://github.com/landlock-lsm/linux/issues/54

Restricting IPC scopes

The following invocation will restrict IPC to more privileged Landlock domains, if possible:

err := landlock.V8.BestEffort().RestrictScoped()

This functionality is available since Landlock V6.

Restricting all types of operations at once

The following invocation restricts all types of operations at once. The effect is the same as calling Config.RestrictPaths, Config.RestrictNet and Config.RestrictScoped one after another, but it happens in one step.

err := landlock.V8.BestEffort().Restrict(
    landlock.RODirs("/usr", "/bin"),
    landlock.RWDirs("/tmp"),
    landlock.BindTCP(8080),
    landlock.ConnectTCP(53),
)

More possible invocations

landlock.V8.RestrictPaths(...) (without the call to Config.BestEffort) enforces the given rules using the capabilities of Landlock V8, but returns an error if that functionality is not available on the system that the program is running on.

If Audit logging is enabled in the Linux kernel, kernels with Landlock ABI V7 and higher will log Landlock denials to the audit log. By default, this only happens for denials affecting the same process which enforced the policy, and it also happens for nested Landlock policies.

The situations in which audit logging happens can be configured using Config.DisableLoggingForOriginatingProcess, Config.EnableLoggingForSubprocesses and Config.DisableLoggingForSubdomains.

Landlock ABI versioning

The Landlock ABI is versioned, so that callers can probe for the availability of different Landlock features.

When using the Go-Landlock package, callers need to identify at which ABI level they want to use Landlock and call one of the restriction methods (e.g., Config.RestrictPaths) on the corresponding ABI constant.

When new Landlock versions become available in landlock, users will manually need to upgrade their usages to higher Landlock versions, as there is a risk that new Landlock versions will break operations that their programs rely on.

The documentation for landlock.V8 and the adjacent version numbers explains what you need to look for when upgrading between Landlock ABI versions.

Graceful degradation on older kernels

Programs that get run on different kernel versions will want to use the Config.BestEffort method to gracefully degrade to using the best available Landlock version on the current kernel.

In this case, the Go-Landlock library will enforce as much as possible, but it will ensure that all the requested access rights are permitted after Landlock enforcement.

Current limitations (Filesystem)

Landlock can not currently restrict all filesystem operations. The operations that can and can not be restricted yet are listed in the Kernel Documentation about Access Rights.

Enabling Landlock implicitly turns off the following filesystem features:

  • File reparenting: renaming or linking a file to a different parent directory is denied, unless it is explicitly enabled on both directories with the "Refer" access modifier, and the new target directory does not grant the file additional rights through its Landlock access rules.
  • Filesystem topology modification: arbitrary mounts are always denied.

These are Landlock limitations that will be resolved in future versions. See the Kernel Documentation about Current Limitations for more details.

Multithreading Limitations

This warning only applies to programs using cgo and linking C libraries that start OS threads through means other than pthread_create() before landlock is called:

Before Landlock ABI V8, when using cgo, the landlock package relies on libpsx in order to apply the rules across all OS threads, (rather than just the ones managed by the Go runtime). psx achieves this by wrapping the C-level pthread_create() API which is very commonly used on UNIX to start threads. However, C libraries calling clone(2) through other means before landlock is called might still create threads that won't have Landlock protections.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Landlock V1 support (basic file operations).
	V1 = abiInfos[1].asConfig()
	// Landlock V2 support (V1 + file reparenting between different directories)
	V2 = abiInfos[2].asConfig()
	// Landlock V3 support (V2 + file truncation)
	V3 = abiInfos[3].asConfig()
	// Landlock V4 support (V3 + networking)
	V4 = abiInfos[4].asConfig()
	// Landlock V5 support (V4 + ioctl on device files)
	V5 = abiInfos[5].asConfig()
	// Landlock V6 support (V5 + IPC scopes for signals and
	// Abstract UNIX Domain Sockets (see unix(7)))
	V6 = abiInfos[6].asConfig()
	// Landlock V7 support (V6 + logging support)
	V7 = abiInfos[7].asConfig()
	// Landlock V8 support (V7 + thread synchronization)
	V8 = abiInfos[8].asConfig()
)
  • Upgrading to V2
  • Upgrading to V3
  • Upgrading to V4
  • Upgrading to V5
  • Upgrading to V6
  • Upgrading to V7
  • Upgrading to V8

These are Landlock configurations for the currently supported Landlock ABI versions, configured to restrict the highest possible set of operations possible for each version.

The higher the ABI version, the more operations Landlock will be able to restrict.

Upgrading to V2

Upgrading from V1 to V2 should not break existing programs. Programs that need it can now move and link files between directories with the "refer" access right.

The RWFiles and RWDirs helpers do not grant the "refer" right automatically, but you can ask for the access right explicitly using FSRule.WithRefer.

Upgrading to V3

Upgrading from V2 to V3 should not break existing programs, as long as they are using [RWPaths] and RWDirs to express access rights.

Programs that spell out individual access rights might need to add the "truncate" access right to the required access rights. Note that the truncation right is often required for opening files for writing, because that often does an implicit truncation for existing files.

Upgrading to V4

When upgrading from V3 to V4, the TCP connect() and bind() operations (required for net.Dial and net.Listen) will be restricted when using Config.Restrict or Config.RestrictNet.

Note: This only affects "classic" TCP, not Multipath TCP. Multipath TCP, which is the default for net.Listen since Go 1.24, continues to work.

For comprehensive network sandboxing at this ABI level, we recommend using additional sandboxing mechanisms.

Upgrading to V5

When upgrading from V4 to V5, if you use Config.Restrict or Config.RestrictPaths, IOCTL operations on device files are now restricted. A small list of common IOCTLs continues to be permitted and is listed in the Kernel Documentation about Access Rights.

The RWFiles and RWDirs helpers do not grant IOCTL rights automatically, but you can ask for the access right explicitly using FSRule.WithIoctlDev.

Upgrading to V6

When upgrading from V5 to V6, the following operations are newly restricted if you are using Config.Restrict or Config.RestrictScoped:

  • Abstract UNIX Domain Socket connections that are reaching out to a server outside of the enforced Landlock domain.
  • UNIX signals that are signaling a program which is running outside of the enforced Landlock domain.

Upgrading to V7

Upgrading from V6 to V7 is safe.

With ABI V7, the following methods can be newly used to influence audit logging of Landlock denials:

It is safe to use these in combination with Config.BestEffort, also on Linux systems that only support older Landlock ABIs.

When one of these logging flags is set but Config.BestEffort is omitted, you are asserting that you are running on a kernel that supports ABI V7+, and you will get an error at restriction time if the kernel does not support that.

Upgrading to V8

Upgrading from V7 to V8 is safe.

ABI V8 adds an under-the-hood improvement for multithreaded Landlock enforcement, which is used by Go-Landlock whenever it is available. The landlock.V8 configuration preset restricts the same operations as landlock.V7.

Functions

This section is empty.

Types

type AccessFSSet

type AccessFSSet uint64

AccessFSSet is a set of Landlockable filesystem access operations.

func (AccessFSSet) String

func (a AccessFSSet) String() string

type AccessNetSet

type AccessNetSet uint64

AccessNetSet is a set of Landlockable network access rights.

func (AccessNetSet) String

func (a AccessNetSet) String() string

type Config

type Config struct {
	// contains filtered or unexported fields
}

The Landlock configuration describes the desired set of landlockable operations to be restricted and the constraints on it (e.g., best effort mode).

It is recommended to use one of the preset configurations such as landlock.V8, which restrict the full set of access rights available at this Landlock ABI version.

func MustConfig

func MustConfig(args ...any) Config

MustConfig is like NewConfig but panics on error.

func NewConfig

func NewConfig(args ...any) (*Config, error)

NewConfig creates a new Landlock configuration with the given parameters.

Passing an AccessFSSet, AccessNetSet or ScopedSet will set these as the set of filesystem/network/scoped operations to restrict when enabling Landlock. The sets need to stay within the bounds of what Go-Landlock supports. (If you are getting an error, you might need to upgrade to a newer version of Go-Landlock.)

Example:

cfg, err := NewConfig(AccessFSSet(llsyscall.AccessFSExecute))

func (Config) BestEffort

func (c Config) BestEffort() Config

BestEffort returns a config that will opportunistically enforce the strongest rules it can, up to the given ABI version, working with the level of Landlock support available in the running kernel.

Warning: A best-effort call to RestrictPaths() will succeed without error even when Landlock is not available at all on the current kernel.

func (Config) DisableLoggingForOriginatingProcess added in v0.7.0

func (c Config) DisableLoggingForOriginatingProcess() Config

DisableLoggingForOriginatingProcess disables logging of denied accesses originating from the thread creating the Landlock domain, as well as its children, as long as they continue running the same executable code (i.e., without an intervening execve(2) call).

This is intended for programs that execute unknown code without invoking execve(2), such as script interpreters. Programs that only sandbox themselves should not set this flag, so users can be notified of unauthorized access attempts via system logs.

Requires a Linux kernel that supports Landlock ABI V7 or higher. In combination with Config.BestEffort, the logging option will be omitted on older kernels and not result in an error.

func (Config) DisableLoggingForSubdomains added in v0.7.0

func (c Config) DisableLoggingForSubdomains() Config

DisableLoggingForSubdomains disables logging of denied accesses originating from nested Landlock domains created by the caller or its descendants. This flag should be set according to runtime configuration, not hardcoded, to avoid suppressing important security events.

It is useful for container runtimes or sandboxing tools that may launch programs which themselves create Landlock domains and could otherwise generate excessive logs. Unlike [DisableLoggingForOriginatingProcess], this affects future nested domains, not the one being created.

Requires a Linux kernel that supports Landlock ABI V7 or higher. In combination with Config.BestEffort, the logging option will be omitted on older kernels and not result in an error.

func (Config) EnableLoggingForSubprocesses added in v0.7.0

func (c Config) EnableLoggingForSubprocesses() Config

EnableLoggingForSubprocesses enables logging of denied accesses after an execve(2) call, providing visibility into unauthorized access attempts by newly executed programs within the created Landlock domain.

This flag is recommended only when all potential executables in the domain are expected to comply with the access restrictions, as excessive audit log entries could make it more difficult to identify critical events.

Requires a Linux kernel that supports Landlock ABI V7 or higher. In combination with Config.BestEffort, the logging option will be omitted on older kernels and not result in an error.

func (Config) Restrict

func (c Config) Restrict(rules ...Rule) error

Restrict restricts all types of access rights which are restrictable with the Config.

Using Landlock V8, this is equivalent to calling all of Config.RestrictPaths, Config.RestrictNet and Config.RestrictScoped with the respective subset of rule arguments that apply to them.

In future Landlock versions, this function might restrict additional types of access rights which are specified in the Config.

func (Config) RestrictNet

func (c Config) RestrictNet(rules ...Rule) error

RestrictNet restricts network access in all goroutines.

Using Landlock V4, this function restricts the use of bind(2) and connect(2) for TCP ports, unless those TCP ports are specifically permitted using these rules:

  • ConnectTCP permits connect(2) operations to a given TCP port.
  • BindTCP permits bind(2) operations on a given TCP port.

These network access rights are documented in more depth in the Kernel Documentation about Network flags.

The restrictions do not currently work with Multipath TCP, which is the default for net.Listen since Go 1.24. See the discussion in the package-level documentation.

Landlock's network sandboxing support is still incomplete as of Landlock ABI V8 and we recommend using additional sandboxing mechanisms to augment it.

To restrict multiple types of access rights at the same time, use the more generic Config.Restrict.

func (Config) RestrictPaths

func (c Config) RestrictPaths(rules ...Rule) error

RestrictPaths restricts all goroutines to only "see" the files provided as inputs. After this call successfully returns, the goroutines will only be able to use files in the ways as they were specified in advance in the call to RestrictPaths.

Example: The following invocation will restrict all goroutines so that they can only read from /usr, /bin and /tmp, and only write to /tmp:

err := landlock.V8.RestrictPaths(
    landlock.RODirs("/usr", "/bin"),
    landlock.RWDirs("/tmp"),
)
if err != nil {
    log.Fatalf("landlock.V8.RestrictPaths(): %v", err)
}

RestrictPaths returns an error if any of the given paths does not denote an actual directory or file, or if Landlock can't be enforced using the desired ABI version constraints.

RestrictPaths also sets the "no new privileges" flag for all OS threads managed by the Go runtime.

Restrictable access rights

The notions of what "reading" and "writing" mean are limited by what the selected Landlock version supports.

Calling RestrictPaths with a given Landlock ABI version will inhibit all future calls to the access rights supported by this ABI version, unless the accessed path is in a file hierarchy that is specifically allow-listed for a specific set of access rights.

The overall set of operations that RestrictPaths can restrict are:

For reading:

  • Executing a file (V1+)
  • Opening a file with read access (V1+)
  • Opening a directory or listing its content (V1+)

For writing:

  • Opening a file with write access (V1+)
  • Truncating file contents (V3+)

For directory manipulation:

  • Removing an empty directory or renaming one (V1+)
  • Removing (or renaming) a file (V1+)
  • Creating (or renaming or linking) a character device (V1+)
  • Creating (or renaming) a directory (V1+)
  • Creating (or renaming or linking) a regular file (V1+)
  • Creating (or renaming or linking) a UNIX domain socket (V1+)
  • Creating (or renaming or linking) a named pipe (V1+)
  • Creating (or renaming or linking) a block device (V1+)
  • Creating (or renaming or linking) a symbolic link (V1+)
  • Renaming or linking a file between directories (V2+)

Future versions of Landlock will be able to inhibit more operations. Quoting the Landlock documentation:

It is currently not possible to restrict some file-related
actions accessible through these syscall families: chdir(2),
stat(2), flock(2), chmod(2), chown(2), setxattr(2), utime(2),
ioctl(2), fcntl(2), access(2). Future Landlock evolutions will
enable to restrict them.

The access rights are documented in more depth in the Kernel Documentation about Access Rights.

Helper functions for selecting access rights

These helper functions help selecting common subsets of access rights:

  • RODirs selects access rights in the group "for reading". In V1, this means reading files, listing directories and executing files.
  • RWDirs selects access rights in the group "for reading", "for writing" and "for directory manipulation". This grants the full set of access rights which are available within the configuration.
  • ROFiles is like RODirs, but does not select directory-specific access rights. In V1, this means reading and executing files.
  • RWFiles is like RWDirs, but does not select directory-specific access rights. In V1, this means reading, writing and executing files.

The PathAccess rule lets callers define custom subsets of these access rights. AccessFSSets permitted using PathAccess must be a subset of the AccessFSSet that the Config restricts.

To restrict multiple types of access rights at the same time, use the more generic Config.Restrict.

func (Config) RestrictScoped

func (c Config) RestrictScoped() error

RestrictScoped restricts scoped IPC access in all goroutines.

Starting with Landlock V6, this restricts the use of IPC mechanisms like signals and abstract UNIX domain sockets, when talking to processes in more privileged Landlock domains.

To restrict multiple types of access rights at the same time, use the more generic Config.Restrict.

func (Config) String

func (c Config) String() string

String builds a human-readable representation of the Config.

type FSRule

type FSRule struct {
	// contains filtered or unexported fields
}

FSRule is a Rule which permits access to filesystem paths.

func PathAccess

func PathAccess(accessFS AccessFSSet, paths ...string) FSRule

PathAccess is a Rule which grants the access rights specified by accessFS to the file hierarchies under the given paths.

When accessFS is larger than what is permitted by the Landlock version in use, only the applicable subset of accessFS will be used.

Most users should use the functions RODirs, RWDirs, ROFiles and RWFiles instead, which provide canned rules for commonly used values of accessFS.

Filesystem access rights are represented using bits in a uint64. The individual access rights and their meaning are defined in the landlock/syscall package and explained further in the Kernel Documentation about Access Rights.

accessFS must be a subset of the permissions that the Config restricts.

func RODirs

func RODirs(paths ...string) FSRule

RODirs is a Rule which grants common read-only access to files and directories and permits executing files.

func ROFiles

func ROFiles(paths ...string) FSRule

ROFiles is a Rule which grants common read access to individual files, but not to directories, for the file hierarchies under the given paths.

func RWDirs

func RWDirs(paths ...string) FSRule

RWDirs is a Rule which grants full (read and write) access to files and directories under the given paths.

Noteworthy operations which are *not* covered by RWDirs:

  • RWDirs does *not* grant the right to *reparent or link* files across different directories. If this access right is required, use FSRule.WithRefer.

  • RWDirs does *not* grant the right to *use IOCTL* on device files. If this access right is required, use FSRule.WithIoctlDev.

func RWFiles

func RWFiles(paths ...string) FSRule

RWFiles is a Rule which grants common read and write access to files under the given paths, but it does not permit access to directories.

Noteworthy operations which are *not* covered by RWFiles:

  • RWFiles does *not* grant the right to *use IOCTL* on device files. If this access right is required, use FSRule.WithIoctlDev.

func (FSRule) IgnoreIfMissing

func (r FSRule) IgnoreIfMissing() FSRule

IgnoreIfMissing gracefully ignores missing paths.

Under normal circumstances, referring to a non-existing path in a rule would lead to a runtime error. When the rule uses the IgnoreIfMissing modifier, these runtime errors are ignored. This can be useful for optional configuration paths, which are only read but not written.

func (FSRule) String

func (r FSRule) String() string

func (FSRule) WithIoctlDev

func (r FSRule) WithIoctlDev() FSRule

WithIoctlDev adds the "ioctl dev" access right to a FSRule.

It is uncommon to need this access right, so it is not part of RWFiles or RWDirs.

func (FSRule) WithRefer

func (r FSRule) WithRefer() FSRule

WithRefer adds the "refer" access right to a FSRule.

Notably, asking for the "refer" access right does not work on kernels below 5.19. In best effort mode, this will fall back to not using Landlock enforcement at all on these kernel versions. If you want to use Landlock on these kernels, do not use the "refer" access right.

type NetRule

type NetRule struct {
	// contains filtered or unexported fields
}

func BindTCP

func BindTCP(port uint16) NetRule

BindTCP is a Rule which grants the right to bind a socket to a given TCP port.

In Go, the bind(2) operation is usually run as part of net.Listen. Since Go 1.24, net.Listen defaults to Multipath TCP, see the discussion in the package documentation.

func ConnectTCP

func ConnectTCP(port uint16) NetRule

ConnectTCP is a Rule which grants the right to connect a socket to a given TCP port.

In Go, the connect(2) operation is usually run as part of net.Dial.

func (NetRule) String

func (n NetRule) String() string

type PathOpt deprecated

type PathOpt = Rule

PathOpt is a deprecated alias for Rule.

Deprecated: This alias is only kept around for backwards compatibility and will disappear with the next major release.

type Rule

type Rule interface {
	// contains filtered or unexported methods
}

Rule represents one or more Landlock rules which can be added to a Landlock ruleset.

func CompositeRule added in v0.8.0

func CompositeRule(rules ...Rule) Rule

CompositeRule returns a single rule representing multiple rules at once.

A composite rule passed to Config.Restrict behaves the same as passing all sub-rules individually. Composite rules are not strictly necessary in Go-Landlock, but useful for building libraries of reusable Landlock rules.

type ScopedSet

type ScopedSet uint64

ScopedSet is a set of restrictable IPC scopes.

When the scope is restricted, these IPC operations can not be used to communicate with a process in a more privileged sandbox domain (e.g., a process in a parent domain or a non-sandboxed process).

func (ScopedSet) String

func (a ScopedSet) String() string

Directories

Path Synopsis
Package lltest has helpers for Landlock-enabled tests.
Package lltest has helpers for Landlock-enabled tests.
Package syscall provides a low-level interface to the Linux Landlock sandboxing feature.
Package syscall provides a low-level interface to the Linux Landlock sandboxing feature.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL