sst (Simple sandboxer tool) is a small CLI tool that will run some other
program while applying sandboxing, using Linux Landlock API. It requires no
root privileges to run and it is trying to be "as little fuss" as possible; a
single sst.c-file implementation that only demands a C compiler and some Linux
kernel headers to compile.
Example:
# What this does:
#
# 1) No outgoing TCP connections
# 2) Incoming TCP connections only allowed on port 5000
# 3) No filesystem write access
#
$ sst ENABLE_NETWORK_SANDBOXING ENABLE_FILESYSTEM_SANDBOXING ALLOW_INCOMING_TCP_PORT:5000 PATH_BENEATH_EXEC:/ -- java -jar some-program.jarSee CHEATSHEET.md in this repository for a quick reference.
If you have a Linux machine with development tools, then make hopefully
suffices. It will compile sst to the current directory.
$ make
$ ./sst <options here>sst.c is, on purpose, a single file with no dependencies other than Kernel headers, so you could also try:
$ gcc -Wall -O2 sst.c -o sst
$ ./sst <options here>The command line tool is called sst (for "Simple Sandboxer Tool"). By default, it will
not restrict anything.
$ sst <options...> -- <command> <arg1> <arg2> ... <argN>Note: The -- is mandatory. You specify on the command line, before the --, how do you want to sandbox.
The ordering of the options before -- does not matter.
You can nest sst calls, e.g. one only does filesystem sandboxing, and another
does networking sandboxing. The permissions can only be tightened; an sst
cannot increase privileges under Landlock.
Open a shell that can't do (TCP) networking:
$ sst ENABLE_NETWORK_SANDBOXING -- bash
$ nc -v example.org 80
Warning: inverse host lookup failed for 104.18.3.24:
Warning: inverse host lookup failed for 104.18.2.24:
example.org [104.18.3.24] 80 (http) : Permission denied(note: some programs may know HTTP3/QUIC which uses UDP instead, as mentioned
elsewhere in this README.md. They would still work.).
Run a program with a read-only root and some directories with write permission:
$ sst ENABLE_FILESYSTEM_SANDBOXING PATH_BENEATH_EXEC:/ PATH_BENEATH_WRITE:/workspace/my_program -- my_program --option 123This is often my starting point when I design a sandboxer for some program.
This stops networking and stops filesystem write access entirely. The root is set to PATH_BENEATH_EXEC which lets the opened bash shell to execute and run commands; but none of these commands can leave a mark on the filesystem.
$ sst ENABLE_FILESYSTEM_SANDBOXING ENABLE_NETWORK_SANDBOXING PATH_BENEATH_EXEC:/ -- bashRemove ENABLE_NETWORK_SANDBOXING if you would like to allow Internet (or fine-tune that with the networing-related sandboxing options).
Add PATH_BENEATH_WRITE:<directory> as needed to allow selectively write access to some parts of the filesystem.
You might be interested in CHEATSHEET.md in this repository for an uncluttered "cheat sheet" version of this information.
Forbidden operations generally result in an EPERM error given to the caller; refer to Landlock documentation for details.
To use any options below, you must specify, somewhere, on the command line,
before the -- separator, the trigger option word ENABLE_FILESYSTEM_SANDBOXING. This
enables the use of the options below, and it also restricts all filesystem access except
the ones specified.
FILE_READ:<filepath>: allow reading from a specific file. The file has to be an actual file, not a directory. Executing the file is not allowed.FILE_EXEC:<filepath>: same asFILE_READbut adds execution privileges.FILE_WRITE:<filepath>: same asFILE_READbut adds write privileges (not execution privileges).FILE_EXEC_WRITE:<filepath>: combinedFILE_EXECandFILE_WRITE(you can also separately specify them).FILE_WRITE_EXEC:<filepath>: alias forFILE_EXEC_WRITE.PATH_BENEATH_READ:<dir>: allow reading (not executing) everything under<dir>. The path must refer to a directory.PATH_BENEATH_EXEC:<dir>: same asPATH_BENEATH_READbut also gives execution privileges.PATH_BENEATH_WRITE:<dir>: same asPATH_BENEATH_READbut also gives write privileges.PATH_BENEATH_EXEC_WRITE:<dir>: combinedPATH_BENEATH_EXECandPATH_BENEATH_WRITE(you can also separately specify them).PATH_BENEATH_WRITE_EXEC:<dir>: alias forPATH_BENEATH_EXEC_WRITE.
To use any options below, you must specify, somewhere, on the command line,
before the -- separator, the trigger option word ENABLE_NETWORK_SANDBOXING. This
enables the other options, and it also restricts all TCP networking except the ones specified.
Note: this does not restrict other forms of network communications (e.g. UDP).
QUIC for example is UDP, so this tool cannot block it. (try curl https://google.com/ while sandboxing networking;
a modern enough curl will use UDP!).
As of writing of this: ABI 8 of Landlock API looks like UDP support is coming.
ALLOW_INCOMING_TCP_PORT:<port>: allow incoming connections to the given port. 0 can be specified, readbind()documentation on what does binding to port 0 mean exactly.ALLOW_OUTGOING_TCP_PORT:<port>: allow outgoing connections to the given port.
Landlock at least in its current form is not a foolproof sandboxing system;
look for other technologies (e.g. bubblewrap, firejail on Linux; App Sandboxing
on MacOS; and use actual firewalls to stop networking) if you are working with
code you expect to be actively adversarial that makes an effort to work around
the sandbox. I consider sst to be the "quick & lazy sandboxing method that I
can just slap on things, bam done" route, and I escalate to more thorough
methods depending on what am I dealing with.
Here are some use cases I've used this for so far:
- stop unwanted telemetry (my original motivation; in late 2025 I touched a JavaScript framework with a particularly facetious attitude about telemetry and got annoyed :-)
- sandbox my own applications. I especially do this with software that I have to deal with and that is exposed to the Internet; e.g. Minecraft servers, <insert your latest 21st century Web/JavaScript thingamajig framework>-based applications.
I consider this to be, at best, one layer of defense, and I think at the moment this only is effective because most software I target with this are not expecting to be sandboxed (i.e. software by unscrupulous tech companies filled with telemetry or some other user-hostile code), or they are not hostile programs to begin with.
Future improvements to sst might be about adding features to help with
observability rather than sandboxing. Landlock itself has some amount of
"audit features" but I have not studied them as of writing this section right
now, or if it makes sense to put such features into sst specifically.
The sst filesystem restrictions restrict a lot of operations; the most permissive
setting you can set (while also having filesystem sandboxing on at all) is this combo:
static const __u32 EXEC_WRITE_FILE_ACCESS_DIR =
LANDLOCK_ACCESS_FS_EXECUTE |
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_TRUNCATE |
LANDLOCK_ACCESS_FS_IOCTL_DEV;Landlock itself is a bit more fine-grained (and likely will be more so in the
future); so this is a missing feature in sst.
The current set of options you can set reflect my own use of this tool, but I will expand and improve over time if I find a good design for it.
Non-TCP networking is not blocked at all (e.g. UDP, used in HTTP3/QUIC). I will likely add it as soon as my system gets updated enough to have it in the installed Kernel headers and I am able to test it conveniently. One of the use cases of this sandboxing is to stop unwanted telemetry, but if the telemetry uses UDP it kind of defeats the point of sandboxing.
Networking uses the ALLOW verb, e.g. ALLOW_INCOMING_TCP_PORT.
Filesystem settings however use no such verbiage. Inconsistent.
Also my names are long and capitalized and they look like they are shouting.
I might at some point add better designed names as aliases; the current names are what they are because I accidentally got used to them.
GPL3 only.