=GATES(who.opened)
1
lff.kosir.info · agentless · MIT

linux-firewall

Which gates stand open, and who told them to.

Declare what each host is allowed to expose. Then read the effective firewalld ruleset, diff it against the declaration, and reconcile the difference with per-host confirmation.

“And the rule was added for one afternoon, and the afternoon was in 2021.” Findings 4:9 — nobody remembers, and nobody dares remove it

A firewall is not a state, it is an accumulation. Every rule was added by someone reasonable, for a reason that was true at the time, and almost none were ever taken out. The question is not whether thy rules are wrong. It is whether anyone still knows why they are there.

web01.hostname.loc · default zone · click a gate to shut it

sshthe guard holds this one httpin policy httpsin policy 8443/tcpin policy, absent from host 5432/tcpon host, not in policy 8080/tcpon host, reason unknown

Try the first one. It will refuse, and on thy real fleet it refuses too — unless thou passest --force, which exists, and which thou shouldst think about before using.

2

The Porter's Round

Three verbs. Two of them only look.

discover — read-only. Connects over SSH, escalates with sudo, and reads the effective firewalld configuration for each host's default zone: allowed services and ports, in both the runtime and the permanent configuration. It diffs that against thy policy and writes firewall_plan.json plus a workbook.

apply — takes the plan and reconciles, per host, with confirmation. It writes the permanent rules first, then reloads and verifies the result, and records what changed in firewall_results.json.

report — re-renders an existing plan to Excel without touching a single host. For the second meeting about the same spreadsheet.

local.user@localhost $ linux_firewall.py discover -H hosts.txt --policy policy.json
88 hosts · firewalld on 84 · nftables-only on 2 · unmanaged 2
remove 41 services/ports on hosts but not in policy
add 12 policy entries absent from the host
blocked 3 changes the SSH guard refused
drift 7 rules in runtime but not permanent
firewall_report.xlsx written. nothing was changed.

“Runtime but not permanent” is the quiet one. Those rules are working today and will vanish at the next reboot, and nothing will tell thee when they do.

Hosts without firewalld

firewalld is the primary backend and the only one apply supports. On a host without it — or with --backend nftablesdiscover falls back to dumping the nftables ruleset so thou canst at least see it. It does not diff or reconcile that, and it says so rather than skipping the host in silence.

3

The Policy

One file thou ownest, committed to a repository, defensible in a meeting

A JSON file — or YAML, if PyYAML is installed. Groups hold shared allow-lists, hosts may name a group and add their own, and optional defaults apply to any host not listed.

// policy.json — groups, hosts, defaults { "groups": { "web": {"allow_services": ["ssh", "http", "https"], "allow_ports": ["8443/tcp"]} }, "hosts": { "web01.hostname.loc": {"group": "web", "allow_ports": ["9100/tcp"]}, "db01.hostname.loc": {"allow_services": ["ssh"], "allow_ports": ["5432/tcp"]} }, "defaults": {"allow_services": ["ssh"]} }

A host's effective allow-list is the union of the defaults, its group, and its own entry. A port written without a protocol is taken as /tcp.

The unmanaged host

A host absent from the policy, with no defaults to fall back on, is treated as unmanaged. Its extra rules are reported so thou canst see them, and never removed. Silence in a policy file means “I have not decided about this host yet”, not “strip it”, and the tool refuses to read it the second way.

4

Thou Shalt Not Bar Thine Own Door

A guard thou canst override, and a moment's thought before thou dost

Every fleet engineer has, exactly once, dropped their own SSH session with a firewall rule and then discovered that the console access they were sure existed does not. It is a very quiet thirty seconds.

The SSH lockout guard

Any change that would drop thy control SSH access is blocked, and the blocked items are listed in the Policy Drift sheet so thou knowest exactly what was held back and why. --force disables the guard. It exists because occasionally thou really art migrating the SSH port and thou dost have console access — but it is a flag thou shouldst have to type on purpose.

If SSH does not run on 22, tell the guard which port to protect with --ssh-port 2222/tcp. A guard defending the wrong door is worse than no guard, because thou wilt trust it.

Permanent first, then reload, then verify. The change is written to the permanent configuration, firewalld is reloaded, and the result is read back. An apply that reported success without the rule landing is a failure, not a pass.

Per host, by default. Eighty hosts is eighty decisions. -y / --yes skips the prompts for automation, and like --force it is there because sometimes thou needest it — but the default is to stop and ask, so thou canst halt at host nine when something looks wrong rather than finding out at host eighty.

A note on scope, so nothing surprises thee: version one reconciles services and ports on the default zone. Rich rules, sources, interfaces and multi-zone arrangements are read and reported, but never modified.

5

A Report Thou Canst Hand to Anyone

Six sheets, and the interesting ones are not the first two

SummaryPolicy DriftEffective Rules Runtime vs PermanentErrorsAbout

Policy Drift is the working sheet: every service and port to add or remove, plus anything the SSH guard blocked. Effective Rules puts the runtime services and ports next to what the policy allows, side by side, which is the view that settles arguments. Runtime vs Permanent names the rules that exist in one and not the other — the ones that will quietly change behaviour at the next reboot.

The Summary sheet carries, per host, the backend, the zone, the change count, the blocked count, whether runtime and permanent agree, and whether the host is managed at all. That last column is worth reading first.

6

The Rite of the Gates

Describe, then look, then reconcile — in that order

# receive it git clone https://github.com/vikozs/linux-firewall.git cd linux-firewall python3 -m pip install -r requirements.txt
# look — read-only, writes a plan and a report python3 linux_firewall.py discover -H hosts.txt -u local.user \ --policy policy.json --ask-ssh-pass --sudo-pass-same-as-ssh
# reconcile — confirms per host, guards thy own session python3 linux_firewall.py apply --plan firewall_plan.json \ --policy policy.json -H hosts.txt -u local.user \ --ask-ssh-pass --sudo-pass-same-as-ssh
# and if SSH lives elsewhere, tell the guard which door is thine python3 linux_firewall.py discover -H hosts.txt -u local.user \ --policy policy.json --ask-ssh-pass --sudo-pass-same-as-ssh \ --ssh-port 2222/tcp

Auth flags match every other book: keys or agent by default, --ask-ssh-pass for password login, --sudo-pass-same-as-ssh for sudo. Passwords travel by stdin or the SSHPASS environment variable, never argv. Sudo is needed for firewall-cmd.

7

What This Actually Is

Breaking the fourth wall, briefly

A real tool, written by a real sysadmin who inherited a fleet whose firewall rules had been accumulating since before he arrived, and could not find one person who could explain port 8080. Agentless, MIT licensed.

It talks to firewalld through firewall-cmd, because that is the interface RHEL supports, and hand-written nft rules are how thou endest up with a fleet nobody else can maintain. Hosts without firewalld are reported as such rather than quietly skipped.

It writes an xlsx, and it never writes a formula into one. Everything a host reports is written as text, so a compromised server cannot hide a payload in a zone name and have it fire when thou openest the report. Same as its elder siblings, through the same xlsx_safe module. That part is still not a joke.

Built by vK, who has been paged at 03:00 and did not enjoy it. Companion to linux-audit, which examines, and linux-drift, which notices when the gates move again. Its plans can also be handed to linux-apply, which drops any removal of the ssh service before it runs a thing.

Kneel also at the parish and read at the scriptorium. The Cluster abides. The Loop reconciles. The gate, once opened, is rarely shut.

8

The Communion of Tools

Nothing here works alone, and neither shouldst thou

The fleet is one testament in several books. Each does one job, writes a report thou canst hand to anyone, and refuses to be clever with thy servers.