Files
fips/packaging/freebsd/README.md
Johnathan Corgan 4cab546c61 Merge maint into master: the first v0.5.4 batch
Bloom filter, session, native API, DNS and TUN security fixes, the
held over-cap filter, and the Debian, FreeBSD, Windows and OpenWrt
packaging fixes.

The DNS responder's child moved to src/ipv6tun/lifecycle.rs on master,
so the mesh-side filter input (dns_mesh_side and the fallback warning)
is applied there rather than in the node lifecycle, and the IPV6_PKTINFO
helper becomes crate-visible in src/ipv6tun/dns.rs beside the socket
helpers master already moved there. The identity store's session
fallback reads master's proven peer key (SessionEntry::peer_key). The
bloom recompute counter the root crate's tests read is a test-hooks
item, since fips-core is its own crate here. The changelog entries go
into master's Unreleased sections by category.
2026-10-23 01:23:22 +00:00

5.6 KiB

FIPS FreeBSD packaging

Builds a native FreeBSD .pkg shipping fips, fipsctl, fipstop, rc.d services, and .fips DNS integration. fips-gateway is excluded (its NAT backend is nftables, Linux-only).

On pfSense? Use packaging/pfsense/ instead. pfSense is FreeBSD underneath, but it boots packages, wires up DNS and handles upgrades differently enough that this package does not work there; the pfSense one addresses each difference.

Platform notes: the Ethernet and BLE transports are not available on FreeBSD (UDP, TCP, Tor, and Nym are). The UDP datapath deliberately uses the portable single-packet receive loop — FreeBSD's recvmmsg(2) is a libc loop over recvmsg, not a kernel batch, so the Linux/macOS batched arm would gain nothing — and the connected-UDP fast path is not compiled pending FreeBSD-specific SO_REUSEPORT validation.

Build

./packaging/freebsd/build-pkg.sh            # cargo build --release + pkg create
./packaging/freebsd/build-pkg.sh --no-build # package existing release binaries

Output: deploy/fips-<version>-freebsd-<arch>.pkg. pkg versions cannot contain - or +, so both are mapped to .: a Cargo version of <x.y.z>-dev becomes fips-<x.y.z>.dev-freebsd-amd64.pkg.

Install

pkg add ./deploy/fips-<version>-freebsd-<arch>.pkg
# post-install seeds this from the sample if absent, at mode 0600
vi /usr/local/etc/fips/fips.yaml
sysrc fips_enable=YES fips_dns_enable=YES
service fips start
service fips_dns start
fipsctl show status

Config installs sample-style (@sample semantics via manifest scripts), so an edited fips.yaml survives upgrade/removal. fips.yaml is installed 0600 — it may hold the node's private key (nsec:). The daemon runs under daemon(8) with pidfile /var/run/fips/fips.pid and logs to /var/log/fips.log (rc.conf knobs: fips_config, fips_flags, fips_logfile).

The log is rotated by /usr/local/etc/newsyslog.conf.d/fips.conf: five bzip2-compressed generations of 1000 KB. daemon(8) runs with -H and records its own pid in /var/run/fips/daemon.pid; newsyslog signals that pid after the rename, and daemon(8) reopens the log. The entry covers the default fips_logfile only, so a log moved elsewhere needs its own newsyslog entry. The entry belongs to the package and is replaced on upgrade.

The package creates a fips group; members can run fipsctl and fipstop without root (pw groupmod fips -m <user>, then re-login). An upgrade (pkg upgrade, or pkg install of a newer package file) leaves the running daemon alone: it keeps running the previous binary until you run service fips restart, and the package prints that instruction. service fips restart also starts a daemon that is not running. If you use it, run service fips_dns restart too. The package does not restart the daemon itself because pkg 2.8.4 ends a process that a package script starts once pkg exits (measured with pkg install of a package file). Packages before 0.5.4 stop both services on upgrade, so an upgrade from one of them leaves the daemon stopped until the same restart. On pkg delete the services are stopped and the .fips resolver drop-in is removed.

.fips DNS integration

The daemon answers .fips queries on [::1]:5354. fips_dns points the system resolver's fips. zone there; backends tried in order: base local_unbound (drop-in /var/unbound/conf.d/fips.conf), pkg unbound, pkg dnsmasq. The unbound drop-in must (and does) set:

  • do-not-query-localhost: no — unbound's default silently refuses loopback forwarders, SERVFAILing every .fips query.
  • do-ip6: yes — the daemon binds ::1 only.
  • domain-insecure: "fips." — the zone is unsigned.

One-time host resolver setup (NOT automated)

The package configures the fips. zone only; making the local resolver the system resolver is an operator decision. On a typical box:

sysrc local_unbound_enable=YES
local-unbound-setup 1.1.1.1 9.9.9.9   # explicit upstreams — see below
service local_unbound restart

Field-tested caveats (fips-dns-setup detects and warns about each):

  • /etc/resolv.conf must list a loopback nameserver (ideally only 127.0.0.1), or nothing ever queries unbound and .fips cannot resolve.
  • Do not use a home-router DNS proxy as unbound's upstream. Many CPE forwarders are EDNS-broken; unbound always sends EDNS, so every public query SERVFAILs. Forward to real resolvers (ISP or public).
  • Remove options edns0 from /etc/resolv.conf if present, and set resolv_conf_options="" in /etc/resolvconf.conf so resolvconf(8) does not re-add it — against an EDNS-broken router it breaks libc resolution outright.
  • Never run local-unbound-setup with no arguments while resolv.conf already points at 127.0.0.1 — it snapshots that as upstream and forwards unbound to itself.

Upgrade test

test-upgrade.sh checks that pkg install of a newer package leaves a running daemon alone, prints the restart instruction, and that service fips restart then runs the new binary. Run it as root on a FreeBSD host with the package from this tree installed; it builds a 99.0.0 package from the same binaries to upgrade to. The FreeBSD Package workflow runs it in its VM leg.

Debugging

drill -p 5354 <npub>.fips @::1 AAAA   # daemon directly (bypasses unbound)
drill <npub>.fips AAAA                # full chain; SERVER: must be 127.0.0.1
cat /var/run/fips/dns-backend         # which backend fips_dns configured

The TUN interface gets a kernel-assigned name (tun0, tun1, ...), like utun on macOS, and is destroyed automatically when the daemon exits. ifconfig <name> prints Opened by PID <n> for the process holding a tun device.