Firewalls

enforza-cockpit: a free web GUI for nftables on your homelab

enforza-cockpit is a free, open-source Cockpit plugin that gives you a browser GUI for nftables: build a firewall policy, turn one Linux box into a router and NAT gateway, and watch per-rule logs, with no cloud account and nothing phoning home.

nftables is the right tool for a Linux firewall and a genuinely nice piece of engineering, but writing rulesets by hand is a niche pleasure. If you run a homelab or a small cloud box and you want a firewall you can see and click, without pulling in a whole appliance distribution or standing up something in the cloud, there is a free, open-source option worth knowing about: enforza-cockpit. It is a Cockpit plugin that gives you a browser GUI over nftables. Install it, build a policy, and watch per-rule logs land in your syslog.

This article walks through what it is and how to use it. It asks nothing of you and there is nothing to buy, so read it as a how-to. The one-line disclosure of who makes it is at the bottom where it belongs.

What it actually is

enforza-cockpit turns a single Linux box into three things that usually come separately, all on the one host and with nothing external to depend on:

  • A firewall. Three rule sections compile to a live nftables ruleset that filters traffic to, through, and from the host.
  • A router and NAT gateway. Turn on IP forwarding and the box routes between its interfaces; mark a rule SNAT and it masquerades outbound traffic, so it works as an edge gateway for whatever sits behind it.
  • A management console. Cockpit’s web UI is the console. You build, preview, apply and log every rule from the browser. There is no separate controller, no agent phoning home, and no cloud account.

Because it runs entirely on the one host and inside Cockpit’s own authentication and TLS, the result is a stand-alone appliance you happen to manage from a web page. That is the whole idea: a clean GUI wrapper for a local nftables firewall, nothing more clever than that.

Installing it

You need a Linux server with root or sudo, inbound access to TCP 9090 (Cockpit’s web port) from wherever you browse, and a browser. On a cloud instance, open 9090 in the security group to your own IP only, not the world.

There are two scripts. The first clones the repo and installs the prerequisites, detecting your package manager along the way:

git clone https://github.com/enforza/enforza-cockpit.git
cd enforza-cockpit
sudo ./bootstrap.sh

That pulls in nftables, its JSON/Python bindings (so the GUI can read and write the ruleset programmatically), ulogd2 for userspace netfilter logging, and Cockpit itself. It enables Cockpit’s socket immediately, and enables the nftables service but deliberately does not start it with an empty ruleset, so it cannot lock you out before you have written a policy.

The second script installs the plugin into Cockpit:

sudo ./deploy.sh

That copies the built files into Cockpit’s system package directory (/usr/share/cockpit/enforza). Browse to https://<your-host>:9090, accept the self-signed certificate warning on a fresh install, and log in with a local system account that can use sudo. The firewall plugin needs administrative privilege, so if Cockpit shows a “Limited access” banner, click it and re-authenticate. The plugin then appears as Firewall (enforza) in the left-hand menu.

The three chains, in plain terms

The reason a GUI over nftables is useful is that the mental model is simple once someone lays it out. enforza-cockpit gives you three rule sections, and each maps directly to one of nftables’ filter hooks:

Tabnftables hookWhat it controls
ManagementinputTraffic to the box itself: SSH, the Cockpit port, health checks.
NetworkforwardTraffic passing through the box between other hosts and the internet.
LocaloutputTraffic originating from the box: updates, DNS, outbound calls.

Each section has a default action (accept or drop). First match wins; anything a rule does not catch falls through to the default. There is also an Objects tab for reusable named network (CIDR) and port sets: define home-lan, mgmt, web once and reference them as @home-lan, @web and so on across any rule. Edit the object and every rule using it updates, which is the same reason you use named sets in a hand-written ruleset, minus the typing.

If you have ever stared at input, forward and output in the docs and not been quite sure which one your rule belonged in, seeing them as three labelled tabs is most of the value on its own.

Building a first policy

The safe first policy is a locked-down Management section: allow SSH and the Cockpit port from your own address, drop everything else. In the Management tab, set the default action to drop, then add a rule:

  • Action accept, Protocol tcp, Destination port 22
  • Source your admin IP or CIDR (or any to start with)
  • Tick Log matches, which is what puts the rule in your syslog
  • Comment SSH from admin, which is included in the log line, so make it mean something

Add a second rule the same way for TCP 9090 so you keep the Cockpit UI. And heed the obvious warning: allow SSH and Cockpit from your own IP before you apply a default-drop policy, or you will be relying on the auto-revert to get back in.

Then the apply flow, which is the part that makes this safe to do on a box you care about:

  1. Preview renders the policy to nftables JSON and dry-runs it against the kernel. A green “Valid” means the kernel accepts it.
  2. Apply puts it live and starts a confirm-or-revert banner with a 60-second countdown.
  3. Verify you still have access. Open a fresh SSH session, or just check the Cockpit page still responds.
  4. Confirm to keep the ruleset. Do nothing and it auto-reverts to the previous one; there is also a Revert now button.
  5. Save persists the policy document to /etc/enforza/policy.json so it reloads next time.

Your rules are now live in the inet enforza nftables table, and you can always check what actually landed from a shell with sudo nft list table inet enforza. The GUI is a front end; the kernel state is the truth, and nothing stops you reading it directly.

Turning it into a router and NAT gateway

The Network tab (the forward path) is where the box stops being just a host firewall and becomes a gateway. Turn on Enable routing to switch on IP forwarding, then add a Network rule and mark it SNAT. That masquerades outbound traffic from the hosts behind the box out through its public interface, so a single Linux instance becomes the edge gateway for a whole segment: a lab network, a set of VMs, or the machines on a home LAN. Network rules can carry both a LOG flag and the SNAT flag, so you can log and source-NAT the same traffic, and reference @objects for source, destination and ports to keep the rules readable.

That is the same job a small cloud NAT instance does, on hardware you already have.

Watching the logs

Any rule with Log matches ticked emits a kernel log line each time it matches, and enforza-cockpit tags every line with three useful things: the section (to-firewall, through-firewall or from-firewall), a plain verdict word (ALLOW, DENY or REJECT), and your rule’s comment. So a matched SSH accept reads like:

kernel: enforza to-firewall ALLOW: SSH from admin IN=eth0 ... SRC=203.0.113.10 DPT=22 ...

Because the verdict is a plain word, grep does the rest:

sudo tail -f /var/log/syslog | grep 'enforza.*DENY'   # only denials
sudo tail -f /var/log/syslog | grep 'SSH from admin'  # one rule, by its comment

On distributions without /var/log/syslog (many dnf-based ones), read the kernel log through the journal instead with sudo journalctl -kf | grep enforza.

One gotcha worth internalising, because it catches everyone once: a section’s default drop is not logged. If you want to see what is being denied, add an explicit last rule with Action drop (or reject), Source any and Log matches ticked, and make sure it sits at the bottom of the list. First match wins, so a logging catch-all only works as the final rule.

What it isn’t

Since being honest about limits is more useful than a feature list, here is what enforza-cockpit does not do:

  • No application-layer filtering. It is plain nftables: L3/L4 rules plus SNAT. There is no FQDN or hostname filtering, no SNI inspection, no L7 anything. If you need to allow github.com but not the rest of the internet, this is not the tool.
  • One host, not a fleet. It manages the box it runs on. There is no central console, no pushing one policy to many machines, and no policy-as-code pipeline. For a homelab that is exactly right; for fifty boxes it is fifty logins.
  • A static ruleset. The kernel enforces the rules you compiled. It does not learn, score, or adapt, and it does not pretend to.

None of that is a criticism. It is a small, sharp tool that does one job well, and knowing the edges is what lets you use it confidently.

Who makes it

enforza-cockpit is a free, MIT-licensed community tool from the people behind enforza, a managed cloud-firewall platform. It needs no account and no cloud connection, the code is open so you can read exactly what it does to your box, and contributions and bug reports are welcome. That is the whole disclosure. Go build a firewall.

FAQ

Is enforza-cockpit really free?

Yes. It is MIT-licensed and open source, needs no account, no cloud connection and no subscription, and all firewall state stays on your machine. The repo is github.com/enforza/enforza-cockpit.

What is it built on?

It is a plugin for Cockpit, the web console that ships with most Linux distributions. The GUI renders your policy to a live nftables ruleset (in a table called inet enforza) and lets the kernel do the enforcing. A bootstrap script installs nftables, its JSON/Python bindings, ulogd2 for logging, and Cockpit itself.

Which Linux distributions does it support?

Debian and Ubuntu, Fedora, RHEL/Rocky/Alma, and openSUSE. The bootstrap script detects your package manager (apt, dnf, yum or zypper) so the same steps work across the common homelab and small-cloud distributions.

Can a bad rule lock me out of the box?

It is designed so it cannot, permanently. Every apply uses a confirm-or-revert model: after you click Apply you have 60 seconds to click Confirm, and if you do not, the previous ruleset is automatically restored. Keep a second way onto the box handy the first few times anyway. If you ever do get stuck, get on out-of-band and run nft flush ruleset or nft delete table inet enforza.

Does it do FQDN or application-layer filtering?

No, and it is worth being clear about that. It is a GUI over plain nftables: L3/L4 rules on the three chains (traffic to, through and from the host) plus SNAT. There is no FQDN, SNI or other L7 filtering, and it manages one host, not a fleet. If that is what you need, this is not the tool.

Can it act as a router and NAT gateway?

Yes. Turn on Enable routing (IP forwarding) and the box routes between its interfaces; mark a Network rule SNAT and it masquerades outbound traffic, so it works as an edge gateway for the machines behind it. That is the homelab use case it is happiest in.

← Back to all articles
Same features. Without the cost.

Ditch the data-processing charges.

Flat, per-firewall pricing — and no per-GB data-processing charges, ever. The same egress filtering, FQDN/SNI-based L7 and NAT, in any cloud or on-prem. Start free, no card.