· 5 min read ·

The Kernel Problem Behind LittleSnitch's Linux Port

Source: hackernews

Objective Development has shipped LittleSnitch for Linux, and the HackerNews thread that followed hit over 1,300 points. That reception makes sense. LittleSnitch has been the definitive application-level firewall on macOS for over two decades, and the Linux desktop has never had a fully polished equivalent. But the interesting story here isn’t the product launch; it’s the technical gap the port has to bridge.

Per-process network filtering on macOS is a solved problem from the API perspective. Apple introduced the Network Extension framework in macOS 10.15 Catalina as the sanctioned replacement for kernel extensions. You implement a NEFilterDataProvider, register it as a system extension, and the kernel calls into your code before any network flow is established. Critically, you receive the originating process’s audit token with every intercepted connection. The process identity is right there, authoritative, provided by the kernel itself. Building LittleSnitch on macOS is hard engineering, but the API tells you exactly what you need to know.

Linux has no equivalent of this.

How the Linux Kernel Thinks About Network Policy

Netfilter, which underpins iptables and nftables, was designed around packets and addresses, not processes. Its matching capabilities are extensive: you can match on source IP, destination port, connection state, protocol flags, and more. What it does not natively give you is the process that sent the packet. The closest approximation in nftables is meta skuid and meta skgid, which match against the UID or GID of the socket owner. That lets you write rules that say “deny traffic from user id 1001” but not “deny traffic from /usr/bin/curl”.

The traditional workaround has been to use NFQUEUE. You direct packets to a userspace queue, where your daemon receives them, then races to identify the owning process by correlating the socket’s inode number. The path is: parse /proc/net/tcp (or tcp6, udp, udp6) to find the entry with the matching address and port, extract its inode number, then walk every /proc/[pid]/fd/ symlink target looking for a socket inode that matches. If you find it before the process exits or the connection changes state, you have your process identity.

This is what OpenSnitch does, and it works well enough in practice. OpenSnitch started as a Python project inspired explicitly by LittleSnitch, then received a complete rewrite in Go. The Go daemon intercepts connections via NFQUEUE, does the /proc walk, applies a rules engine, and surfaces a GTK interface for interactive decisions. For casual use on a desktop system with sane process lifetimes, the race condition is rarely triggered. Under load, or with short-lived processes, you will occasionally misidentify or fail to identify the owner.

The race has a name: TOCTOU, time-of-check to time-of-use. By the time you check /proc, the state you needed may already be gone.

eBPF Changes the Equation

The more correct solution, and almost certainly what LittleSnitch for Linux uses, is eBPF. The kernel introduced cgroup-socket eBPF programs (BPF_PROG_TYPE_CGROUP_SOCK and BPF_PROG_TYPE_CGROUP_SOCK_ADDR) that let you attach a BPF program to a cgroup and intercept connect() and bind() calls before they complete. You are executing inside the syscall context, which means you have direct access to the current task’s credentials and process metadata through the bpf_get_current_pid_tgid() and bpf_get_current_comm() helpers. No /proc race, no correlation step.

The challenge shifts to a different layer: blocking interactively. An eBPF program makes its decision synchronously during the syscall. You can return an error code to deny the connection outright, but you cannot pause the syscall while your GUI asks the user a question. The practical architecture is a BPF ring buffer where the kernel-side program logs connection attempts and signals a userspace daemon, which then enforces a default policy (allow or deny) until the user responds, then applies the new rule retroactively for subsequent connections to the same destination. This is conceptually similar to how OpenSnitch handles it: the first packet or connection attempt triggers a prompt, and a temporary policy applies while the decision is pending.

Portmaster, another commercial application firewall for Linux from Austrian company Safing, takes a similar approach: a Go-based daemon paired with kernel-level interception, layered with DNS-based filtering for blocking domains before a TCP connection is even attempted. The DNS interception layer is architecturally clever because you can block entire categories of traffic before the kernel’s packet path is involved at all.

What Polish Actually Means Here

The technical mechanisms for per-process network filtering on Linux have been available for years. OpenSnitch works. Portmaster works. The question is whether “works” is sufficient, and this is where LittleSnitch’s track record matters.

The macOS version of LittleSnitch is known not just for its alert dialogs but for its network monitor: a live, streaming view of every connection grouped by process, with traffic volume, remote hostname resolution, geolocation, and historical data. It makes the invisible visible in a way that is genuinely useful for understanding what your machine is doing, not just for blocking things. The product has been in active development since 2002 and has gone through multiple complete rewrites as macOS kernel APIs changed.

Open source alternatives have historically struggled with reliability in edge cases and with UI quality. OpenSnitch’s interface is functional but dated. Portmaster’s UI is more modern but the product has had a rocky history with kernel compatibility across distributions. Neither has the resources of a company that has been selling a premium security product for 20 years.

There is also the question of distribution. Linux’s kernel ABI is stable for userspace but not for kernel modules, and eBPF programs compiled against one kernel version may not load on another due to BTF (BPF Type Format) changes or verifier strictness differences. A commercial product with a support channel and an update cadence is a meaningfully different proposition for users who are not prepared to debug eBPF verifier errors.

The Broader Moment

Linux desktop usage has grown meaningfully over the past few years. The Steam Deck normalized Linux as a gaming platform. Developer workstations running Fedora or Ubuntu are no longer unusual in environments that were previously all-macOS. That expanding base creates a viable market for commercial desktop Linux software, which has been nearly nonexistent outside of JetBrains IDEs and a handful of others.

LittleSnitch for Linux is significant in the same way that 1Password’s native Linux client was significant when it launched: it signals that a company with a real product believes the Linux desktop user base is worth building for. The technical problem is harder than on macOS, the distribution story is more fragmented, and the user expectations are calibrated to free software. Shipping anyway is a statement about where the platform is heading.

For users who have been running OpenSnitch and tolerating its rough edges, there is now a commercial alternative worth evaluating. For everyone else, the existence of LittleSnitch for Linux is a useful reminder that the Linux kernel, for all its sophistication, still lacks a first-class API for the basic question: which process is making this network connection, and should I allow it.

Was this interesting?