pfSense¶
pfSense is an open-source firewall and routing platform. dnsweaver manages host override records on pfSense's DNS resolvers via a REST API.
pfSense exposes two DNS resolvers: the DNS Resolver (Unbound, default) and the DNS Forwarder (Dnsmasq). A single dnsweaver pfsense provider covers both — pick the resolver with the ENGINE setting.
Requirements¶
pfSense does not ship a REST API in its base system, and Netgate offers no official one. You must install the community pfSense-pkg-RESTAPI package (REST API v2) on the firewall first — it is the de facto standard and works on both pfSense CE and pfSense Plus:
- Install it from System → Package Manager, then create a key under System → REST API → Keys.
- Enable the target resolver (Services → DNS Resolver, or Services → DNS Forwarder).
- The API key needs permission to manage the target resolver.
- dnsweaver needs network reachability to the pfSense web/GUI port.
Why REST, not SSH?¶
pfSense manages state through config.xml and its own internal state machine, including HA sync over CARP/XMLRPC. Writing to the underlying resolver's config files over SSH would work briefly, then break the moment HA sync, an upgrade, or a backup/restore rewrites config.xml. The REST API is the only supported way to make changes.
If you have a standalone Linux dnsmasq host (not fronted by pfSense), use the dnsmasq provider instead.
Configuration¶
environment:
- DNSWEAVER_INSTANCES=pf
- DNSWEAVER_PF_TYPE=pfsense
- DNSWEAVER_PF_URL=https://pfsense.internal
- DNSWEAVER_PF_API_KEY_FILE=/run/secrets/pfsense_key
- DNSWEAVER_PF_ENGINE=unbound # or dnsmasq
- DNSWEAVER_PF_ZONE=home.example.com
- DNSWEAVER_PF_RECORD_TYPE=A
- DNSWEAVER_PF_TARGET=192.0.2.10
- DNSWEAVER_PF_DOMAINS=*.home.example.com
Configuration Reference¶
| Variable | Required | Default | Description |
|---|---|---|---|
TYPE |
Yes | – | Must be pfsense |
URL |
Yes | – | pfSense base URL (https://pfsense.internal) |
API_KEY |
Yes | – | REST API key (sent as the X-API-Key header) |
API_KEY_FILE |
Alt | – | File-based alternative for Docker/K8s secrets |
ENGINE |
No | unbound |
Target resolver: unbound (DNS Resolver) or dnsmasq (DNS Forwarder) |
ZONE |
No | – | DNS zone for record filtering |
TTL |
No | 300 |
Informational only — host overrides have no per-record TTL |
RECONFIGURE_MODE |
No | per_write |
per_write (apply after every change) or never |
TLS_CA_FILE |
No | – | Path to a PEM CA bundle (for a private CA on pfSense) |
TLS_SKIP_VERIFY |
No | false |
Bypass certificate verification (insecure) |
RECORD_TYPE |
Yes | – | A or AAAA |
TARGET |
Yes | – | IP address |
DOMAINS |
Yes | – | Glob patterns to match |
EXCLUDE_DOMAINS |
No | – | Patterns to exclude |
Record Types (v1)¶
Both engines support A and AAAA host overrides. CNAME and other record types are not implemented in the first release.
Resolver Differences¶
The two pfSense resolvers store host overrides differently, and it matters for dual-stack names:
DNS Resolver (unbound) |
DNS Forwarder (dnsmasq) |
|
|---|---|---|
| IPs per host override | Multiple | Exactly one |
| Dual-stack (A + AAAA) for one name | Supported — both IPs live on one override | Not supported — each (host, domain) must be unique with a single IP |
If you point the dnsmasq engine at a name that already has an IP and try to add a second (e.g. an AAAA alongside an existing A), dnsweaver returns a clear error and recommends the unbound engine for dual-stack.
Ownership Marking¶
pfSense host overrides expose no TXT records, so dnsweaver cannot write its usual _dnsweaver.* ownership record.
Instead, every record dnsweaver creates carries a marker in the pfSense description field:
Two consequences:
- Operator-managed host overrides are always safe. dnsweaver's
List()filters to rows that carry its marker, so orphan cleanup can never touch a host override you created by hand in the pfSense GUI. dnsweaver also refuses to modify an existing(host, domain)override it doesn't own. - Editing a dnsweaver-managed record's description in the GUI unbinds it from dnsweaver. If you want to add a note, keep the
dnsweaver:{instance}prefix and separate your note with|— the parser tolerates that form.
Reconfigure Semantics¶
pfSense requires an explicit "apply" call to reload the resolver after a change.
| Mode | Behavior | When to use |
|---|---|---|
per_write (default) |
Apply after every Create/Delete | Small deployments; correctness over throughput |
never |
Never auto-apply | Large deployments; you apply externally (cron, orchestration) |
TLS¶
pfSense typically presents a self-signed certificate. Prefer one of these approaches (in order of preference):
- Trust the pfSense CA properly — export it from pfSense (System → Cert. Manager → CAs) and point
TLS_CA_FILEat it. - Bypass verification — set
TLS_SKIP_VERIFY=true. dnsweaver will log a WARN at startup. Only acceptable on trusted management networks.
API Endpoints Used¶
For reference, dnsweaver uses these pfSense-pkg-RESTAPI v2 endpoints:
DNS Resolver (Unbound):
- GET /api/v2/services/dns_resolver/host_overrides — list rows
- POST /api/v2/services/dns_resolver/host_override — create
- PATCH /api/v2/services/dns_resolver/host_override — update (merge/remove an IP)
- DELETE /api/v2/services/dns_resolver/host_override?id={id} — delete
- POST /api/v2/services/dns_resolver/apply — apply changes
DNS Forwarder (Dnsmasq):
- GET /api/v2/services/dns_forwarder/host_overrides
- POST /api/v2/services/dns_forwarder/host_override
- PATCH /api/v2/services/dns_forwarder/host_override
- DELETE /api/v2/services/dns_forwarder/host_override?id={id}
- POST /api/v2/services/dns_forwarder/apply
Troubleshooting¶
unauthorized- The API key is wrong, disabled, or lacks permission for the target resolver. Verify under System → REST API → Keys.
pfsense reachable but the {engine} host-override endpoint was not found (is the REST API package installed and the {engine} resolver enabled?)- pfSense answered but the resolver's endpoints are missing. Most often this means the REST API package isn't installed, or the target resolver (DNS Resolver / DNS Forwarder) is disabled.
the dnsmasq engine (DNS Forwarder) stores one IP per host override … Use the unbound engine for dual-stack- You tried to give one name both an A and an AAAA record on the DNS Forwarder, which pfSense doesn't allow. Switch that instance to
ENGINE=unbound. refusing to modify host override … it exists on pfsense but is not managed by dnsweaver- A host override for that
(host, domain)already exists and lacks thednsweaver:{instance}marker. dnsweaver won't overwrite operator-managed rows. Remove or rename the conflicting override, or let dnsweaver own it. pfsense provider requires an IP target- The A/AAAA record's
TARGETis not a parseable IP. Host overrides on both engines resolve names to IPs, so the target must be10.1.2.3or2001:db8::1, not another hostname.