Kernel firewall (nf_tables)¶
By default SIPhon drops banned sources in userspace: the transport ACL rejects
them at recv()/accept(), before any SIP parsing. That protects your handlers,
but the packet still travels NIC → kernel → SIPhon before being dropped, so it does
nothing against volume.
security.firewall mirrors SIPhon's bans into a kernel nf_tables set so the
kernel drops the traffic before it reaches SIPhon's socket. It's self-contained
(no nft shell-out, no daemon, no log scraping) — SIPhon programs the set directly
over netlink and the kernel auto-expires each ban via a per-element timeout.
What gets dropped¶
The same sources SIPhon already bans, now enforced in the kernel:
- Auto-ban — the confidence-weighted
failed_auth_banstore. Each ban is pushed to the kernel with the same TTL as the in-memory ban, so both expire in lockstep. - APIBAN — every IP from the APIBAN community blocklist, added permanently (the blocklist carries no per-IP lifetime).
trusted_cidrs are never banned, so they're never in the set.
Enable it¶
security:
failed_auth_ban: # (or apiban:) — the source of bans
threshold: 10
window_secs: 600
ban_duration_secs: 3600
firewall: {} # that's it — every field below defaults
# table: "siphon" # nf_tables table SIPhon owns (family inet)
# chain: "input" # base chain SIPhon adds the drop rules to
# set_v4: "banned4" # IPv4 ban set
# set_v6: "banned6" # IPv6 ban set
# manage_rule: true # SIPhon owns the chain + drop rules too (see below)
firewall: {} is enough. On startup SIPhon creates the inet siphon table, the
two timeout sets, a base chain, and the drop rules that reference them — all
idempotent and safe across restarts. Nothing else to configure.
Grant CAP_NET_ADMIN¶
Programming nf_tables needs CAP_NET_ADMIN (the same capability the IMS P-CSCF
IPsec path uses). Without it SIPhon logs a warning and falls back to the userspace
ACL — the feature is never fatal, it just doesn't reach the kernel.
=== "systemd"
```ini
[Service]
AmbientCapabilities=CAP_NET_ADMIN
```
=== "Docker"
```bash
docker run --cap-add=NET_ADMIN siphon-sip ...
```
=== "Kubernetes"
```yaml
securityContext:
capabilities:
add: ["NET_ADMIN"]
```
Zero-touch by default¶
With manage_rule: true (the default) SIPhon owns the whole ruleset — table,
sets, base chain, and the two drop rules — so enabling firewall is all you do.
On startup it installs, in the inet siphon table:
chain input {
type filter hook input priority filter; policy accept;
ip saddr @banned4 drop
ip6 saddr @banned6 drop
}
Banned sources are dropped in-kernel from the first ban; SIPhon keeps the set contents current with per-element timeouts. The whole setup is one atomic nf_tables transaction: every object is declared idempotently, and the chain's rules are flushed and re-appended inside the same transaction — so a first run, a clean restart, and a restart after a crash all converge to exactly this ruleset, with no duplicated rules and no instant where the drop rule is absent.
Bring your own rule (manage_rule: false)¶
If you already manage nftables and want to place the drop yourself, set
manage_rule: false. SIPhon then maintains only the sets, and you reference
them from your own ruleset:
table inet siphon {
chain input {
type filter hook input priority filter; policy accept;
ip saddr @banned4 drop
ip6 saddr @banned6 drop
}
}
Renaming or disabling: clean up the old objects yourself¶
SIPhon only ever creates its objects — it never deletes a table or chain it
finds, because it can't know the objects aren't yours. So if you rename table
/ chain in the config, flip manage_rule off, or remove firewall: entirely,
the previously created table (and its drop rules) stays in the kernel and keeps
dropping whatever is still in its sets — including permanent APIBAN entries that
never expire. Remove it explicitly:
Containers: use nftables, not XDP¶
Most SIPhon binaries run in containers, and this is the right tool there. nftables
runs in the pod's network namespace; CAP_NET_ADMIN is grantable per-pod
without host privilege, and it works on any CNI, veth, or cloud vNIC.
XDP is not the tool here. From inside a pod you can't attach XDP to the host NIC
(that's the node/CNI's job), veth and cloud vNICs fall back to generic-mode XDP
(no faster than nftables), it needs CAP_BPF/bpffs, and it collides with CNIs that
already own XDP (Cilium). Line-rate volumetric scrubbing belongs at the edge /
host / CNI, not in the SIPhon container. (SIPhon's XDP story is on the media
plane, where packet rates justify it.)
One honest limit: from inside a container, neither nftables nor XDP drops at the
host NIC — the packet has already crossed host → CNI → veth. nftables still drops
it before SIPhon's userspace, which is the win here; true volumetric defense is
an edge/upstream concern.
Verify & troubleshoot¶
Confirm the sets and their live contents:
nft list ruleset
# table inet siphon {
# set banned4 { type ipv4_addr; flags timeout; elements = { 203.0.113.5 timeout 1h expires 59m } }
# ...
# }
- Nothing in the sets? Check that
failed_auth_banand/orapibanare configured — the firewall only mirrors bans those produce. Trigger a few failed auths and watch the set fill. kernel firewall (nf_tables) unavailablein the logs? SIPhon couldn't program the sets — almost always a missingCAP_NET_ADMIN. It's running on the userspace ACL until you grant it.- Bans not dropping? Confirm the
chain input+@banned4/@banned6 droprules are present innft list ruleset. With the defaultmanage_rule: trueSIPhon installs them; withmanage_rule: falseyou add them yourself (see above).
Two counters on /metrics cover the runtime failure modes:
| Metric | Alert when | Meaning |
|---|---|---|
siphon_firewall_command_failures_total |
any sustained rate() > 0 |
Bans are not reaching the kernel (ruleset deleted out from under SIPhon, capability lost). Userspace ACL is the only enforcement left. |
siphon_firewall_commands_dropped_total |
sustained rate() > 0 |
A ban storm is outrunning the netlink actor's queue; kernel enforcement lags the ban rate (userspace ACL still enforces every ban). |
Lifting a false-positive ban¶
If a legitimate source gets auto-banned, lift it through the admin API
rather than editing nft by hand — that keeps the userspace ban and the kernel
set in sync:
curl http://127.0.0.1:9091/admin/bans # list active bans
curl -X DELETE http://127.0.0.1:9091/admin/bans/203.0.113.5 # lift one
DELETE /admin/bans/{ip} clears the userspace ban and, when the kernel firewall
is enabled, removes the matching nf_tables element in the same step. (A ban left
alone expires on its own; both the userspace store and the kernel element use the
same TTL.)
See also¶
- How the scoring works — what earns a ban and how fast.
- Monitoring — the
siphon_banned_ipsgauge tracks the active ban count.