nantevodocsnantevo-fwd

nantevo-fwd guide

nantevo-fwd (ForWarDer · FireWall DNS) is a standalone DNS-to-DoH secure forwarder written in Rust. It accepts plaintext DNS on UDP port 53 from devices that can't speak DoH themselves — servers, printers, SCADA and OT equipment, IoT, legacy hardware — and forwards each device's queries through its own authenticated Nantevo DoH upstream, with local threat filtering applied before anything leaves your network.

What nantevo-fwd is

A single statically-linked binary with no runtime dependencies. It maps each source IP on your network to a unique Nantevo DoH upstream, which gives you per-device attribution on hardware that has never heard of DoH — static IPs and DHCP reservations are the identity mechanism.

Three things happen to every query before it leaves your network:

  1. Authentication mapping — the source IP is matched to its configured client mapping. Unmapped sources get SERVFAIL, immediately (see runtime behavior).
  2. Local filtering — the query is checked against three locally-loaded threat intelligence layers. Blocked domains answer NXDOMAIN without ever being forwarded.
  3. Privacy hardening — ECS stripping, EDNS padding (RFC 8467), randomized query IDs, suppressed user agent, and HTTP/2 frame padding are applied to the outgoing DoH request.

Requirements

ItemDetail
PlatformsFreeBSD amd64 · Linux amd64 / aarch64 · macOS arm64 / amd64 (development only) · any Rust target
Memory~300 MB at full blocklist load; comfortable on a Raspberry Pi 4
NetworkBinds UDP :53 for client queries; HTTPS (443) outbound to your Nantevo DoH endpoints
PrivilegesRuns as an unprivileged user; on Linux, CAP_NET_BIND_SERVICE grants the port-53 bind
BootstrapOne plaintext DNS query at startup to resolve its own DoH endpoint — the only plaintext query it ever makes. Auditable and logged.
ConfigurationA single TOML file, passed with --config
NoteBinaries and your organization's configuration bundle are provided during design-partner onboarding. Contact [email protected] if you need access.

Deployment

FreeBSD — rc.d service

nantevo-fwd ships with a complete rc.d service script for FreeBSD 13+ and 15. It starts at boot, restarts on failure, and logs to syslog — the same deployment model used by the Nantevo cloud resolver nodes.

# /etc/rc.conf
nantevo_fwd_enable="YES"

service nantevo-fwd start

FreeBSD — BastilleBSD jail

For kernel-enforced process isolation, deploy inside a BastilleBSD jail with a dedicated IP and separate network namespace. Zero overhead versus bare metal.

bastille create fwd-01 15.0-RELEASE 10.0.0.53
bastille template fwd-01 nantevo/fwd-template

Linux — systemd service

A systemd unit file is included for any modern distribution (Ubuntu, Debian, RHEL, Alpine, and others). Tested on amd64 and aarch64.

systemctl enable nantevo-fwd
systemctl start nantevo-fwd

# follow logs
journalctl -u nantevo-fwd -f

ARM single-board computers

Tested in production on Raspberry Pi 4 (64-bit Raspberry Pi OS). The ~300 MB footprint leaves ample headroom for other services — an ideal branch-office or home-lab edge deployment for network-wide IoT and legacy coverage.

./nantevo-fwd --config fwd.toml

macOS — development and testing

Compiles and runs on Apple Silicon and Intel Macs for configuration testing and client-mapping validation before pushing to FreeBSD or Linux nodes. Not a recommended production platform.

cargo build --release
sudo ./target/release/nantevo-fwd --config fwd.toml

Client mapping

Attribution is driven by source IP. Each device you want individually attributed needs a stable address:

# conceptually: one source IP → one authenticated upstream
client 10.17.89.53  →  nantevo-endpoint-A
client 10.17.89.18  →  nantevo-endpoint-B
       10.17.89.99  →  SERVFAIL  (not enrolled)
NoteThe full fwd.toml configuration reference — including the client-mapping syntax — is distributed with your onboarding bundle rather than published here, because mappings embed your private per-client endpoints and ClientIDs.

Local threat intelligence

Three independent blocking layers are enforced locally, before any query is forwarded upstream. Your devices stay protected even during the forwarding hop.

LayerSourceScaleAction
Domain blocklist (TIF)HaGeZi Threat Intelligence Feed — malware, C2, phishing, tracking. Refreshes every 24h.~1.18M domainsBlock — answers NXDOMAIN
IP reputation (DROP + XBL)Spamhaus DROP (hijacked netblocks) + XBL DQS (real-time botnet / compromised-host lookup). Blocks responses resolving to known-malicious infrastructure.~15.16M IPv4 (58,231 exact + 1,714 CIDR ranges, +96 IPv6 subnets)Block
NRD observe (Stamus)Newly Registered Domain tracking for DGA and phishing patterns.~2.29M domainsFlag only — never blocked. Feeds RoCi's async analysis.

Threat events are ranked by severity: Critical (XBL — botnet / compromised hosts) → High (DROP — hijacked netblocks) → Medium (TIF — domain blocklist) → Policy (Nantevo upstream policy block).

Safe search enforcement

Optional DNS-level safe search rewrites resolution for Google, YouTube, Bing, DuckDuckGo, Yandex, Qwant, and Pixabay to their safe-search endpoints. Configured once on the forwarder (safe_search: strict or moderate), it applies to every device on the network — including those that can't be individually configured.

Runtime behavior

Fail closed

Any source IP without a client mapping receives SERVFAIL. There is no silent forwarding and no fallback to an unprotected resolver — a device that isn't enrolled cannot resolve DNS through the forwarder, period. Unmapped attempts are not forwarded and not logged as queries; they appear only in the Unknown Devices view.

Caching and prefetch

Each enrolled client gets an independent cache with configurable TTL bounds (e.g. max_ttl: 3600). Prefetching refreshes popular records before expiry to keep hit rates high — warm-cache responses return in under 5 ms. Per-client cache hit rates and latency distributions are visible on the Fleet Overview.

DNSSEC

Three validation modes, selectable per deployment: off, opportunistic (validate where supported, pass through unsigned records), and strict (validate everything, fail closed with SERVFAIL on validation errors). RRSIG consistency checks run locally before responses are served from cache.

Privacy hardening on the upstream hop

Dashboard & metrics

The read-only dashboard is embedded in the binary at /dashboard — no separate web server or external dependencies. It provides:

Every tracked metric is also exported natively to Prometheus at /metrics:

nantevo_queries_total{client="mixpost"}
nantevo_blocked_total{src="tif"}
nantevo_latency_p95{client="conduit"}

Pointing the network at it

Set your DHCP server's DNS option to the host running nantevo-fwd. Every device on the network — regardless of OS, age, or capability — immediately routes through the forwarder with no client-side changes.

# ISC DHCP / dnsmasq
option domain-name-servers 10.22.22.1;
# nantevo-fwd listens on :53
Heads upRemember the two-phase bootstrap: the forwarder needs to resolve its own DoH endpoint once at startup. If you firewall the forwarder host aggressively, permit that initial resolution path or startup will stall.