Files
fips/packaging/freebsd/README.md

4.7 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). On pkg upgrade the services are stopped before the binaries are replaced and started again afterwards if enabled; on pkg delete they 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.

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.