Quick Start
A five-minute tour of Landlock-Sharp: check kernel support, build a ruleset, allow one directory, enforce the sandbox, and watch unauthorised accesses fail.
If you haven't already, install the NuGet package:
dotnet add package Landlock
Landlock's full kernel-side semantics are documented on the landlock.io site and the landlock(7) man page. This guide focuses on the C# binding — refer back to those for the authoritative behaviour.
1. Check kernel support
Landlock is Linux-only and needs kernel 5.13+. Always branch on IsSupported() so your code stays portable.
using Sandbox;
if (!Landlock.IsSupported())
{
Console.WriteLine("Landlock unavailable — running without sandbox.");
return;
}
IsSupported() returns true only on Linux x86-64 with a kernel that exposes Landlock. See ABI versions if you also need to detect which features are available.
2. Create a ruleset
A ruleset declares which access rights the sandbox will handle. Anything you don't list is left untouched by Landlock; anything you do list is denied by default and must be re-granted by a rule.
var sandbox = Landlock.CreateRuleset(Landlock.FileSystem.CORE);
FileSystem.CORE is a convenience that expands to every filesystem right available on the current kernel. To restrict only specific operations, pass them individually:
var sandbox = Landlock.CreateRuleset(
Landlock.FileSystem.READ_FILE,
Landlock.FileSystem.WRITE_FILE,
Landlock.FileSystem.EXECUTE);
For the meaning of each right, see the "Filesystem flags" section of landlock(7).
3. Allow a directory
Use AddPathBeneathRule to grant specific rights to a directory tree. The path must be a directory; rules cascade to everything beneath it.
sandbox.AddPathBeneathRule(
"/var/lib/myapp/data",
Landlock.FileSystem.READ_FILE,
Landlock.FileSystem.READ_DIR);
The rights you grant per rule must be a subset of the rights you declared when creating the ruleset — you cannot grant WRITE_FILE here unless WRITE_FILE (or CORE) appeared in CreateRuleset.
4. Enforce the sandbox
Enforce() is the point of no return. Once it returns, the calling thread (and every child thread and process) is permanently bound to the ruleset.
sandbox.Enforce();
Internally the call:
- Sets
PR_SET_NO_NEW_PRIVSso the process cannot gain privileges via setuid binaries. - Invokes
landlock_restrict_selfto bind the ruleset to the current thread. - Closes the ruleset file descriptor.
The restriction is irrevocable — there is no way to widen the sandbox afterwards. The only way to "escape" is to fork an entirely new process from a less-restricted parent.
One-way door
You cannot re-call CreateRuleset and "replace" the sandbox. New rulesets only ever layer further restrictions on top of the existing one. See the Enforcing guide for thread vs. process semantics.
5. See it in action
With the sandbox in place, anything outside the allowed path fails with EACCES.
// OK — inside the allowed tree
string contents = File.ReadAllText("/var/lib/myapp/data/hello.txt");
// Denied — outside the allowed tree
try
{
File.ReadAllText("/etc/passwd");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Blocked by Landlock");
}
// Denied — write was never allowed, even inside the tree
try
{
File.WriteAllText("/var/lib/myapp/data/new.txt", "nope");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Blocked: write was never granted");
}
6. Put it all together
using Sandbox;
if (!Landlock.IsSupported())
{
Console.WriteLine("Landlock unavailable.");
return;
}
var sandbox = Landlock.CreateRuleset(Landlock.FileSystem.CORE);
sandbox.AddPathBeneathRule(
"/var/lib/myapp/data",
Landlock.FileSystem.READ_FILE,
Landlock.FileSystem.READ_DIR);
sandbox.Enforce();
Console.WriteLine(File.ReadAllText("/var/lib/myapp/data/hello.txt"));
try { File.ReadAllText("/etc/passwd"); }
catch (UnauthorizedAccessException) { Console.WriteLine("blocked /etc/passwd"); }
hello world
blocked /etc/passwd
Going further
Landlock can do more than filesystem rules:
- TCP ports — bind/connect allow-lists via
AddPortRule(kernel 6.7+). See Network rules. - IPC scopes — block abstract Unix sockets and inbound signals (kernel 6.12+). See Scopes.
- Deny logging — opt into audit logs of blocked accesses (kernel 6.13+). See Logging.