pfsense — a pfSense firewall alias

Blocks on a pfSense firewall by managing the contents of a host-type alias through the pfSense-API package's REST API (v2). One alias holds both IPv4 and IPv6; the alias and the rule blocking on it are yours to create — the kur manages membership only, rendering the alias wholesale from its own ban book on every change and applying.

[kur.sshd]
backend = "pfsense"

[kur.sshd.options]
host = "fw.example.org"
key  = "the-api-key"

pfSense-side setup — required first

Requirements

Settings

Options

| option | default | what | |------------|-------------------|----------------------------------------------| | host | (required) | pfSense host, optionally host:port | | key | (required) | API key, sent as the X-API-Key header | | alias | <prefix>_<name> | the pre-existing host alias to manage | | timeout | 30 | HTTP timeout in seconds | | insecure | 0 | skip TLS certificate verification |

What each operation does

Membership is rendered wholesale: each change PATCHes the full sorted IP list, then applies:

| operation | API traffic | |------------|-------------------------------------------------------------------------------| | init | GET /api/v2/firewall/alias?name=<alias> — verifies auth and the alias | | ban | PATCH /api/v2/firewall/alias with {"id":"<alias>","type":"host","address":[...all banned IPs...]}, then POST /api/v2/firewall/apply | | unban | the same render-and-apply, minus the IP | | list | no API call — the kur's own ban book | | check | same probe as init | | flush | render with an empty list + apply | | re_init | teardown (best effort), init, render the full book + apply | | teardown | render empty + apply (ban book kept for re_init) |

A failed PATCH rolls the kur's book back, so the book and the alias stay agreed.

self_heal

check verifies the API answers and the alias exists — not its contents, and not the rule. But because every mutation re-renders the whole membership from the book, hand-removed entries reappear at the next ban/unban even without a re_init — wholesale rendering is self-correcting in a way the per-entry backends are not.

Gotchas