landlock

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Feb 6, 2024 License: MPL-2.0 Imports: 9 Imported by: 10

README

go-landlock

Go Reference CI Tests

The go-landlock module provides a Go library for interfacing with the Linux kernel landlock feature. Landlock is a mechanism for minimizing filesystem access to a Linux process. Using go-landlock does not require root or any escalated capabilities.

Requirements

Requires Linux 5.13+ with Landlock enabled. There is a no-op implementation provided for non-Linux platforms for convenience, which provide no isolation.

Most recent Linux distributions should be supported.

Verified distros

  • Ubuntu 22.04 LTS
  • Ubuntu 20.04 LTS
  • Fedora 36

The minimum Go version is go1.19.

Install

Use go get to grab the latest version of go-landlock.

go get -u github.com/shoenig/go-landlock@latest
Influence

This library is made possible after studying several sources, including but not limited to

API

Full documentation is on pkg.go.dev.

The go-landlock package aims to provide a simple abstraction over the Kernel landlock implementation details. Simply create a Locker with the Path's to expose, and then call .Lock() to isolate the process. The process will only be able to access the files and directories, at the file modes specified. Attempts to access any other filesystem paths will result in errors returned from the kernel system calls (like open).

Groups of commonly used paths are pre-defined for convenience.

  • Shared() : for executing dynamically linked binaries
  • Stdio() : for using standard I/O operations
  • TTY() : for using terminal operations
  • Tmp() : for accessing system tmp directory
  • VMInfo() : for reading system information
  • DNS() : for reading DNS related information
  • Certs() : for reading system SSL/TLS certificate files

Custom paths can be specified using File() or Dir(). Each takes 2 arguments - the actual filepath (absolute or relative), and a mode string. A mode string describes what level of file mode permissions to allow. Must be a subset of "rwxc".

  • r : enable read permissions
  • w : enable write permissions
  • x : enable execute permissions
  • c : enable create permissions

Once a Locker is configured, isolation starts on the call to Lock(). The level of safety is configured by passing either Mandatory or Try.

  • Mandatory : return an error is Landlock is unsupported or activation causes an error
  • Try : continue without error regardless if landlock is supported or working
  • OnlySupported : like Mandatory, but returns no error if the operating system does not support landlock

Once a process has been locked, it cannot be unlocked. Any descendent processes of the locked process will also be locked, and cannot be unlocked. A child process can further restrict itself via additional uses of landlock.

Examples
complete example

This is a complete example of a small program which is able to read from /etc/os-release, and is unable to access any other part of the filesystem

package main

import (
  "fmt"
  "os"

  "github.com/shoenig/go-landlock"
)

func main() {
  l := landlock.New(
    landlock.File("/etc/os-release", "r"),
  )
  err := l.Lock(landlock.Mandatory)
  if err != nil {
    panic(err)
  }

  _, err = os.ReadFile("/etc/os-release")
  fmt.Println("reading /etc/os-release", err)

  _, err = os.ReadFile("/etc/passwd")
  fmt.Println("reading /etc/passwd", err)
}
➜ go run main.go
reading /etc/os-release <nil>
reading /etc/passwd open /etc/passwd: permission denied
shared objects (dynamic linking)

Programs that exec other processes may need to un-restrict a set of shared object libraries. go-landlock provides the Shared() path to simplify this configuration.

l := landlock.New(
  landlock.Shared(), // common shared object files
  landlock.File("/usr/bin/echo", "rx"),
)

// e.g. execute echo in a sub-process
ssl/tls/dns (networking)

Programs that make use of the internet can use the DNS() and Certs() helper paths to unlock necessary files for DNS resolution and reading system SSL/TLS certificates.

l := landlock.New(
  landlock.DNS(),
  landlock.Certs(),
)

// e.g.
// _, err = http.Get("https://example.com")
License

Open source under the MPL

Documentation

Overview

Package landlock provides a Go library for using the landlock feature of the modern Linux kernel.

The landlock feature of the kernel is used to isolate a process from accessing the filesystem except for blessed paths and access modes.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrLandlockNotAvailable = errors.New("landlock not available")
	ErrLandlockFailedToLock = errors.New("landlock failed to lock")
)
View Source
var (
	// ErrImproperType indicates an improper filetype string
	ErrImproperType = errors.New("improper filetype")

	// ErrImproperMode indicates an improper mode string
	ErrImproperMode = errors.New("improper mode")

	// ErrImproperPath indicates an improper filepath string
	ErrImproperPath = errors.New("improper path")
)
View Source
var (
	// ErrVersionUndetectable indicates a failure when checking landlock version
	ErrVersionUndetectable = errors.New("landlock version detection failure")

	// ErrNotSupported indicates landlock is not supported on this system
	ErrNotSupported = errors.New("landlock not supported")
)

Functions

func Available

func Available() bool

Available returns true if landlock is available, false otherwise.

func Detect

func Detect() (int, error)

Detect returns the version of landlock available, or an error.

func IsProperMode

func IsProperMode(mode string) bool

IsProperMode returns whether mode conforms to the "rwcx" characters of a mode string.

func IsProperPath

func IsProperPath(path string) bool

IsProperPath returns whether fp conforms to a valid filepath.

func IsProperType

func IsProperType(filetype string) bool

Types

type Locker

type Locker interface {
	fmt.Stringer
	Lock(s Safety) error
}

A Locker is an interface over the Kernel landlock LSM feature.

func New

func New(paths ...*Path) Locker

New creates a Locker that allows the given paths and permissions.

type Path

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

func Certs

func Certs() *Path

Certs creates a Path representing the common files needed for SSL/TLS certificate validation.

func DNS

func DNS() *Path

DNS creates a Path representing the common files needed for DNS related operations.

func Dir

func Dir(path, mode string) *Path

Dir creates a Path given path and mode, associated with a directory.

func File

func File(path, mode string) *Path

File creates a Path given the path and mode, associated with a file.

File should be used with regular files, FIFOs, sockets, symlinks.

A File cannot be used to create or delete files.

func ParsePath

func ParsePath(s string) (*Path, error)

ParsePath parses s into a Path.

s must contain 'd' or 'f' indicating whether the path represents a file or directory, followed by a mode string indicating the permissions of the path, followed by a filepath.

A mode is zero or more of: - 'r' - enable read permission - 'w' - enable write permission - 'c' - enable create permission - 'x' - enable execute permission

s must be in the form "[kind]:[mode]:path"

"d:rw:$HOME" would enable reading and writing to the users home directory.

"f:x:/bin/cat" would enable executing the /bin/cat file.

It is recommended to use the File or Dir helper functions.

func Shared

func Shared() *Path

Shared creates a Path representing the common files and directories needed for dynamic shared object files.

Use Shared when allowing the execution of dynamically linked binaries.

func Stdio

func Stdio() *Path

Stdio creates a Path representing the common files and directories needed for standard I/O operations.

func TTY

func TTY() *Path

TTY creates a path representing common files needed for terminal operations.

func Tmp

func Tmp() *Path

Tmp creates a Path representing the common files and directories needed for reading and writing to the system tmp space.

func VMInfo

func VMInfo() *Path

VMInfo creates a Path representing the common files and directories needed for virtual machines and system introspection.

func (*Path) Equal

func (p *Path) Equal(o *Path) bool

Equal returns true if p is equal to o in terms of mode and filepath.

func (*Path) Hash

func (p *Path) Hash() string

Hash returns the path element of p.

func (*Path) String

func (p *Path) String() string

type Safety

type Safety byte

Safety indicates the enforcement behavior on systems where landlock does not exist or operate as expected.

const (
	// Mandatory mode will return an error on failure, including on
	// systems where landlock is not supported.
	Mandatory Safety = iota

	// OnlySupported will return an error on failure if running on a supported
	// operating system (Linux), or no error otherwise. Unlike OnlyAvailable,
	// this includes returning an error on systems where the Linux kernel was
	// built without landlock support.
	OnlySupported

	// OnlyAvailable will return an error on failure if running in an environment
	// where landlock is detected and available, or no error otherwise. Unlike
	// OnlySupported, OnlyAvailable does not cause an error on Linux systems built
	// without landlock support.
	OnlyAvailable

	// Try mode will continue with no error on failure.
	Try
)

Jump to

Keyboard shortcuts

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