Landlock-Sharp

Network rules

Landlock can restrict which TCP ports a sandboxed process is allowed to bind to (BIND_TCP) and connect to (CONNECT_TCP). The model mirrors filesystem rules: declare which kinds of network access the sandbox handles, then grant exemptions for individual ports.

Network rules require Landlock ABI 4 (Linux kernel 6.7+). For the canonical reference see the "Network flags" section of landlock(7) and the kernel doc on network restrictions.

TCP only

Landlock's network filter only covers TCP bind(2) and connect(2). UDP, raw sockets, and other protocols are not affected. Use seccomp or a separate network namespace to restrict those.


Building a network-aware ruleset

There are two ways to enable the network filter:

// Network only
var sandbox = Landlock.CreateRuleset(
    Landlock.Network.BIND_TCP,
    Landlock.Network.CONNECT_TCP);

// Filesystem + network together
var sandbox = Landlock.CreateRuleset(
    fileSystem: new[] { Landlock.FileSystem.CORE },
    network:    new[] { Landlock.Network.BIND_TCP, Landlock.Network.CONNECT_TCP });

If BIND_TCP is handled but no AddPortRule(port, BIND_TCP) is added, every bind to a TCP socket is denied. Same story for CONNECT_TCP.


Allowing a specific port

AddPortRule(port, ...allowedActions) re-grants TCP access for one port number. Bind and connect are independent — pass both flags if you want both.

sandbox
    .AddPortRule(443,  Landlock.Network.CONNECT_TCP)        // outbound HTTPS
    .AddPortRule(80,   Landlock.Network.CONNECT_TCP)        // outbound HTTP
    .AddPortRule(8080, Landlock.Network.BIND_TCP);          // local listener

The port number is the port itself, not a range. Add one rule per port. There is no wildcard; a process that wants to listen on any ephemeral port has to list every port it might use.


Worked example — outbound HTTPS only

A typical service that pulls from https://api.example.com but should never listen on any port:

if (Landlock.IsSupported() && Landlock.GetAbiVersion() >= 4)
{
    Landlock.CreateRuleset(
        fileSystem: new[] { Landlock.FileSystem.CORE },
        network:    new[] { Landlock.Network.BIND_TCP, Landlock.Network.CONNECT_TCP })

        // Filesystem
        .AddPathBeneathRule("/etc/ssl",
            Landlock.FileSystem.READ_FILE,
            Landlock.FileSystem.READ_DIR)

        // Network — only outbound 443 and 80 (HTTPS + HTTP for redirects)
        .AddPortRule(443, Landlock.Network.CONNECT_TCP)
        .AddPortRule(80,  Landlock.Network.CONNECT_TCP)

        // No BIND_TCP rules → process cannot listen on any TCP port

        .Enforce();
}

After Enforce(), bind(2) on every TCP port returns EACCES, and connect(2) to anything other than ports 80 and 443 also returns EACCES. UDP, on the other hand, is unaffected.


Falling back when ABI 4 is unavailable

On a kernel older than 6.7, Landlock.Network.* flags are silently filtered out — see ABI versions. If your security model requires network sandboxing, gate on the ABI explicitly:

if (!Landlock.IsSupported())
    throw new PlatformNotSupportedException("Landlock unavailable");

if (Landlock.GetAbiVersion() < 4)
    throw new PlatformNotSupportedException(
        "This service requires Landlock ABI ≥ 4 (Linux 6.7+) for TCP sandboxing.");

What about the local address?

Landlock filters by port number only — there is no rule for "bind only to 127.0.0.1" or "connect only to 10.0.0.5". If you need IP-level filtering, layer Landlock with a packet filter (nftables, eBPF) or run the process in its own network namespace.

This is documented as a deliberate design choice in the upstream kernel doc.


Cross-reference

Referenced by

© 2026 Landlock-Sharp. All rights reserved.