- https://github.com/containers/bubblewrap
- https://codeberg.org/dannyfritz/dotfiles/src/commit/38343008...
bwrap --unshare-pid --dev-bind / / --tmpfs /home --bind "$(pwd)" "$(pwd)" bash
it seems to work fairly well? But I just started playing with bwrap this weekend. I do wish bwrap could be told "put the program in this pre-prepared network namespace" because accessing unsecured local dev servers could also be an issue.My questions are:
- How does this help with malware? I want to craft an environment where any program trying to read f.ex. anything inside ~/.ssh is automatically denied. I don't want a malicious build script to exfiltrate all my sensitive data!
- It seems that this software is well-positioned for us to write application launchers with, is that true? If so, well, I like the idea but it seems too manual.
Maybe I'm looking at the wrong thing. I strongly prefer deny-by-default in an invisible manner i.e. my system to refuse most requests to access this or that. Not opting in to it. Bad actors will not graciously limit their own program with Landlock. They'll try to get anything before I can even blink my eyes.
I feel I'm missing crucially important context. Can somebody help?
It is not a suitable technology for sandboxing a program that wasn't designed to be sandboxed in this way. For that, you need one of the other technologies listed in the article.
It is only useful for guarding your own process against someone using malicious inputs to get your process to do something you don’t intend. It is not a guard against programs written by malicious actors (malware), there exist other mechanisms to guard against malware.
That will inevitably lag behind what the kernel supports, but more importantly I don't foresee many container image packagers, Helm recipe maintainers and other YAML wranglers getting into the business of maintaining a Landlock sandbox policy.
It makes sense for an application to use Landlock directly to sandbox some parser or other sensitive component. But if the CRI just blocks the syscalls by default, no infra person is going to take on the maintainance of their own sandbox policy for every app. The app will just see ENOSYS and not be sandboxed.
I might be missing the whole idea here, but I really don't see why we need some custom layer in the middle instead of having container runtimes let the security syscalls through?
> A official c library doesn’t exist yet unfortunately, but there’s several out there you can try.
> Landlock is a Linux Security Module (LSM) available since Linux 5.13
Since when is not a C API the first and foremost interface for developers when it comes to Linux kernel stuff?
> A official c library doesn’t exist yet unfortunately, but there’s several out there you can try.
Also, it looks like there is more than zero support for C programs calling Landlock APIs. Even without a 3rd-party library you're not just calling syscall() with a magic number:
https://docs.kernel.org/userspace-api/landlock.html
https://github.com/torvalds/linux/blob/6bda50f4/include/uapi...
https://github.com/torvalds/linux/blob/6bda50f4/include/linu...
For C, unofficial support apparently sufficed for now.
Meaning glibc or musl or your favorite C library probably doesn't have this yet, but since the system calls are well defined you can use A C library (create your own header file using the _syscallN macro for example).
Since the kernel developers don't make userland software?
The Linux kernel has a relatively simple example on how to use the syscall even if you can't find a library to deal with it for you: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
There is no "liblandlock" or whatever, though there totally could be. The only reason Rust, Go, and Haskell have an easy-to-use API for this syscall is because someone bothered to implement a wrapper and publish it to the usual package managers. Whatever procedure distros use to add new libraries could just as easily be used to push a landlock library/header to use it in C.
https://github.com/torvalds/linux/blob/master/include/uapi/l...
you can add simple one-line wrappers if you don't like using syscall() function.
Can sysadmins disable access to Landlock syscalls via seccomp? Not that I can see why they'd want to, just wondering how this is layered.
I suppose the problem might be if the system has been set up at some earlier point in time to whitelist a set of syscalls for a process and, as Landlock is newer, its syscall numbers won't be included. A program that has been updated to use Landlock while a seccomp policy that predates Landlock is applied would presumably be terminated with SIGSYS due to this?
How can a program determine if Landlock is present without just trying the syscalls and seeing if they work?
I also don't think doing so is extraordinarily useful.
If you allow something in landlock, it's still subject to traditional DAC and other restrictions because its a stackable LSM. It can only restrict existing access, not allow new accesses.
But also: even within a container (which isn't itself a sandbox) or a VM, you still have concentric circles of trust and/or privilege. If you're installing arbitrary dependencies from the Internet, for example, you probably want a basic initial defense of preventing those dependencies from exfiltrating your secrets at build time.
Linux holds on to a negligible share of the overall desktop market OS, but it is marginally more popular among tech savvy people, which have plenty of disposable income, meaning the platform has steadily growing interest for malware authors and distributors despite its relatively low usage.
It could even be paired with a chroot to make a container runtime. It's more like a building block for process restrictions
VM's can be security wrappers, but if you expose all of $HOME to a VM, then there really isn't much security happening, in terms of your data.
This lets developers of applications harden themselves, it doesn't require the end-user to do anything(like put it in a VM).
Defense in depth. Lock your valuables inside a safe, inside of your locked house. Why lock them in a safe when your house is already locked? Because if someone breaks into your house, you want additional defense "just in case". So just in case I wrote some shitty code and my server got hacked, lock the valuables in a safe anyway so that thief can't steal the expensive silverware (prod credentials).
Deno is a JS runtime that often runs, at my behest, code that I did not myself write and haven't vetted. At run time, I can invoke Deno with --allow-read=$PWD and know that Deno will prevent all that untrusted JS from reading any files outside the current directory.
If Deno itself is compromised then yeah, that won't work. But that's a smaller attack surface than all my NPM packages.
Just one example of how something like this helps in practise.
So the restrictions are permanent for the life of the program. Even root can't undo them.
I'd love to see a comparison of landlock to restricted containers.
Fun fact: because landlock is unprivleged, you can even use it inside containers; or to build an unprivileged container runtime :)
https://github.com/Zouuup/landrun
All you gotta do is apply a policy and do a fork() exec(). There is also support in firejail.
Also, it's very easy to write your own LandLock policy in the programming language of your choice and wrap whatever program you like rather than downloading stuff from Github. Here's another example in Go:
package main
import (
"fmt"
"github.com/landlock-lsm/go-landlock/landlock"
"log"
"os"
"os/exec"
)
func main() {
// Define the LandLock policy
err := landlock.V1.RestrictPaths(...)
// Execute FireFox
cmd := exec.Command("/usr/bin/firefox")
}It allows running non/semi-trusted workloads with isolation. Pretty useful to onboard applications into a proper scheduler with all bells and whistles without having to containerise, but still with decent levels of isolation between them.
package main
import (
"flag"
"fmt"
"github.com/landlock-lsm/go-landlock/landlock"
"io/ioutil"
"log"
"os"
)
// simple program that demonstrates how landlock works in Go on Linux systems.
// Requires 5.13 or newer kernel and .config should look something like this:
// CONFIG_SECURITY_LANDLOCK=y
// CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo"
func main() {
var help = flag.Bool("help", false, "landlock-example -f /path/to/file.txt")
var file = flag.String("f", "", "the file path to read")
flag.Parse()
if *help || len(os.Args) == 1 {
flag.PrintDefaults()
return
}
// allow the program to read files in /home/user/tmp
err := landlock.V1.RestrictPaths(landlock.RODirs("/home/user/tmp"))
if err != nil {
log.Fatal(err)
}
// attempt to read a file
bytes, err := ioutil.ReadFile(*file)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(bytes))
}It's becoming increasingly usable as a wrapper for untrusted applications as well.