Hardened Application Firewall

Feb. 19, 2025 [hardening] [privacy-security] [guides] [technology] [libre]

Network firewalls for the Linuxes have historically been application-blind. Which is a point of shame for a platform prided on security and privacy, especially considering that proprietary competitor Windows has had per-program discriminating firewalling so much sooner. A while back, I’d found OpenSnitch to be functionally ready. After using it on my daily driver for several months, I’m finally comfortable sharing some operational practices.

Setting Up For First Use

After pulling the opensnitch package down from the repository, edit the file at /etc/opensnitchd/default-config.json and set nftables as the default:

"Firewall" : "nftables",

You will probably want to add an nftables rule to allow ICMP traffic for ping functionality.

nft insert rule mangle output icmp type echo-request accept

Using OpenSnitch’s UI, create a new rule to permit local loopback traffic. Its entry should start with zeroes to ensure it gets parsed first.


Local loopback rules in OpenSnitch UI

That is,

Name: “000-allow-always-localhost”
Enable 🗹
Action: Allow
Duration: Always
(Tab) Network
To this IP/Network: ^(127\.0\.0\.1|::1)$

OpenSnitch can also filter by regular expressions for blocking domains based on predictable strings that indicate intent to use for tracking or adertising. This is easily done through a blocklist.

wget --https-only https://github.com/mmotti/pihole-regex/raw/refs/heads/master/regex.list

Create a rule that points to this regex.list with:

Name: “000-block-regex”
Enable 🗹
Priority rule 🗹
Action: Reject Duration: Always
(Tab) List of domains/IPs > To this list of domains (Regular expressions): /path/to/regex.list


Regular expression based blocklist rules in OpenSnitch UI

All that heavy lifting your ad blocker has been doing can be offloaded (or duplicated) to OpenSnitch, as it can also filter by static lists of domains. The project unofficially supplies a ready-made script for updating a local filter list.

wget --https-only https://raw.githubusercontent.com/evilsocket/opensnitch/master/utils/scripts/ads/update_adlists.sh

Change the line “adsDir="/etc/opensnitchd/blocklists/domains/” to instead point to the path for your own blocklists directory. Then create a cron or anacron job to run update_adlists.sh periodically.

Now, like with the regular expressions entry, we want to add another priority rule for domains blocking:

Name: “000-block-domains”
Enable 🗹
Priority rule 🗹
Action: Reject
Duration: Always
(Tab) List of domains/IPs > To this list of domains: /path/to/blocklists/directory

Now set the global default action in OpenSnitch’s settings. In Settings > Pop-ups tab > Default options:

Action: deny
Duration: 1h (1 Hour)
Default target: by executable
Show advanced view by default ☐
Filter connections also by: Destination IP 🗹
User ID ☐
Destination port ☐


OpenSnitch customized default settings

The rationale for these defaults are explained earlier in OpenSnitch, “uMatrix” for your Entire Desktop.

Permitting Common Programs

Now whenever your system attempts to initiate a new network connection to some resource for the first time, a dialog prompt will check with you whether to allow or deny. This can generate many prompts at first, so you will need to whitelist certain common programs with the always duration.

But there are two main categories of programs that necessitate different classes of rules.

  1. Connects to destination whose domain(s) is already known
  2. Connects to destinations whose domains are not always known

Most programs fall into the first class, while others such as Tor, Mumble or DNSutils’ dig fall into the second class. So for most programs, you should just be able to select “Allow” on the pop-up dialog. But for something like dnsutils’ dig, run a lookup to initiate an OpenSnitch prompt and use it to create a permanent rule, but instead of using the global default “To this host”, select “To this port” and append “53”:

Name: allow-always-usr-bin-dig
Enable 🗹
Action: Allow
Duration: Always
(Tab) Network
To this port: 53


OpenSnitch rule for programs whose destination host is unknown

Some utilities which you may want to create permanent Allow rules for include:

Web browsers are best treated specially, with permitting rules on a per-site basis. For example, you instruct Firefox to visit a website which you will only view for a few minutes and probably never return to again. With our default global settings, that is as easy as clicking “Allow”. And hopefully you’re also using something like uMatrix to limit third party domains, otherwise you might get clobbered with dialogs. Sites that you cherish and visit frequently are worth adding permanent rules for by first scroll-wheeling or hotkeying down the Duration dropdown to “forever”.


OpenSnitch popup dialog for Firefox

OpenSnitch complements browser extension web firewalls extremely well.

Exception Denying

An unintended strength of OpenSnitch is its capacity to preemptively block connections with high specificity. For example, denying connections to www.bitchute.com but permitting connections to old.bitchute.com. That way, mistyping addresses or following other people’s links doesn’t land you on a page you wish not to see. I have many permanent reject rules configured for connections that I don’t want applications to make. A few examples:

It is important that you select the Reject action for such instances, rather than the Deny action. Otherwise it will leave the program waiting for timeout. It shouldn’t matter that you’re sending a “somebody is home” tipoff, these are outbound connections being intercepted.

Plastered over the OpenSnitch documentation are warnings not to broadly allow programs parsed by interpreters (e.g. Python) to be permitted wholesale, as it would allow any other python program to also access network. But since we set global defaults earlier to discriminate by destination IP as well as to default 1h (1 Hour) for temporary rules, permitting one interpreted program will not universally allow others of that same langauge to access network with impunity.

Common Syntax Shortcuts

I often find that permitting domains and its subdomains in one rule is best handled with .*\.example.com|example.com. Simply using only the first party domain on its own will fail to permit any subdomains.


Example of inluding subdomains in one rule

Specifying multiple ports, such as for a program like Tor, is easily handled through passing a ^(9001|9050|443|8443|9443)$ regular expression to the “To this port” field.

Becoming Second Nature

Once your common programs from your regular regiment have all been accounted for, which could take up to a few days, then OpenSnitch settles neatly into the background. Installing a new networked program, or visiting a new interactive website will occasionally have you wrangling some new permissions. But it will by then be a familiar process.

Running a system wide application-aware firewall alongside browser extension web firewalls makes for an incredible defense-in-depth approach. I hope that OpenSnitch continues to mature, but am also keeping some fallback solutions close by just in case. Originally I was investigating using AppArmor for this. But as I poured over the documentation it became increasingly clear that AppArmor is geared more toward packagers and developers, and not toward end users who need to rapidly allow or disallow permissions on the fly. Not to mention AppArmor is still entirely network access allow or disallowed, with no granularity beyond that.