landlock-lsm / go-landlock Goto Github PK
View Code? Open in Web Editor NEWA Go library for the Linux Landlock sandboxing feature
License: MIT License
A Go library for the Linux Landlock sandboxing feature
License: MIT License
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.
Deal with the case where the kernel supports a higher Landlock version than known to go-landlock.
When using non-file access rights on files (rather than directories),
it is possible to get go-landlock to return a BUG(go-landlock)
error.
This is unintended and should be fixed.
Reported by https://www.reddit.com/user/ikmckenz/ on https://www.reddit.com/r/linuxquestions/comments/17qg6fj/give_landlock_access_to_devnull_properly/
The mapping of Goroutines to system threads is managed by the Go runtime and even at the start of main() you don't get the guarantee that there is only one OS thread active. (In fact, even the GC uses Goroutines, so it's clear to see this is difficult to guarantee.)
The same problem has already been discussed in the context of seccomp(2)
, which has the same semantics: golang/go#3405 @AndrewGMorgan is pointing to syscall.AllThreadsSyscall()
as a way to apply a syscall to all Go-managed OS threads at once.
Improve documentation, explain underlying concepts a bit better.
Restrict
is passing the wrong flags to Landlock - the rwDirs and rwFiles should also be readable, according to their name.
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:
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)
Use this with more real-world scenarios before making it v1.
All feedback appreciated, please feel free to post of this bug if you've tried it. :)
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 opencontainers/runtime-spec#1111 (review)
Landlock access rules in spec file go in string format that is needed to be converted in AccessFSSet one:
Compare https://wiki.gnoack.org/LandlockIoctlSupport
Work underway on the "ioctl" branch.
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.
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:
landlock.V99.BestEffort().RestrictPaths(
landlock.PathAccess(landlock.AccessFSSet(ll.AccessTransmogrifyFile), "/tmp"),
)
This last case is the one that the bug is about.
I should do some exhaustive testing before tagging v1, to make sure golandlock works as expected.
Landlock V2 is scheduled to appear in kernel 5.19 (mail with links to the patches) - let's get support into Go-Landlock for it.
Could you please pick (a) license(s) to make it open-source? As free as possible would be great. 😉
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
Compare opencontainers/runtime-spec#1111 (review)
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?
Trying to sandbox a Go wrapper that calls ffprobe, code looks something like this:
package main
import (
"fmt"
"github.com/landlock-lsm/go-landlock/landlock"
"os"
"os/exec"
)
func main() {
ffprobe, _ := exec.LookPath("ffprobe")
file := "/path/to/file"
err := landlock.V3.BestEffort().RestrictPaths(
landlock.ROFiles("/dev/null"),
landlock.ROFiles(ffprobe),
landlock.ROFiles(file),
)
cmd := exec.Command(ffprobe,"-print_format", "json","-show_streams", file)
out, err := cmd.Output()
if err != nil {
fmt.Fprintf(os.Stderr, "ffprobe encountered an error.\nError JSON:\n%s\nError: %s", string(out), err.Error())
}
}
This always returns
/usr/bin/ffprobe
ffprobe encountered an error.
Error JSON:
Error: fork/exec /usr/bin/ffprobe: permission denied
Using strace, I see it fails with epoll_ctl(4, EPOLL_CTL_ADD, 3, {events=EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, data={u32=1363365560, u64=139699469635256}}) = -1 EPERM (Operation not permitted)
Is this a limitation from Landlock? Or is this something from the Go implementation?
Let's move golandlock into github.com/landlock-lsm, as offered by @l0kod.
Constraints:
Let's move it to: github.com/landlock-lsm/go-landlock/landlock
The name is a little bit repetitive, but:
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:
In the old git repo: Add a big warning pointing to the new location.
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.
Make it hard to construct unsupported AccessFSSets so that users can't be bitten by constructing a wrong set.
As for landlock-lsm/rust-landlock#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
Users should be able to group their own libraries of commonly used rules that are used together
idea:
package llopts
import ...
var SharedLibraries = landlock.GroupRules(
landlock.RODirs("/usr/lib", "/lib"),
landlock.RWDirs(os.Getenv("TMPDIR")),
)
// Looking a bit into the future here with Network rules...
var DNSClient = landlock.GroupRules(
landlock.ROFiles("/etc/hosts"),
landlock.DialTCP(53),
landlock.DialUDP(53),
)
(This is just an example -- the details of these rules are not really fleshed out)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.