tui-tools

tui-firewall

ufw, firewalld, iptables and nftables, every change previewed before it runs

A terminal UI (TUI) for ufw, firewall-cmd, iptables and nft on Linux: the Linux firewall, driving ufw, firewalld, iptables and nftables directly.

v0.6.2betafirewallMIT
The rules table, with status, default policies and logging in the header
The rules table, with status, default policies and logging in the header

A terminal UI for the Linux firewall, driving ufw, firewalld, iptables and nftables directly. It shows the rules you actually have — action, ports, source, destination, services, rich rules, IPv6 and forwarding included — alongside the default policies or chain policies and the logging level.

The UI never builds a command line: the confirm dialog shows the backend's command values and hands the same ones to the runner, so what you read is what executes.

On a machine nothing else manages it drives nft itself: filter chains by hook, a NAT view, and named sets as aliases. It writes only where it safely can, and says why when it will not.

On a cloud image whose iptables rules are restored at boot by iptables-persistent or iptables-services, a new rule lands in front of the catch-all REJECT, and persisting is its own previewed step.

Run tui-firewall --demo for an in-memory ufw, --demo=firewalld for firewalld, --demo=iptables for a cloud-image iptables, or --demo=nftables for a sample router ruleset: every command is built and previewed for real, and nothing touches your system.

Keys

KeyAction
aAdd a rule
dDelete the selected rule
DDisable or enable the selected rule, keeping its spec and position (nftables)
eEnable or disable the firewall (ufw)
pChange a default policy, or a firewalld zone target
LChange the logging level, or the firewalld log-denied value
rReload the firewall
WPersist the running rules with a diff preview (iptables); save the tool's table (nftables)
xActions this backend offers beyond these keys
/Filter rules across every column
vPick a group: a firewalld zone, an nftables or iptables chain, NAT, aliases
[ / ]Previous / next group: firewalld zones, nftables chains
RRe-read the firewall
?Help
qQuit

Press ? inside the tool for the full help screen, which is generated from the same action table as the key map.

Compatibility

This tool is a face on the program below, so its version matters. Tested is not a claim: a version lands there only after the tool's own suite passed against it on a real machine in the lab, and the run is kept as evidence in the repository. At startup the tool probes the backend once and says what it found in its header — a version nobody has run against is shown as (untested) in the warning colour, one older than the minimum as (below minimum) in the error colour. Neither stops it: the backend still refuses what it cannot do, in its own words.

ufw

ufw ≥ 0.36
Binary
ufw
Minimum
0.36
Probed with
ufw --version
Tested
0.36.2

Needs a version this new

  • rule-commentssince 0.35

What changes on older versions

  • ufw <0.36

    ufw status numbered has no app profile column, so a rule added from a profile is shown by its ports and cannot be edited as a profile

  • ufw 0.36.x

    status numbered indexes IPv4 and IPv6 halves of one rule separately, so deleting by number renumbers the rest and the list is re-read after every delete

firewalld

firewall-cmd ≥ 0.9
Binary
firewall-cmd
Minimum
0.9
Probed with
firewall-cmd --version
Tested
2.4.4

What changes on older versions

  • firewalld ==2.0.0

    --permanent --list-all-zones prints the same settings for every zone on this release (firewalld#1152), so the permanent half of the runtime/permanent comparison is not trustworthy; 2.0.1 fixed it

  • firewalld >=2.2

    firewalld removed the lockdown feature, so no lockdown state is shown

iptables

iptables ≥ 1.6
Binary
iptables
Minimum
1.6
Probed with
iptables --version
Tested
1.8.101.8.11

What changes on older versions

  • iptables <1.8

    only the legacy xtables interface exists, so the rules live outside nf_tables and the nft ruleset cannot show them; the backend reads them through iptables-save and the selector asks the legacy tables directly

  • iptables >=1.8

    iptables-nft writes table ip filter and ip6 filter in nf_tables, and iptables --version says which variant is installed; the nftables backend treats those tables as read-only and points here

nftables

nft ≥ 0.9
Binary
nft
Minimum
0.9
Probed with
nft --version
Tested
No recorded run yet. The tool still runs and reports the version it found as untested.

What changes on older versions

  • nftables <0.9

    nft -j does not exist yet, so there is no ruleset to read in any form this tool will parse. 0.9.0 is where nft grew JSON output and stamped it json_schema_version 1, the schema this backend reads, and that is the whole reason the minimum is 0.9

  • nftables >=0.9

    nft -j list ruleset carries rule handles whether or not -a is given, unlike the human output. A handle is how a rule is deleted, because positions shift the moment anything is inserted above them

Maintain ufw, firewalld, iptables or nftables? You are welcome to help shape this tool. About → maintainers

Guides

Written after the work, from a machine the commands were actually run on. Every guide that mentions tui-firewall is listed here.

  • A company tailnet with Keycloak single sign-on

    A local CA from tui-cert, Keycloak served over https with a certificate from it, and a Headscale control plane set up with tui-tailscale that admits only the members of one Keycloak group. Run end to end in the family lab and told through the screens it produced, including the login that was refused.

  • A private tailnet with your own CA

    A Headscale control plane with no public DNS name and no Let's Encrypt. tui-cert runs a local certificate authority, tui-tailscale serves its certificate and trusts it on each client, and machines join with pre-auth keys.

  • Opening ports on a cloud image's iptables firewall

    Many cloud images ship no ufw and no firewalld, just an iptables ruleset whose INPUT chain ends in a REJECT. A rule appended after it never matches. tui-firewall inserts in front of it, persists with a diff, and says when the running rules and the saved ones disagree.

  • Self-hosted Tailscale with Headscale and Google login

    One public host runs the Headscale control plane with a Let's Encrypt certificate, people log in with their Google account, and the same host serves its private network to every client. Every step is one key in tui-tailscale, previewed before it runs.

  • Verify a release before you run it

    Three checks, with the real commands and the real output, that answer who built a tui-tools binary before you give it your root password.

Install

Once per machine, then every tool in the family is one pacman away.

Add the repository
$ curl -fsSL https://pkgs.tui.tools/install.sh | sh
Or run the same thing yourself, one command at a time

A family whose whole promise is preview before you run is not going to insist you pipe a script into a shell. These are the commands that script runs.

The one-time setup, by hand
$ curl -fsSL -o /tmp/tui-tools.asc https://pkgs.tui.tools/pubkey.asc
  sudo pacman-key --add /tmp/tui-tools.asc
  sudo pacman-key --lsign-key \
    "$(gpg --show-keys --with-colons /tmp/tui-tools.asc | awk -F: '/^fpr:/{print $10; exit}')"
  printf '[tui-tools]\nServer = https://pkgs.tui.tools/arch/$arch\n' \
    | sudo tee -a /etc/pacman.conf
  sudo pacman -Sy
Arch Linux · tui-firewall
$ sudo pacman -S tui-firewall

Upgrades then arrive with the rest of your system updates.

What it can do to your machine

  • Preview, then confirm

    Every change is shown as an exact command line and confirmed before it runs. The dialog and the runner receive the same value, so the preview cannot drift from what executes.

  • Escalates for actions

    Reading the firewall and every change need root: ufw, firewall-cmd, nft, iptables and their -save/-restore tools, and netfilter-persistent. Escalation goes through sudo -n, which never prompts; --sudo "" runs the command directly when you are already root.

  • No daemon, no state of its own

    Nothing keeps running after you quit, and nothing is installed to run later. The system is the source of truth; the tool re-reads it after every change.

  • No network access

    The tool opens no network connection of its own. No telemetry, no update check, no phone home.

  • Static binary

    Released statically linked, with no runtime dependencies to install.

  • Signed release, with provenance

    checksums.txt carries a keyless cosign signature from this repository's release workflow, every archive has a CycloneDX SBOM, and every archive, package and the checksum file carry SLSA build provenance. The security page shows the three commands that check it.

Reporting

Report a security issue privately, through GitHub's private vulnerability reporting on the repository's Security tab. The family-wide policy, and what counts as in scope, is on the security page.

Downloads

v0.6.2 · 2026-09-26
FileSizeSHA-256
checksums.txt1 KB—
checksums.txt.sigstore.json10 KB—
provenance.intoto.jsonl12 KB—
tui-firewall-0.6.2-1-aarch64.pkg.tar.zst2.1 MB1d82c52e80d940a8c23fbc9cdaae96c92eb58b29adb82a579efd092075346f43
tui-firewall-0.6.2-1-x86_64.pkg.tar.zst2.3 MBbc908a1f94c9e914353d4e3961337e1202b3f3de66cd8e81976d6c0f3c401700
tui-firewall-0.6.2-1.aarch64.rpm2.1 MBac4b08ad540c9aab8d4b4d27b284f6d2c2004bbb3b43c57e78b92024195a38e3
tui-firewall-0.6.2-1.x86_64.rpm2.3 MBf24386dc3a550861e059f3861bd02e870afa2d2b08db6cdff66e3c84d7eee097
tui-firewall_0.6.2_amd64.deb2.3 MB27bcb8167abc5c949c3b579d62b9ba55eb4bcc3d655747e1d08d44fc5c5cf777
tui-firewall_0.6.2_arm64.deb2.1 MB3d98c8a1ea6750fd6c9ab80901c1c8aef1b4d450d440bb68eadca1f31f0cccee
tui-firewall_0.6.2_linux_amd64.tar.gz2.3 MB226847271494a327f322c307b00c724da21c3da6679d05ac87d6dd14dde06793
tui-firewall_0.6.2_linux_amd64.tar.gz.cyclonedx.json24 KB0d6743ff44ac5da3f68cfd254b875603b414671adf00f0219352c8a8d67f9945
tui-firewall_0.6.2_linux_arm64.tar.gz2.1 MBd2ead839e57295d912f290197764f1fec4f75247be50c3174b5cad669839f57d
tui-firewall_0.6.2_linux_arm64.tar.gz.cyclonedx.json24 KB916490918698d8e6759eac4854232f2a53e7f828ed341bdd08a4ce358b08db26

Every release ships a checksums.txt. Download it next to the archive and run sha256sum -c checksums.txt --ignore-missing. See verifying a download.

Releases

v0.6.2

2026-09-26
## tui-firewall v0.6.2

tui-firewall 0.6.2

- firewalld: `--check` and the reload in the UI read the firewall in under 2 s instead of about 8 s. The tool now asks firewalld for whole zone and policy listings and runs those reads in parallel. The output is unchanged.
- README: the `--check` section says what `--check` costs on each backend, and that callers can keep its answer for a few seconds.
Version 0.6.2.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 53f2c9ea82ea6375a2ad17ea0f9e68fff081fb12 0.6.2: firewalld --check in one parallel wave of whole listings (#29) (#30)

v0.6.1

2026-09-25
## tui-firewall v0.6.1

tui-firewall 0.6.1: tui-kit v0.4.4

tui-firewall 0.6.1 moves to tui-kit v0.4.4: every ufw, firewalld, iptables and nft command now runs in its own session without a controlling terminal, so a step that would prompt fails at once instead of hanging behind the UI, and a failed step reports its meaningful line, cleaned of escape codes. The two screenshots that changed under the new renderer are re-rendered and the install snippet points at this release.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 520745207888b5ba589419691cc195503b66a4b9 0.6.1: tui-kit v0.4.4 (#28)

v0.6.0

2026-09-25
## tui-firewall v0.6.0

tui-firewall 0.6.0

Every command preview is now shell-quoted (tui-kit v0.4.3): a rule whose comment carries a space or a parenthesis previews as `ufw allow 19443/tcp comment 'headscale control (tailnet)'`, and the line pasted into a shell runs the same argv. On iptables a comment is no longer limited to one word. A new `--open <port>/<proto>[,<port>/<proto>...]` flag, with an optional `--comment <text>`, starts the UI in the add-rule form prefilled to allow each port in turn, one form and one confirm per port, still previewed; it is the hand-off target for another tool that has just found a port closed. Every screenshot is re-rendered with the kit's fixed box borders.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* ae73a87eef6be8474cf0ade81c013e9f4cdc7594 0.6.0: quoted previews; --open prefilled add form (#26) (#27)

v0.5.0

2026-09-25
## tui-firewall v0.5.0

tui-firewall 0.5.0: plain iptables

tui-firewall now drives plain iptables, the firewall of cloud images whose rules are restored at boot by iptables-persistent (Debian, Ubuntu) or iptables-services (Fedora, RHEL). It is picked automatically when that loader is enabled, and the selection reason says so. A new rule is inserted right before the chain's catch-all REJECT or DROP, with the position and the reason in the preview; rules are deleted by specification, not by number. Changes apply to the running kernel, and `W` persists them with the layer's own save (`netfilter-persistent save`, or iptables-services' `iptables.init save` and `ip6tables.init save`), previewed with a diff against the saved files. The header, the status line and `--check` report runtime-vs-saved drift, ignoring the chains docker and tailscaled rebuild. Staging works here too, through `iptables-restore --noflush` with an automatic rollback. Also: tui-kit v0.4.0.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* cf8a7c2a3fb7552def245b51bee6c2c039f99b3d Bump anchore/sbom-action/download-syft in the github-actions group (#21)
* 5ee7cd68539d7cd9b6ce38a89faf09f6c639d9f8 Bump the github-actions group with 4 updates (#22)
* 00b452af619a977f1256d9bffb8fed1f9df19c75 README: link the tool's page on tui.tools (#20)
* 73fda3e67bd4d0b5fe93b06fc4654f669f41ecfe README: point the install snippet at the current release (#19)
* ee682ff208be4e6b9b43be015834f386d3b8fa63 Release 0.5.0: iptables backend; tui-kit v0.4.0 (#25)
* 00e649ca1e533480ff3f13c3ff97b8e56c7b12f5 iptables backend (#23) (#24)

v0.4.1

2026-09-02
## tui-firewall v0.4.1

tui-kit v0.3.0: the dialogs wrap and scroll instead of clipping the
command preview, and the picker filters as you type.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 21bcef401a84bcdb426ab13761f0cfaf75b2d395 Bump tui-kit to v0.3.0 (#18)

v0.4.0

2026-09-01
## tui-firewall v0.4.0

Manage, not view (wave 2): apply what it diagnoses, author what it manages; lab-validated.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 8ebbc93c5eab918b523bb9a5d55af9a28c80e599 Per-rule disable/enable with a persisted spec (#17)

v0.3.0

2026-09-01
## tui-firewall v0.3.0

Manage, not view: create and edit from the TUI (wave 1), lab-validated on the router image.

Built by this repository's CI on the tag itself. Each archive ships a
CycloneDX SBOM, `checksums.txt` carries a keyless cosign signature, and
every file has SLSA build provenance — see the README's "Verify a
download" for the two commands that check them.

The commits in this release:

## Changelog
* 91b40387200a319423f563e1359f0aa313ef98bd A code of conduct that fits on one screen (#9)
* e3ccd27185701b969d17f309fdc143cb45fceea8 Add --report, and a bug form that asks for it (#10)
* 6fc9ddb047873dc4bc830fd7a6b0fc3c2741794a Bump anchore/sbom-action/download-syft (#4)
* df922a3d08b89c45d65a47fbee6cc5466bb87db1 Carry the OpenSSF Best Practices badge (#7)
* 06b13fbc25228642a6f2140ba644ad623cb26a88 Fetch the tag annotation before releasing (#5)
* 1afcf1edea8ce6a9a5e1547138ae3384edc1f03e Manage, not view: persistence (Save), rule edit and move (#16)
* 51b2a63fd2d1a331f4fcde2eb5b3c88ae600458b Pin the kit tag that carries report/ (#11)
* e5a1ed852273d4775163d71ca55e328d2309866f Pin what CI runs, fuzz the parsers, and say the releases are signed (#3)
* 072389274fdb1ff4a64a4f7732ea7f0d94379a14 Print the whole tag message, not only its body (#6)
* ac88f4147890fa2b18462722a96456ffe418eab0 Search the report for the host name where the host name could leak (#12)
* f9ca88daffe73c0940f66081f8769329578e9847 Take tui-kit v0.2.8: hashed pip install, provenance bundle on the release (#8)
* 4f12d5b2a958d5b627fa33bafad22940d5e26fb1 live log: journalctl -k is --dmesg, not --kernel (#15)
* 644a640f745b3a9df3c5710cf391312a777ab0e5 nftables backend: router-shaped matches and staged atomic apply (#13)
* a6028a9d7d50ca2c73afae89f5e9c2c40052b564 nftables: per-rule logging and a live firewall-log view (#14)

v0.2.2

2026-08-30
## Changelog
* 77b917006ffb71046e6fd7079886dfb9300b452e Drop cross-repo dispatch; the package repo and the site poll releases
* b5303ad204323bc125d642bedd7e728588936598 Gate the release on a security job, and sign what it publishes
* a9872f61b7ff1a97653a8cf666932df2ab6b224a Packages are available from pkgs.tui.tools
* c8e32ba9f6c83d7ba5461926ea95c98f17ea00d6 Pin the two actions that publish no moving major tag (#2)
* 89f338010ee80c08d3b7f49326295f1f9b3c659d Stop telling readers the packages are unpublished

v0.2.1

2026-08-30
## Changelog
* 4062d6971a9e859a8be3c2c6482541d922e685ef Ship the tool as a .deb, an .rpm and a pacman package

v0.2.0

2026-08-30
## Changelog
* 207f07781735090534fcfefc2ed7a06a9066fe9d Add --check, a non-interactive read path, and a backend smoke test
* 2f750800d0ddd6aaa7f030f90b7ab94ade7d3424 Add tool.json and generate the README Install section from it
* c48b5087567f49c363aa218b8a0d5056bdef163d Assert real firewalld behaviour in the lab, not a stub
* fe0eab2a5a1e383c75a02412264451bd5b6ffc29 Bump the github-actions group with 5 updates (#1)
* a041d686c727f2131875c495967d02f958b87450 Document firewalld, and screenshot it
* 8a08d6e8a0edd23c19ecd39b52b58e9e2b5554da Drive firewalld for real, behind the same backend interface
* a2f36e0bf3049795539f3f94446d8bd69c1d38cc Hold both ends of the tested firewalld range
* 745e37130fab6b1576310b1974afe09797da5827 Hold this tool to the family's lint bar
* 2e081b07e7a2d078eae6952e4078b3b007460f0b Keep the sudo note inside the manifest's length limit
* 2fa4c9aa63b2b9d28510117c5d04c54d7fc3a72c Re-pin the kit at v0.1.3 for the help panel width fix
* e46dccea33b8b8e70eb22637c0a6771dcfb9b3d2 Re-pin the kit at v0.2.3
* 615bf4454c3fbd7d807acaa2de0c6e7823ac6ab3 Separate deciding which firewall to drive from reaching for it
* 34abbdb3baf3d43283c99461ec10c6b7d44ab6f4 Show which ufw version is being driven
* 7ed706447b61f79992a17fdcc95648be2c53e7cf Test the firewalld backend against captured firewall-cmd output