-
Notifications
You must be signed in to change notification settings - Fork 0
Firewall
warden holds the machine's packet filter in whatever that machine actually uses, and every change it makes can be undone by doing nothing.
warden firewall allow ssh --from 10.0.0.0/8
warden firewall allow 8080
warden firewall applyRules are written down first and applied when you say so. warden firewall export prints what the machine would be told, and changes nothing. --for nftables writes for a firewall this machine does not have, which is how a
ruleset gets read on a laptop before it reaches the server it is meant for.
kind |
Used on | How a whole ruleset is applied |
|---|---|---|
nftables |
Linux, by default |
nft -f — one transaction |
iptables |
Linux, asked for by name |
iptables-restore — one transaction |
pf |
macOS, FreeBSD, OpenBSD, NetBSD |
pfctl -f — one ruleset at a time |
windows |
Windows |
netsh, rule by rule, with a snapshot around it |
warden picks by what the machine is. WARDEN_FIREWALL_BACKEND or --for names
one instead — an older Linux box still running iptables-legacy wants
iptables.
Three of the four swap a whole ruleset in one step. Windows Defender Firewall has no such thing, so that backend takes its own snapshot before it starts and puts it back if any rule fails: a half-applied policy is never left standing.
Windows also filters statefully whichever way it is told, so it needs no established-traffic rule — allowing outbound is what lets the answers back. The other three are told explicitly, and warden never writes a ruleset without it:
ct state established,related accept
iif lo accept
Without those two lines a default-drop policy also drops the ssh session that applied it.
$ warden firewall apply
12 rules applied
rolling back in 60s unless you run `warden firewall confirm`The order matters and is not negotiable:
- Snapshot first. Taken after the change, it would describe the change rather than what to go back to.
- Arm the rollback second. Armed after applying, there would be a window where a lost session means a lost machine.
- Apply third. An apply that fails disarms, because nothing was applied and nothing should be waiting to be undone.
The watchdog runs detached — its own session on POSIX, DETACHED_PROCESS on
Windows — so it outlives the ssh session that armed it. That is the whole
point: a rule that locks you out costs a minute, not a drive.
| Command | |
|---|---|
warden firewall confirm |
Keep it, and call off the rollback |
warden firewall restore |
Put it back now, without waiting |
warden firewall status |
Whether a rollback is waiting, and for how long |
--rollback 0 turns the window off, for a machine you are sitting at.
WARDEN_FIREWALL_ROLLBACK sets the default, which is 60 seconds.
Two firewalls managing one machine is one too many, so taking over means reading the other one first and turning it off last.
$ warden firewall adopt
ufw is holding 5 rules
NAME DIR ACTION WHAT FROM ORIGIN UNTIL
allow-22 in allow tcp/22 any adopted -
allow-8000-8100… in allow tcp/8000-8100 10.0.0.0/8 adopted -
deny-3389 in deny tcp/3389 any adopted -
could not read: [ 6] Anywhere on eth0 ALLOW FWD 10.5.0.0/16
those would be lost. Write them by hand first, or say no.
Take over from ufw? [y/N]:-
ufw is read from
ufw status numbered, which is the output it documents. A bare3389means both protocols to ufw, so it becomes two rules rather than half an answer. ufw lists v4 and v6 apart; warden's table isinet, which is both, so the twin is not a second rule. -
firewalld is read from
firewall-cmd --list-all: services resolved against warden's own catalogue, ports and ranges as written, and the zone's source becoming the rule's source.
Anything that cannot be translated is named before anything is applied. A rule quietly dropped in this step is a door quietly left open, or quietly shut.
The other firewall stays enabled while the rollback window is open. Its
rules are not loaded — warden's ruleset replaced them in one step — but the
service is untouched, so rolling back returns the machine exactly as it was.
warden firewall confirm is what finally disables it, and says so:
kept
ufw --force disable
ufw is off
warden firewall allow ssh
warden firewall allow postgres --from 10.0.0.0/8
warden firewall deny smbssh, sftp, ftp, http, https, dns, dhcp, ntp, smtp, imap,
rdp, vnc, smb, postgres, mysql, redis, mongodb, wireguard,
mdns. A name nobody knows suggests the nearest one rather than just refusing.
A bare port number works too, and --proto udp when it is not tcp.
This is the part no other firewall tool can do, and the reason the registry and the firewall are one program: warden knows which port a service holds, for how long, and whether its holder is still there.
$ warden firewall open shop-api
NAME DIR ACTION WHAT FROM ORIGIN UNTIL
allow-shop-api in allow tcp/8300 10.0.0.0/8 registry 58sThe rule takes the port the registry actually handed out, and the lease with it. When the registration lapses or is released, the rule goes:
$ warden firewall list
closed allow-shop-api - its service is gone
no rules yetwarden firewall dev-mode --for 2 opens the whole pool from one network for
two hours, for the afternoon somebody else needs to reach what you are running.
It closes itself, and warden doctor says it is open for as long as it is.
The registry listens on loopback and asks for no token there. If registering a service could open the machine to the network, then anything able to register could open the machine to the network — which is how UPnP became a byword. So a rule whose origin is the registry passes through one place, and that place decides:
-
Registering opens nothing. A person types
warden firewall open. -
The pool is the ceiling. Only ports between
pool_startandpool_end. 22, 3389 and 445 are outside it and stay unreachable — to a mistake, and to a registry somebody else is driving. - Loopback services have nothing to open, and are told so.
-
Off until switched on.
firewall_from_registryisfalse. -
Where from is decided once, by the operator, in
firewall_allow_from. Not by the service. - The lease is the rule's lease. A rule may not outlive it.
-
Every rule says where it came from —
manual,adopted,catalogue,registry— in the listing, the history, the event stream and the webhook. - A development window is bounded and temporary, never a standing hole.
Bounds 2 and 6 carry the weight: one limits what can ever be opened, the other how long. Both are checked in one place, and there is a test for each of the eight that tries to get through it.
Everything above is a person at a keyboard. The thing that most wants to open a port is somewhere else: a deploy that has just registered a service.
from warden import WardenClient
with WardenClient("http://build-01:7010", token="...") as client:
rule = client.firewall_open("shop-api", source="10.0.0.0/8")
client.firewall_apply(rollback=60) # undoes itself unless confirmed
client.firewall_confirm()warden firewall status --on http://build-01:7010
warden firewall list --on http://build-01:7010
warden firewall open shop-api --on http://build-01:7010Reading is what a token already allows. Changing needs one setting on the machine being asked, and it is off out of the box:
warden settings set allow_remote_firewall trueWithout it, every change comes back 403:
changing the firewall over the API is switched off -
set allow_remote_firewall on this warden to allow it
It is deliberately separate from firewall_from_registry. One decides what a
rule may be, the other decides who may ask for one. A machine that never
wants to be asked says so once, rather than relying on the other being off.
warden doctor fails — not warns — a warden that will change its own
firewall on request, listens beyond loopback and asks for no token. That
combination is a way through a firewall rather than a firewall.
A rule asked for over the network is built by the same code as one asked for at the keyboard, so it meets the same eight bounds:
| Asked for | Answer |
|---|---|
a source outside firewall_allow_from
|
403, naming what is allowed |
a service bound to 127.0.0.1
|
403, "there is nothing to open" |
| a port outside the pool | nothing can hold it, so nothing can open it |
| a comment with a newline in it |
422, before it is stored |
| nothing named, and two networks declared |
403 — warden does not pick for you |
| Method | Path | Needs | Purpose |
|---|---|---|---|
GET |
/v1/firewall |
token | Backend, whether it is available, rule counts, what is waiting to roll back |
GET |
/v1/firewall/rules |
token | Every rule, ?origin= to filter |
POST |
/v1/firewall/open |
token + switch | The port a registered service holds |
DELETE |
/v1/firewall/rules/{name} |
token + switch | Take one back out |
POST |
/v1/firewall/apply |
token + switch | Make them true, ?rollback= in seconds |
POST |
/v1/firewall/confirm |
token + switch | Keep what was applied |
POST |
/v1/firewall/restore |
token + switch | Put a snapshot back, ?snapshot=
|
The rollback matters more here than at a keyboard: the caller is somewhere
else, and a rule that shuts the door shuts it on them. apply arms it, a
detached watchdog counts it down, and nothing is kept until confirm.
One firewall at a time is what nobody does at forty machines. The hub sees all of them and can drive all of them:
warden firewall status --all # one line per node
warden firewall list --all # every rule anywhere, with its node
warden firewall open shop-api --all # on every node that holds it
warden firewall apply --fleet --rollback 120
warden firewall confirm --fleet$ warden firewall status --all
NODE BACKEND RULES REGISTRY REMOTE ROLLING BACK
build-01 nftables 12 3 yes 2m
hub nftables 8 0 yes -
web-02 iptables 0 0 no -
db-03 (http://db-03:7010) could not be reached
A node that did not answer is named rather than quietly left out — the same rule the rest of the fleet views follow.
$ warden firewall apply --fleet --rollback 120
NODE RESULT DETAIL
build-01 applied 12 rules, rolling back at 14:22:31
db-03 refused changing the firewall over the API is switched off
hub applied 8 rules, rolling back at 14:22:31
web-02 applied 4 rules, rolling back at 14:22:31
3 of 4 applied
`warden firewall confirm --fleet` keeps them; anything not confirmed puts itself back
Every node does it to its own firewall: its own snapshot, its own ruleset, its own watchdog counting down on its own machine. Nobody waits for anybody, and a node that refuses does not stop the rest.
A fleet-wide apply will not give up its rollback. --rollback 0 is refused,
and it is the only place in warden where leaving the window out is not allowed.
One wrong rule across forty machines is forty machines you cannot reach, and the
watchdog on each of them is the only thing that opens them again without
somebody driving there.
Confirming is a separate act. The hub never confirms on its own, however cleanly every node reported — the point of the window is that a person checks they can still reach the machines. Anything left unconfirmed comes back by itself.
$ warden firewall open shop-api --all
NODE RESULT DETAIL
build-01 opened allow-shop-api - 8000 from 10.0.0.0/8
hub skipped does not hold shop-api
web-02 opened allow-shop-api - 8143 from 10.0.0.0/8
A name means a different port on every machine, and nothing at all on most of them. The hub asks who holds it first; a node that never registered it is skipped and said so rather than treated as a failure. Each node still resolves its own port and its own lease, and each rule still meets every bound.
warden firewall open shop-api --node build-01
warden firewall delete allow-shop-api --node build-01
warden firewall apply --node build-01 --rollback 120
warden firewall confirm --node build-01--node goes through the hub. --on http://host:7010 goes straight to one
warden without a hub in the middle. Both end up asking the same machine, and
that machine decides.
-
It cannot overrule
allow_remote_firewall. A node with it off refuses, and the refusal is printed in that node's own words. -
It cannot send a rule.
opennames a service; which port that is, and for how long, is the node's own answer. - It cannot get round the eight bounds. They are checked where the rule is built, which is on the node.
- It cannot confirm for you. See above.
warden closes the rule in its own book straight away, so it can never be
applied again. The kernel still holds it until the next warden firewall apply, and warden doctor says so. Re-applying a firewall without being asked
is not something warden does quietly.
Root, or an elevated prompt on Windows. Every backend says which it is rather than failing obscurely:
nft refused: Operation not permitted - a firewall needs root
netsh refused: a firewall needs an elevated prompt on Windows
warden — nothing binds a port without asking ·
uv tool install warden-ports
Repository · Issues · Releases · PyPI · MIT
Getting started
While it runs
Several machines
Reference