9.3 Dynamic blocklists — apiban and ipban¶
[!IMPORTANT] If you started from the stock
kamailio.cfg, you are already running a dynamic blocklist — the default config greylists flooders with no module you'd recognise as "the blocklist." This chapter opens that machinery: the self-expiring ban the stock config ships, then how an external reputation feed extends it to IPs that have never hit you yet.
The antiflood you already shipped¶
The stock config wires 9.2's pike into an htable-backed ban set in a handful of lines. The table:
A named htable (8.3 htable) ipban, a hash map in shm. The load-bearing param is autoexpire=300: every entry self-deletes 300 s after it's written — nobody cleans up, the ban forgets. Then, in route[REQINIT] (near the top of request_route), two checks back to back:
if ($sht(ipban=>$si) != $null) { # already banned? drop, spend nothing else
exit;
}
if (!pike_check_req()) { # not banned — let pike count this packet
$sht(ipban=>$si) = 1; # flood verdict → create the key
exit;
}
The order is the mechanism. The $sht(ipban=>$si) lookup runs first — a banned $si is dropped before sanity, before the rest of the parse, before any real work. Only unbanned sources reach pike_check_req() (9.2: 1 ok, -1/-2 flooding); a flood verdict writes $sht(ipban=>$si) = 1, and that assignment is the ban — the next packet hits the top lookup and dies. pike detects, ipban remembers, the lookup enforces, autoexpire releases — htable + pike composed into a self-expiring blocklist with no external dependency and no DB. The first over-limit packet pays for a pike check; every later one for the next 300 s costs a hash lookup and a drop.
Why self-expiring, not permanent¶
A public source IP is not a person — it's a NAT gateway, a CGNAT pool, a corporate egress, a mobile APN, shared by hundreds of legitimate users alongside whatever tripped your limit. A permanent ban eventually locks out a real customer behind the same address as a scanner, with no recovery but a human editing a list. autoexpire=300 makes the ban a cooldown: the flooder is shut out for five minutes, a real client behind the same NAT waits at most five, self-healing with zero operator involvement. The honest cost: a determined attacker rotates source IPs, and your table fills with five-minute-old entries it has already abandoned. Per-source rate limiting answers "is this IP misbehaving now" — it has no opinion on an IP it has never seen. That gap is what a reputation feed fills.
apiban — borrowed reputation¶
apiban (apiban.org) is a free external REST service distributing a curated list of IPs already caught attacking other operators, collected via a network of SIP honeypots. You request an API key and poll its HTTPS endpoint for JSON. It is not a Kamailio module — there is no modparam("apiban", ...). It is a feed; you choose where to act on it.
- Kernel — the standalone client. apiban ships Go and bash clients with iptables / nftables /
ipset/ fail2ban integrations that poll on acrontabtimer and program the firewall (the iptables client maintains a dedicatedAPIBANchain). Packets from a listed IP are dropped by the kernel before Kamailio parses a byte — beforepike, before theipbanlookup. - In-route — feed pulled into an
htable. No Kamailio client exists for this; you build it: poll the same JSON withhttp_clienton anrtimerclock and write each IP into anhtable— the sameipbantable, populated from the feed instead ofpike. Your existing$sht(...)check then blocks feed IPs and locally-detected flooders with one line, now after parse.
| Layer | Per-packet cost | Flexibility | Use it for |
|---|---|---|---|
Kernel — ipset/nftables/iptables (apiban client) |
lowest — drop before any SIP parse | low — pure IP yes/no, no SIP context | blunt volume: reputation feeds, obvious flood IPs |
In-Kamailio — ipban htable + pike |
a hash lookup, after parse | high — per-route logic, "challenge not drop", dmq-shared |
behaviour-driven greylisting; decisions needing SIP context |
Complements, not rivals: push the high-volume, no-judgement reputation list to the kernel where each drop is nearly free; keep the behaviour-driven greylist — the bans your own traffic earned — in Kamailio where you have context. Across instances, ipban is exactly the state 8.5 dmq replicates: detect a flooder on one node, ban it cluster-wide. And the quiet scanners that probe one well-formed request at a time carry the payload of 9.4 — a feed that already saw them elsewhere is your cheapest defence against them landing here.
← Table of contents · ← 9.2 Defensive modules · Next: 9.4 Fuzzing & RCE →