A Go library for the Linux Landlock sandboxing feature

Go landlock library

The Go landlock library provides an interface to Linux 5.13's Landlock kernel sandboxing features.

The library provides access to Landlock on two levels:

High level interface

To restrict the current process to only see a given set of paths and subdirectories, use golandlock.RestrictPaths.

Example: The following invocation will restrict the current thread so that it can only read from /usr, /bin and /tmp, and only write to /tmp.

err := golandlock.RestrictPaths(
    golandlock.RODirs("/usr", "/bin"),
    golandlock.RWDirs("/tmp"),
)

Low level interface

The low level interface in golandlock/syscall provides access to the raw Landlock syscalls.

Caveats

Some filesystem operations can't currently be restricted with Landlock. Familiarity with the Landlock kernel interface and its limitations is assumed (documentation, filesystem flags:

It is currently not possible to restrict some file-related actions accessible through these syscall families: chdir(2), truncate(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.

Owner
Günther Noack
Dirty deeds, done dirt cheap
Günther Noack
Comments
  • Move golandlock under landlock-lsm

    Move golandlock under landlock-lsm

    Let's move golandlock into github.com/landlock-lsm, as offered by @l0kod.

    Constraints:

    • The imported Go package name (last part of path) should only contain letters
      • underscores not recommended, dashes don't work
      • Only applies to last path component. It can also be nested in an additional subdirectory and that wouldn't look completely wrong.
      • Prior art: the popular package github.com/go-cmp/cmp and others (I grepped my own Go imports and found a few)
    • There is already github.com/landlock-lsm/rust-landlock, and naming it similarly would be logical.
    • Let's move it sooner rather than later before the old location is used too much

    Proposal

    Let's move it to: github.com/landlock-lsm/go-landlock/landlock The name is a little bit repetitive, but:

    • last path component is just "landlock" and that would read nicely when the package is being used.
    • It's consistent with the location of the Rust package

    What we'd need to do

    Copy the repo into the desired location. Important: We should remove the git tag while doing so, so that the Go tooling doesn't interpret the new location as a stable Go module.

    (I suspect that the right way to copy this over is like this: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/duplicating-a-repository so that it's not a "fork" in the Github sense, but stays disconnected from the original location?)

    In the new git repo:

    • move the Go packages into a new "landlock" subdirectory
    • adapt the spelled-out package paths in all locations

    In the old git repo: Add a big warning pointing to the new location.

    Alternatives considered

    There is also https://docs.github.com/en/github/administering-a-repository/managing-repository-settings/transferring-a-repository If I'm reading this correctly that would make the repo accessible under both the new location and the old location (as redirect). I suspect that wouldn't play well with the Go tooling, where the modules spell out their own location in some places.

  • Presence of CGO breaks the prctl call

    Presence of CGO breaks the prctl call

    Since fixing #5, the presence of the net/http package breaks the (slightly altered) prctl invocation.

    To reproduce on kernel 5.13, import

    import (
      _ "net/http"
    )
    

    and immediately call golandlock.V1.RestrictPath() in main().

    Observed results:

    2021/07/24 18:24:01 Landlock: Landlock v1: prctl(PR_SET_NO_NEW_PRIVS): operation not supported
    exit status 1
    

    Expected results: should have worked

  • Make Restrict() future-proof

    Make Restrict() future-proof

    This package does not provide stability guarantees yet, and we need to fix the API so that we can provide these guarantees.

    Things I'd like to fix:

    • Arguments: When calling Restrict, it's difficult to tell apart which of the arguments is which. They all have the same type.
    • Flexibility: There is little flexibility to use different sets of permissions other than the four built-in ones. (Also see bug #3)
    • Future Landlock compatibility: The backwards compatibility story for future versions of Landlock is unclear: To make sure that calling programs don't break when golandlock starts supporting a future Landlock ABI v2, callers will need to take action to update to new versions at some point.
    • Old kernel compatibility: The backwards compatibility story for older kernels is unclear: As @l0kod pointed out, as soon as Landlock will have multiple ABI levels, there will be a use case for graceful degradation on old kernels, where the Restrict() call should lock permissions down as much as possible given the kernel that it's running on. Other users might want to still fail in that case though.

    Prior art for backwards compatibility: @l0kod's rust-landlock supports multiple backwards compatibility strategies. (See ErrorThreshold enum)

  • pathOpt struct external usage

    pathOpt struct external usage

    Dear Günther! Is it possible to make pathOpt structure for external usage cause it would be convenient in runc/containerd implementation? image

    It could be easily parsed from runc spec .json and into opts array directly and then fed to RestrictPaths(opts ...pathOpt) function

  • Ensure that PathAccess(...).accessFS ⊆ cfg.handledAccessFS

    Ensure that PathAccess(...).accessFS ⊆ cfg.handledAccessFS

    Ensure that

    PathAccess(...).accessFS ⊆ cfg.handledAccessFS

    for all path options constructed with PathAccess().

    This is a conservative choice, but it's easier to enforce that way.

    RODirs() and friends are more "magic" - they let you give broad access permissions on entire directories, and that is supposed to work even when the user specifies a smaller AccessFSSet in their config. On the other hand, when users pass a custom AccessFSSet to PathAccess(), they should have a clear understanding of the configuration that they will use it in, and it can be expected that they ensure it is a subset.

    Examples

    This is a good example. On library upgrade, it should be enough to bump the version number, in most cases. Note that a Go-landlock library that supports V3 at some point still needs to do the exact same thing in the V2 case though:

    landlock.V2.BestEffort().RestrictPaths(
      landlock.RODirs("/bin", "/usr", "/etc"),
      landlock.RWDirs("/tmp"),
    )
    

    This is a good example as well. Making and removing directories is forbidden everywhere except in /tmp.

    landlock.MustConfig(landlock.AccessFSSet(ll.AccessFSMakeDir|ll.AccessFSRemoveDir)).BestEffort().RestrictPaths(
      landlock.RWDirs("/tmp"),
    )
    

    This is a good example as well. Making and removing directories is forbidden, except in /tmp and creating them in /home/x/tmp. It's verbose, but the author knows exactly what is being restricted and what is the scope of each exception:

    landlock.MustConfig(landlock.AccessFSSet(ll.AccessFSMakeDir|ll.AccessFSRemoveDir)).BestEffort().RestrictPaths(
      landlock.PathAccess(landlock.AccessFSSet(ll.AccessFSMakeDir|ll.AccessFSRemoveDir), "/tmp"),
      landlock.PathAccess(landlock.AccessFSSet(ll.AccessFSMakeDir), "/home/x/tmp"),
    )
    

    The following example is on the fence:

    • It's a good example as long as the author has checked that "transmogrify" is captured in Landlock ABI V99.
    • It's a bad example if the author has not checked that. If "transmogrify" is not part of Landlock ABI V99, it's very likely to be a programmer mistake, and it would be better to give an error in that case.
    landlock.V99.BestEffort().RestrictPaths(
      landlock.PathAccess(landlock.AccessFSSet(ll.AccessTransmogrifyFile), "/tmp"),
    )
    

    This last case is the one that the bug is about.

  • landlock/syscall: use syscall.AllThreadsSyscall{,6} directly

    landlock/syscall: use syscall.AllThreadsSyscall{,6} directly

    Go 1.16 added the AllThreadsSyscall{,6} functions to the syscall package. The kernel.org/pub/linux/libs/security/libcap/psx package merely wraps these for Go 1.16 and newer (and implements them using cgo for older Go versions).

    This package's go.mod specifies version 1.16. Thus, AllThreadsSyscall{,6} can be used directly from package syscall.

  • Make the handledAccessFS set configurable.

    Make the handledAccessFS set configurable.

    Compare https://github.com/opencontainers/runtime-spec/pull/1111#pullrequestreview-739022043

    from @kailun-qin:

    Removing the abi field from the config and will leverage the explicit access rights specified.

    In this case, I think we need direct access to ruleset.handledAccessFS in go-landlock?

  • Add Landlock V2 support.

    Add Landlock V2 support.

    This adds support for the upcoming Landlock ABI V2.

    In addition to the existing file system access rights, it is now possible to move or link files across different directories with the new 'refer' access right. (For details, see Landlock documentation.)

    Changes in the library:

    • Introduce landlock.V2 config as a way to explicitly ask for this ABI level for users.
    • Introduce syscall.AccessFSRefer constant for the new "refer" right.
    • Add PathOpt.WithRefer() method so that users can ask for "refer" right.
    • The semantics of RWDirs() stay unmodified.

    The 'refer' access right for a path may only be specified handledAccessFS also contains it (i.e. by using the landlock.V2 config).

    Upgrade path:

    Callers using the landlock.V1.BestEffort().RestrictPaths(...) form can switch to use landlock.V2.BestEffort().RestrictPaths(...) with the same parameters instead. This change is compatible with before.

    If you additionally desire to link or move files between directories, make sure that both directories have the "refer" access right, by calling .WithRefer() on their landlock.PathOpt objects. For example:

    err := landlock.V2.RestrictPaths(
        landlock.RWDirs("/src", "/dest").WithRefer(),
    )
    

    NOTE: Requiring the "refer" access right is incompatible with kernels before 5.19. If you want to use Landlock with earlier kernels, do not ask for that permission.

    In particular, this means that using "best effort mode" in combination with the refer right will downgrade to "doing nothing" on kernels below 5.19, as linking and moving files would otherwise not work:

    // Downgrades to no Landlock enforcement on Linux kernels before 5.19.
    err := landlock.V2.BestEffort().RestrictPaths(
        landlock.RWDirs("/src", "/dest").WithRefer(),
    )
    

    Resolves #20.

  • Auto-generated documentation

    Auto-generated documentation

    As for https://github.com/landlock-lsm/rust-landlock/pull/13, I think it would be nice to update the API documentation as soon as it get merged.

    I guess we could use https://code.rocketnine.space/tslocum/godoc-static

  • Add Landlock V2 support.

    Add Landlock V2 support.

    This adds preliminary support for the upcoming Landlock ABI V2.

    In addition to the existing file system access rights, it is now possible to move or link files across different directories with the new 'refer' access right. (For details, see Landlock documentation.)

    Changes in the library:

    • Introduce landlock.V2 config as a way to explicitly ask for this ABI level for users.
    • Introduce syscall.AccessFSRefer constant for the new "refer" right.
    • Add PathOpt.WithRefer() method so that users can ask for "refer" right.
    • The semantics of RWDirs() stay unmodified.

    The 'refer' access right for a path may only be specified handledAccessFS also contains it (i.e. by using the landlock.V2 config).

    Upgrade path:

    Callers using the landlock.V1.BestEffort().RestrictPaths(...) form can switch to use landlock.V2.BestEffort().RestrictPaths(...) with the same parameters instead. This change is compatible with before.

    If you additionally desire to link or move files between directories, make sure that both directories have the "refer" access right, by calling .WithRefer() on their landlock.PathOpt objects. For example:

    err := landlock.V2.RestrictPaths( landlock.RWDirs("/src", "/dest").WithRefer(), )

    NOTE: Requiring the "refer" access right is incompatible with kernels before 5.19. If you want to use Landlock with earlier kernels, do not ask for that permission.

    In particular, using "best effort mode" in combination with the refer right will downgrade to "doing nothing" on kernels below 5.19, as linking and moving files would otherwise not work:

    err := landlock.V2.BestEffort().RestrictPaths( landlock.RWDirs("/src", "/dest").WithRefer(), )

    Resolves #20.

  • Use constants from golang.org/x/sys/unix

    Use constants from golang.org/x/sys/unix

    Use the LANDLOCK_ACCESS_*, LANDLOCK_CREATE_RULESET_VERSION and LANDLOCK_RULE_PATH_BENEATH defined in the golang.org/x/sys/unix package. These are generated from the Linux kernel UAPI headers.

  • Add a Github workflow for executing the tests in Qemu on actual kernels.

    Add a Github workflow for executing the tests in Qemu on actual kernels.

    @l0kod This is running the go-landlock tests as Github actions on qemu... (It's based on the example at https://github.com/florianl/bluebox/blob/main/.github/workflows/example.yml by @florianl)

    Some unsolved logistics problems:

    • It probably uses a few more CPU cycles than regular tests, but it might be worth it.
    • If we use something like this, it would also be good to host the kernel images in a better place than my personal repo. Do you already have a place for such a use case where Landlock-enabled kernels can be fetched?
  • Converting allowedAccess strings into AccessFSSet uint64

    Converting allowedAccess strings into AccessFSSet uint64

    Hi Günther! Could you please add a function which converts allowedAccess strings like { "execute, "writefile....} into AccessFSSet uint64. There is already func (a AccessFSSet) String() string {....}. It would be nice to have the one that does the opposite work.

    According to the last commit in https://github.com/opencontainers/runtime-spec/pull/1111#pullrequestreview-739022043 Landlock access rules in spec file go in string format that is needed to be converted in AccessFSSet one: image

  • Fallback mechanism for non-existent files

    Fallback mechanism for non-existent files

    Does golandlock need a fallback mechanism for non-existent files?

    This occurred to me when landlocking some real-life programs; there are some files, such as ~/.Xauthority or various configuration directories, which probably should show up in some programs' path restrictions, but that should be permitted to be missing. (You don't have a .Xauthority on a Wayland-only system, for instance)

    Rough idea:

    golandlock.V1.RestrictPaths(
        golandlock.RODirs(".Xauthority", ".gtkrc").IfPresent(),  // may be missing
        golandlock.RODirs("/usr", "/lib"),  // always need to exist
    )
    

    Need to think about it.

🔮 ✈️ to integrate OPA Gatekeeper's new ExternalData feature with cosign to determine whether the images are valid by verifying their signatures

cosign-gatekeeper-provider To integrate OPA Gatekeeper's new ExternalData feature with cosign to determine whether the images are valid by verifying i

Dec 8, 2022
An example of using Litestream's live read replication feature.

Litestream Read Replica Example This repository is an example of how to setup and deploy a multi-node SQLite database using Litestream's live read rep

Dec 14, 2022
Linux Controllers for Kubernetes

Tambourine Kubelet replacement with Built in Linux extensions Development Success: Install, Manage, and Observe a new systemd service from Kubernetes.

Jun 2, 2021
Sysctl.conf and other system tunings for Linux

PATCHFILES I came to the idea to create patchfiles, when I saw lots of config files people create. Patchfiles implements various config scripts into o

Sep 4, 2022
Linux Gestures
 Linux Gestures

Swipe Gestures on Linux. https://evuraan.info/Swipe/ Screengrab: https://evuraan.info/evuraan/stuff/Swipe.mp4 Features Swipe uses a novel yet simple c

Nov 27, 2022
A Rancher and Kubernetes optimized immutable Linux distribution based on openSUSE

RancherOS v2 WORK IN PROGRESS RancherOS v2 is an immutable Linux distribution built to run Rancher and it's corresponding Kubernetes distributions RKE

Nov 14, 2022
Truly Minimal Linux Distribution for Containers

Statesman Statesman is a minimal Linux distribution, running from memory, that has just enough functionality to run OCI-compatible containers. Rationa

Nov 12, 2021
🔥 🔥 Open source cloud native security observability platform. Linux, K8s, AWS Fargate and more. 🔥 🔥
🔥 🔥   Open source cloud native security observability platform. Linux, K8s, AWS Fargate and more. 🔥 🔥

CVE-2021-44228 Log4J Vulnerability can be detected at runtime and attack paths can be visualized by ThreatMapper. Live demo of Log4J Vulnerability her

Jan 1, 2023
Linux provisioning scripts + application deployment tools. Suitable for self-hosting and hobby-scale application deployments.

Apollo Linux provisioning scripts + application deployment tools. Suitable for self-hosting and hobby-scale application deployments. Philosophy Linux-

Feb 7, 2022
RancherOS v2 is an immutable Linux distribution built to run Rancher and it's corresponding Kubernetes distributions RKE2 and k3s

RancherOS v2 is an immutable Linux distribution built to run Rancher and it's corresponding Kubernetes distributions RKE2 and k3s. It is built using the cOS-toolkit and based on openSUSE

Dec 27, 2022
Lagoon - Simple Linux package repository mirror

Lagoon - Simple Linux package repository mirror A lagoon is a shallow stretch of water separated from the sea by a reef or sandbank. Lagoon can be use

Aug 17, 2022
Automated Arch Linux (Written in Go)
Automated Arch Linux (Written in Go)

ShobuArch -- Automated Arch Linux Tools (Written in Go) Have you ever wanted to use an IaC (Infrastructure as Code) approach towards automating an Arc

Sep 18, 2022
Isle - A Linux Runtime For Mac

Isle Thanks for your interest in isle, Integrated System Linux Environment. Isle is currently in alpha, but working relatively stably! This page will

Dec 29, 2022
HTTP load testing tool and library. It's over 9000!
HTTP load testing tool and library. It's over 9000!

Vegeta Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate. It can be used both as a

Dec 30, 2022
Go library to create resilient feedback loop/control controllers.

Gontroller A Go library to create feedback loop/control controllers, or in other words... a Go library to create controllers without Kubernetes resour

Jan 1, 2023
Orchestra is a library to manage long running go processes.

Orchestra Orchestra is a library to manage long running go processes. At the heart of the library is an interface called Player // Player is a long ru

Oct 21, 2022
Testcontainers is a Golang library that providing a friendly API to run Docker container. It is designed to create runtime environment to use during your automatic tests.

When I was working on a Zipkin PR I discovered a nice Java library called Testcontainers. It provides an easy and clean API over the go docker sdk to

Jan 7, 2023
Automated configuration documentation library for Go Projects.

Cato Cato is an automated documentation generation library for Go Projects. Through the use of custom tags for struct fields, Cato can extract informa

Aug 28, 2020
Gohalt 👮‍♀🛑: Fast; Simple; Powerful; Go Throttler library
Gohalt 👮‍♀🛑: Fast; Simple; Powerful; Go Throttler library

Gohalt ??‍♀ ?? : Fast; Simple; Powerful; Go Throttler library go get -u github.com/1pkg/gohalt Introduction Gohalt is simple and convenient yet powerf

Nov 27, 2022