AFW
AFW — An Application Firewall That Actually Makes Sense on Linux
I don’t like running systems I don’t understand. And for years, one thing bugged me about every Linux desktop I’ve set up: outbound traffic is a free-for-all. Any process can talk to any IP on any port, and unless you’re sitting there watching Wireshark, you’d never know.
On macOS, tools like Little Snitch have solved this for a long time. You launch an app, it asks to connect somewhere, you say yes or no. Simple. On Linux? We have iptables. We have nftables. We have UFW. All great tools — but they’re static. You write rules by hand, and those rules sit there whether the app is running or not. There’s no awareness of what’s actually running on your machine.
That’s what I built AFW to fix.
What AFW Does
AFW is an application firewall for Linux. It uses eBPF to watch every process that starts and stops on your system. When a monitored app launches — say, Discord — AFW automatically opens the ports it needs in nftables. When Discord closes, those ports close too. Everything else is dropped by default.
The idea is straightforward: your firewall should reflect what’s actually happening on your machine, not what you remembered to configure three months ago.
How It Works Under the Hood
AFW attaches eBPF tracepoints to sched_process_exec and sched_process_exit in the kernel. These fire every time any process starts or stops. The events flow through a perf buffer to the userspace daemon, which checks each process name against a config of monitored apps.
When the first instance of a monitored app starts, AFW adds nftables rules for that app’s allowed ports. When the last instance exits, those rules are removed. The default policy on both input and output chains is drop — if it’s not explicitly allowed, it doesn’t leave or enter your machine.
The whole thing runs as a systemd service. You configure it, enable it, and forget about it.
The Config Structure
One thing I wanted to get right was the config. I didn’t want one massive file with a hundred apps in it. AFW uses a drop-in directory structure:
1
2
3
4
5
6
7
8
9
10
/etc/afw/
├── afw.toml # Base rules (DNS, HTTPS, Cloudflare, etc.)
└── conf.d/
├── browsers.toml
├── communication.toml
├── development.toml
├── gaming.toml
├── media.toml
├── vpn_clients.toml
└── ...
Want to add a new category? Drop a .toml file in conf.d/. Want to disable all gaming rules? Delete one file. Each app config looks like this:
1
2
3
4
5
6
7
8
9
[[app]]
name = "discord"
binary = "Discord"
enabled = true
outbound = [
{ port = 443, protocol = "tcp" },
{ port = 80, protocol = "tcp" },
{ port = 50000, range_end = 65535, protocol = "udp" },
]
It ships with 119 pre-configured apps across 14 categories — browsers, communication, media, gaming, development tools, VPN clients, package managers, and more. Most people won’t need to touch the config at all.
Security Was Not an Afterthought
I ran the entire codebase through an OWASP Top 10 audit. Found some real issues and fixed them:
- Injection prevention: App names and port rules are validated before they ever touch nftables. Every character that could enable command injection — quotes, newlines, semicolons, backticks — is rejected.
- IPC authentication: The daemon communicates over a Unix socket. Mutating commands (add, remove, enable, disable) require root. Read-only commands (status, list) work for any user with socket access. This is enforced via kernel-provided peer credentials, not something a client can fake.
- DoS hardening: IPC reads are bounded to 64KB, connections are capped at 16 concurrent, clients get a 5-second timeout, and the eBPF event channel is bounded to prevent fork bombs from eating all your memory.
The project has 328 tests, including a dedicated OWASP security test suite that verifies injection patterns, access control, and data integrity.
Why Rust and eBPF
Rust eliminates entire classes of bugs that would be dangerous in a firewall — buffer overflows, use-after-free, data races. The type system catches port overflow issues at compile time (ports are u16, not strings). Memory safety means I’m not worrying about a crafted packet name causing a heap corruption in the daemon.
eBPF is the right tool for process monitoring because it runs in the kernel with near-zero overhead. No polling /proc, no ptrace, no audit daemon. The kernel tells us directly when processes start and stop, and we react in milliseconds.
Getting It
AFW runs on any Linux system with eBPF support and nftables. It’s packaged for:
- Arch/AUR: PKGBUILD included
- Debian/Ubuntu:
.debpackages on the GitHub releases page - Fedora/RHEL: RPM spec included
- Any distro:
sudo ./install.sh
The source is on GitHub. It’s GPL-3.0.
But for now, AFW does what I originally needed: a firewall that knows what’s running and acts accordingly. If you’re the kind of person who wants to understand and control what your system is doing on the network, give it a look.
