Go-Landlock

repository·main·Indexed 43 days ago

https://github.com/landlock-lsm/go-landlock

A Go library providing an interface to the Landlock LSM, a Linux kernel feature for unprivileged sandboxing. It allows Go programs to restrict their own access to the filesystem, network operations, and IPC. The library includes tools for ABI version checking and demonstration utilities like landlock-restrict, landlock-restrict-net, and landlock-restrict-scoped.

Tokens
3.9K
Snippets
11
Records
18
Agent score
56%

What's inside Go-Landlock

  1. What is Landlock and what can it restrict?

    main

    Landlock is a Linux kernel feature (introduced in Linux 5.13) that provides an unprivileged sandboxing mechanism. It allows a process to restrict its own access to:

    • Filesystem access
    • Some network operations
    • Some IPC operations

    Go-Landlock provides a Go interface to leverage these kernel capabilities for easier unprivileged sandboxing.

  2. How Go-Landlock enforces process-wide policies

    main

    Because Go is multithreaded and goroutines dynamically map to OS threads, Landlock policies cannot be applied to a single thread reliably. To ensure security, the Go-Landlock API always enables the given Landlock policy for the entire process rather than just the calling thread.

    Depending on the kernel version, Go-Landlock uses one of two mechanisms to achieve this:

    1. ABI V8+ (Linux 7.0+): Uses the LANDLOCK_RESTRICT_SELF_TSYNC flag via the landlock_restrict_self system call.
    2. ABI < V8: Uses the libpsx library to execute the system call on every OS thread in the process.

    For cgo programs, libpsx intercepts pthread calls to ensure all threads (including those not started by the Go runtime) are covered using Unix signals.

  3. Create reusable rule libraries with CompositeRule

    main

    You can build reusable libraries of Landlock rules using landlock.CompositeRule. This allows you to bundle multiple rules into a single landlock.Rule object, avoiding manual list appending when constructing rulesets.

    Note that calling landlock.CompositeRule() with no arguments creates an empty composite rule, which acts as a no-op and adds nothing to the Landlock ruleset at the C API layer.

    // DNSLookup bundles the Landlock rules required for basic DNS lookup.
    func DNSLookup() landlock.Rule {
      return landlock.CompositeRule(
        landlock.RODirs("/etc"),
        landlock.ConnectTCP(53),
      )
    }
  4. Quickstart: Restrict filesystem access with Go-Landlock

    main

    To sandbox a Go program using Landlock, call landlock.V10.BestEffort().RestrictPaths(...) after your program has completed its initialization work. This method allows you to define specific directory permissions, such as read-only (RODirs) or read-write (RWDirs). Once invoked, the program is restricted to only accessing the specified paths.

    err := landlock.V10.BestEffort().RestrictPaths(
        landlock.RODirs("/usr", "/bin"),
        landlock.RWDirs("/tmp"),
    )
  5. Onboard a program to Landlock

    main

    To onboard a program to Landlock, perform a threat model analysis of all inputs the program reads and processes. Distinguish between inputs controlled by the calling process (considered safe) and attacker-provided inputs (must be processed within the sandbox).

    Threat Model Checklist:

    • stdin
    • Input files explicitly opened by the program
    • Configuration files
    • Configuration files used by libraries
    • Incoming network requests
    • Responses to outgoing network requests
  6. Upgrade to a newer Landlock ABI version

    main

    To adopt new Landlock features and kernel capabilities, follow this workflow to upgrade your library and ABI version:

    1. Update the library: Use go get to pull the latest version of the go-landlock package.
    2. Identify current ABI usage: Search your codebase for usages of landlock.V* (e.g., landlock.V1) to determine which ABI version your application is currently targeting. You may also check for usages of landlock.NewConfig or landlock.MustConfig.
    3. Review changes: For every version increment between your current version and the latest, check the upgrade notes in the library source using go doc. These notes specify new restrictions and safety considerations.
    4. Apply and Test: Replace your version constant (e.g., changing landlock.V6 to landlock.V10) and run your test suite. Ensure you test scenarios where the sandbox is actively applied, which typically requires invoking the actual program binary.
    # 1. Update the library
    go get -u github.com/landlock-lsm/go-landlock
    
    # 2. Find existing usage
    grep -R 'landlock\.V'
    
    # 3. Check upgrade notes for a specific version
    go doc github.com/landlock-lsm/go-landlock/landlock.V1
  7. Determine the best timing for Landlock enforcement

    main

    To ensure a secure and functional sandbox, structure your program startup as follows:

    1. Open safe inputs first: Open and process configuration files or other trusted inputs before Landlock enforcement. File descriptors opened before enforcement remain valid and usable after enforcement.
    2. Enforce Landlock: Apply the narrowest possible sandboxing policy.
    3. Process attacker-provided inputs: Handle untrusted data only after the sandbox is active.

    If the program only processes a fixed number of attacker-provided files, you can open them before enforcement and begin reading them after enforcement is active.

  8. Construct custom filesystem rules with landlock.PathAccess

    main

    Use the helper function landlock.PathAccess(accesses, paths...) to construct Landlock filesystem rules. This function allows for detailed control over individual Landlock filesystem access rights for specific paths.

    Important Constraints

    When defining access rights, adhere to the following requirements to ensure correct behavior:

    • Write and Truncate Rights: If you grant LANDLOCK_ACCESS_FS_WRITE_FILE, you must also grant LANDLOCK_ACCESS_FS_TRUNCATE. Conversely, if you grant LANDLOCK_ACCESS_FS_TRUNCATE, you must also grant LANDLOCK_ACCESS_FS_WRITE_FILE. These rights have functional overlap and should always be handled as a pair.
    • Refer Right and ABI Version: The LANDLOCK_ACCESS_FS_REFER right is implicitly denied in Landlock ABI V1. If your ruleset requires LANDLOCK_ACCESS_FS_REFER, you must ensure the environment is using Landlock ABI V2 or higher.
    // Example pattern for constructing rules
    // Note: Actual implementation requires specific access constants and paths
    landlock.PathAccess(landlock.LANDLOCK_ACCESS_FS_READ_FILE | landlock.LANDLOCK_ACCESS_FS_WRITE_FILE | landlock.LANDLOCK_ACCESS_FS_TRUNCATE, "/path/to/file")
  9. Use the landlock-restrict CLI to restrict filesystem access

    main

    The landlock-restrict tool executes a process under Landlock filesystem restrictions. It allows you to define specific paths that the target command can access with varying permissions (Read-Only or Read-Write) and for different object types (Directories or Files).

    Usage Pattern

    landlock-restrict [GLOBAL_FLAGS] [RULE_FLAGS] -- COMMAND [ARGS...]

    Note that the command being executed must use an absolute binary path.

    Rule Flags

    Rules are defined by specifying a mode followed by a list of paths. You can modify path behavior using special tokens within the path list:

    • -ro PATH...: Restrict to Read-Only directories.
    • -rw PATH...: Restrict to Read-Write directories.
    • -rofiles PATH...: Restrict to Read-Only files.
    • -rwfiles PATH...: Restrict to Read-Write files.

    Path Modifiers (must be included in the path list):

    • +refer: Grants the refer access right to the path.
    • +ioctl_dev: Grants ioctl device access.
    • +resolve_unix: Enables Unix domain socket resolution.

    Global Flags

    • -v: Enables verbose logging.
    • -l: Enables audit logging for subprocesses.
    • -strict: Disables "best effort" mode, enforcing strict Landlock constraints.
    • -1 through -10: Selects the Landlock version (e.g., -10 for V10).

    Example

    To run /usr/bin/ls with read-only access to /tmp and read-write access to /home/user/data (including the refer right):

    landlock-restrict -ro /tmp -rw +refer /home/user/data -- /usr/bin/ls /home/user/data
    landlock-restrict -ro /tmp -rw +refer /home/user/data -- /usr/bin/ls /home/user/data
  10. Configure file system access rights

    main

    Landlock provides presets for common filesystem access patterns. Use these to simplify policy creation:

    • landlock.ROFiles: Read-only access to files.
    • landlock.RODirs: Read-only access to directories.
    • landlock.RWFiles: Read-write access to files.
    • landlock.RWDirs: Read-write access to directories.

    Advanced Filesystem Requirements:

    • Linking/Renaming across directories: If your program needs to link or rename files across different directories, you must grant the LANDLOCK_ACCESS_FS_REFER right using FSRule.WithRefer(). Note that this requires Landlock ABI V2 or higher.
    • Device IOCTLs: If your program needs to invoke IOCTLs on newly opened device files, you must specifically grant that access right using FSRule.WithIoctlDev().
  11. Configure network access rights

    main

    Landlock's ability to restrict network access is limited. For comprehensive network sandboxing, you should augment Landlock with other mechanisms like Seccomp-BPF.

    Known Limitations:

    • Non-TCP protocols: A variety of protocols outside of TCP are currently not restrictable by Landlock.
    • Multipath TCP: The bind() operation for Multipath TCP is not yet restrictable, though resulting TCP servers remain backwards compatible with vanilla TCP clients.