Application monitoring with OpenSnitch
OpenSnitch is an
"interactive application firewall
". Like other firewalls, it uses a
series of rules to decide what network traffic should be permitted. Unlike
many other firewalls, though, OpenSnitch does not ask the user to create a list of rules
ahead of time. Instead, the list of rules can be built up
incrementally as applications make connections — and the user can peruse both
the rules that have built up over time, and statistics on the connections that
have been attempted.
The OpenSnitch project was started in 2017 by Simone Margaritelli as a native Linux alternative to the Little Snitch firewall application for Apple devices. Usually, firewalls focus on blocking unwanted inbound connections; both Snitches, on the other hand, specialize in blocking unwanted outgoing connections — hopefully foiling unwanted tracking, advertising, and malware connections to command-and-control servers. Over the past seven years, the GPLv3-licensed project has accepted contributions from 80 other contributors, and grown into a capable firewall. Version 1.6.6, released at the beginning of July, contains a small set of bug fixes and improvements.
OpenSnitch produces both .deb and .rpm packages for installation, but the project is only included in the official package lists for Debian, Ubuntu, Arch, and NixOS. The Debian package maintainer, Gustavo Iñiguez Goya, is also the project's most prolific contributor. There are two separate packages, one for the core daemon, and one for the user interface.
Using OpenSnitch
Upon first starting OpenSnitch the user is presented with a series of notifications like this:
OpenSnitch's strength is in its interactivity. Every time an application attempts to open a connection for which there is not an existing firewall rule, the user is shown a dialog that lets them select the appropriate action. If the user doesn't respond within a set amount of time, a configurable default action is taken. In either case, the action is recorded as a new rule, so that further connections from the same application don't trigger new dialogs. Rules can refer to specific executables (identified by path or by hash), command lines, mount points, source and destination IP addresses and ports, users, processes, or protocols. While the popups don't allow one to be too granular, the rule-editor also supports setting regular expressions or other conditions for all of these attributes. The advantage of identifying executables by hash is that malware cannot surreptitiously hijack existing programs to circumvent the firewall rules. The disadvantage is that the rules will require updating after an operating-system update; whether this extra work is worth the security compared to identifying programs by path is, of course, up to the user.
The project's "Getting started" documentation suggests setting the default action to "allow" for a while, so that OpenSnitch can build up a profile of what connections are normal. Then the user can inspect the set of created rules and determine how they would like to move forward. While the initial flood of notifications is a tad overwhelming, once the first rules are in place for the applications currently in use, everything becomes much more manageable. The main user interface (written in QT, using QT's Python bindings) allows one to inspect which firewall actions have been taken recently, peruse the list of accumulated rules, look at what domains are being connected to, and other related administrative actions:
One nice touch is that the user interface does not necessarily have to run on the same computer as the daemon itself. This lets users run the daemon on a fleet of machines, and have them report back to a single centralized computer — a design that makes OpenSnitch useful for more than just personal use. The user interface is not without flaws, however. It disagreed with my usual choice of window manager (Sway), showing black boxes in the rule-editing dialog, until I set QT_QPA_PLATFORM to xcb as recommended in the release notes. The images above are from GNOME, which seems to have no trouble with the user interface.
The graphical user interface isn't the only option for configuring OpenSnitch, however. Users can also write rules directly in JSON format. Or, more reasonably, export them from one computer to use on another. The JSON format was previously a bit complicated to write by hand, but the 1.6.6 release included some cleanup to make rules easier to write. Here's an example from the documentation of a rule that blocks executables with a non-empty LD_PRELOAD environment variable from making connections:
{
"created": "2024-05-31T23:39:28+02:00",
"updated": "2024-05-31T23:39:28+02:00",
"name": "000-block-ld-preload",
"description": "",
"action": "reject",
"duration": "always",
"enabled": true,
"precedence": true,
"nolog": false
"operator": {
"operand": "process.env.LD_PRELOAD",
"data": "^(\\.|/).*",
"type": "regexp",
"sensitive": false
}
}
Implementation details
OpenSnitch can monitor processes in three different ways: using BPF, using the kernel's auditing support, or by watching files in /proc. The preferred method is BPF, because it is both more performant and has fewer problems with intercepting connections under some conditions. OpenSnitch ships with three BPF programs: one that intercepts DNS requests, one that tracks processes as they start and terminate, and one that tracks non-DNS connections. They require a kernel newer than 5.5 to function properly, because they depend on the bpf_probe_read_user_str() helper function. Of the three methods OpenSnitch supports, BPF is the only one that can intercept connections made from kernel space — such as might be caused by a kernel compromise.
If BPF cannot be used, OpenSnitch can instead rely on the kernel's audit subsystem. This requires auditd, the user space component of the system, to be installed and configured properly. Unfortunately, unlike BPF, the audit subsystem doesn't permit intercepting and blocking the connection attempt itself. Instead, OpenSnitch uses iptables to redirect outgoing connections to a queue that it monitors. When a new connection is made, the program uses netlink and information from the audit subsystem to determine which process is responsible. Once it has the information needed to apply its rules, it either drops the connection or sets up a new temporary iptables rule for it.
When the audit system is not available, OpenSnitch gets the information it needs by reading files from /proc — which is available on most systems, making it a reasonable fallback option. The downside is that not all connections can be tracked this way. For example, most connections are visible under /proc/net/, but connections made from a container are instead under /proc/<PID>/net/ for the process identifier (PID) of the container. Because the decision-making parts of OpenSnitch run in user space, the non-BPF methods can cause problems when the computer is under heavy load; connections can be closed or otherwise interfered with before OpenSnitch can find the relevant information.
Despite the performance and reliability improvements compared to searching /proc, the other two methods still remain officially experimental. Still, they have been functional for several years at this point, and using BPF by default seems like a reasonable choice.
In all, OpenSnitch provides a unique offering compared to other firewall solutions. While there are other products that focus on blocking outbound connections — such as Pi-hole — OpenSnitch's interactivity and easy configuration make it a good choice for personal use. The project is not perfect, but it is a useful tool for preventing unwanted connections, and definitely something that security-conscious users should consider.
| Index entries for this article | |
|---|---|
| Security | Tools/Firewall |
