Per-Application Firewalling on Linux Is Harder Than It Looks, and Little Snitch Knows It
Source: lobsters
Objective Development’s Little Snitch for Linux announcement is the kind of thing that looks straightforward on the surface. Take a mature macOS product, port it to Linux, ship it. In practice, building a per-application outbound connection firewall on Linux exposes a set of kernel-level challenges that explain why the Linux ecosystem has struggled with this problem for years despite having several earnest attempts.
Little Snitch on macOS works by intercepting outbound connections before they leave your machine, identifying the responsible process by its code-signed application bundle, presenting a prompt, and recording your decision as a persistent rule. The result is that you always know what is talking to what. Every application, every background process, every update mechanism has to ask permission, and that permission is tied to the binary that asked for it. It has been a staple of privacy-conscious macOS setups since the mid-2000s, and nothing on Linux has matched it for polish or reliability.
The reason for that gap is not lack of interest. It is the kernel API problem.
What macOS Gets for Free
Apple’s Network Extension framework, specifically NEFilterDataProvider, gives developers a clean, stable interface for intercepting network flows before they are established. The framework handles the hardest part: mapping each connection attempt to an application identity, including the bundle identifier and code signature. The developer receives a structured object with the originating process, the destination endpoint, and a callback to issue a verdict. Apple manages the kernel plumbing.
This abstraction is not trivial. Behind it, macOS is doing socket-level filtering through a kernel extension mechanism, then doing the process association through a combination of the process table and Mach port-based security. The framework bundles all of that into an API that a userspace application can consume without touching the kernel directly.
Linux has no equivalent. What it has instead is a collection of lower-level mechanisms that can be composed into something similar, but only if you do that composition yourself.
The Linux Toolkit
The two main approaches on Linux are NFQUEUE and eBPF, and they represent different points in the complexity-power tradeoff.
NFQUEUE is a Netfilter target that diverts packets from the kernel to a userspace process via a Netlink socket. Your daemon calls libnetfilter_queue, registers a callback, and receives packets that the kernel is holding in a queue awaiting a verdict. Your process examines the packet, decides allow or drop, and sends the verdict back. The kernel then proceeds accordingly.
The problem is that NFQUEUE gives you packets, not connections, and it gives you no process identity at all. You receive an IP packet with source and destination addresses and ports. Figuring out which process generated that packet requires a separate lookup. The standard approach is to read /proc/net/tcp (or its IPv6 equivalent), find the socket with the matching local port, extract its inode number, then scan /proc/[pid]/fd/ across every running process to find which one holds that inode. This works, but it is a race condition waiting to happen: the process might have died between when the packet was queued and when you complete the scan. Under high connection rates, it becomes a significant performance bottleneck.
This is the approach OpenSnitch uses. OpenSnitch is a serious project, written in Go with a gRPC daemon and a Python/Qt GUI, and it handles the common cases well. But the underlying proc-scanning approach has visible latency under load, and the UI, while functional, does not approach the refinement of the macOS original.
eBPF offers a better foundation. Since Linux 5.7, the kernel supports LSM eBPF hooks, which let you attach eBPF programs to Linux Security Module hook points including socket_connect and socket_bind. At those hook points, the eBPF program runs in kernel context and has direct access to the task_struct of the calling process. You can extract the PID, the UID, the cgroup, and store that mapping in a BPF map that your userspace daemon reads. No proc scanning, no race conditions.
Portmaster by Safing uses a more modern kernel integration along these lines. It also adds DNS-layer filtering, which catches connections before they even reach the socket layer, since most applications resolve a hostname before connecting. Intercepting at the DNS level lets you present the rule prompt before the TCP handshake happens, which is a cleaner user experience than letting the kernel queue a packet while your UI loads.
The Process Identity Problem
On macOS, process identity is anchored to code signatures. Little Snitch does not just track a process by its path or its PID; it verifies the cryptographic signature of the binary, which means that if an application is replaced by a malicious version, the firewall rule for the original no longer applies. This is a meaningful security property, not just a cosmetic feature.
Linux has no universal equivalent. Most distributions ship unsigned binaries, or sign them with distribution-level keys that are not verified at runtime in the same way. You can check a file’s checksum, but there is no kernel-enforced identity that persists from launch through the lifetime of the process the way macOS code signing does.
For a port of Little Snitch to be meaningful rather than just cosmetically familiar, obdev will need a position on this. They can track process identity by path plus inode, by hash of the binary at launch time, or by cgroup identity if the system is organized that way. None of these are as clean as macOS’s model. A process can re-exec itself, change its binary on disk, or fork a child that inherits file descriptors but has a different identity. The right answer is probably a combination of approaches with explicit fallbacks documented to the user.
Distribution Fragmentation
Beyond the kernel APIs, obdev faces a distribution problem that macOS developers never encounter. Little Snitch on macOS ships as a single .dmg and works on every Apple-silicon or Intel Mac running a supported OS version. Linux has Debian, Ubuntu, Fedora, Arch, NixOS, immutable distributions like Silverblue, and a kernel version spread from 5.4 LTS to 6.x on bleeding-edge systems.
The eBPF approach requires at least kernel 5.7 for LSM hooks, and many enterprise or embedded systems are still on 5.4 or even 4.19. If obdev wants broad coverage, they may need to ship both an eBPF path and an NFQUEUE fallback, with different performance and reliability characteristics.
Packaging is its own challenge. A system daemon that installs kernel-level hooks cannot realistically ship as a Flatpak or Snap without significant capability grants that undermine those formats’ security model. It probably needs a traditional distribution-native package with a systemd service and polkit rules, plus a separate GUI component. That means maintaining .deb, .rpm, and possibly an AUR package, with different init systems in the mix on unusual distributions.
Why This Matters Anyway
For most of my time on Linux, the standard answer to “how do I control what my applications can connect to” has been iptables rules by UID, which requires knowing the UID your application runs as, or running the application in a network namespace. Both approaches require root access to configure and are completely invisible to a non-technical user.
The argument that Linux is more transparent than macOS about what is running on your system is true at the kernel level and false at the user experience level. Without a tool like Little Snitch, the average Linux desktop user has less visibility into outbound connections than a macOS user running the free trial.
Obdev bringing Little Snitch to Linux means a team with a decade of experience building this class of tool is now engaging with the Linux kernel API problem directly. Whatever architectural choices they make will be public in how the product behaves, and the solutions they develop for process identity, kernel compatibility, and GUI prompting latency will inform the next generation of Linux privacy tooling regardless of whether you pay for their product.
OpenSnitch has been doing the hard work in the open for years. Portmaster has been building toward a commercial-quality experience. But a team with Little Snitch’s track record entering the Linux space raises the floor for what users should expect from per-application outbound control, and that pressure is worth having.