· 2 min read · firewall · iptables
An applied block is not a verified block
Why atalince separates request, application and firewall read, and what can wipe a rule without anyone noticing.
Almost every blocking tool works the same way: it detects an attack, runs a command against the firewall and marks the IP as blocked. The problem is the last part. “I ran the command” and “the rule is there right now” are different statements, and the gap between them is wider than it looks.
What wipes a rule without warning
On a real server, plenty of things touch the firewall besides your blocking tool:
- Other tools’ reloads. ufw, firewalld and CSF rebuild their own chains when they reload. Depending on how they do it, a jump another tool inserted into
INPUTdisappears with them. - Fights over position. Tools that want to sit at the top of
INPUTcan swap places in cycles. It happened to atalince itself and mailcow, which swapped places every twenty seconds until agent 1.0.2 fixed it. - Server reboots. iptables rules don’t survive a boot on their own.
- Different families. A block that exists for IPv4 protects nothing if the attack comes over IPv6 and the firewall for that family isn’t available.
In every one of these cases, the tool’s dashboard keeps saying “blocked”. Nobody lied on purpose; nobody went back to check.
Four states, not one
atalince shows each block as a trail with four steps:
- Detected: the detector read the log and identified the attack.
- Block requested: the dashboard asked that server’s agent.
- Applied, reported by agent: the agent applied it and sent a receipt.
- Firewall read verified: a kernel read confirmed the rule is there.
Steps 3 and 4 look alike, but they aren’t. The receipt says the command ran without error. The read says that, at the moment the agent looked, the block was standing.
What the read checks
Every hour, the dashboard asks for a read of the active blocks. The agent applies whatever is pending, repairs the jumps and then reads the kernel, checking three things for each block:
INPUTjumps to theATALINCE-BANSchain;- the chain has the set’s drop rule (
-m set --match-set atalince-bans src -j DROP); - the IP is inside the
atalince-bansset.
If one of them fails, the dashboard says which. “INPUT does not go through the block chain” is a very different message from “the IP is not in the set”, and each one calls for a different action. When a reload wiped the jump, the agent notices and restores it on its own.
Unknown is not success
The most important rule of the trail is about what happens when information is missing. If the receipt didn’t arrive, or the read couldn’t be done, the state stays unknown. The dashboard spells it out: “No evidence; this is not success.”
It looks like an interface detail, but it’s a product decision. A dashboard that rounds the unknown up is a dashboard you can’t trust on the day you need it most.
In practice
On the blocks page, every row shows the current state, and the detail opens the trail with the time of each step. If you want to know how many blocks are actually confirmed right now, that’s the number that matters, not the total of requests.
Read more about how it works in Protection.