The FreeBSD package wrote the daemon's output to /var/log/fips.log and never rotated it, so the log grew without bound. Ship a newsyslog entry in /usr/local/etc/newsyslog.conf.d, which the stock newsyslog.conf includes: five bzip2-compressed generations of 1000 KB, mode 600 to match the file daemon(8) creates. An entry alone would not work, because daemon(8) keeps the -o file open and reopens it on SIGHUP only when started with -H, and only the supervisor handles that signal. The rc script now starts daemon(8) with -H and records the supervisor's pid in /var/run/fips/daemon.pid, which is the pid file the entry signals. The child pidfile that rc.subr uses for stop and status is unchanged. Add a lib test that checks the rc script, the newsyslog entry and the package staging agree on the log path, the signalled pid file and the installed location. The FreeBSD package smoke install now also confirms the stock newsyslog configuration picks up the shipped entry, starts the daemon, forces a rotation of its log, and checks that daemon(8) closes the rotated file and holds the new one open. This exercises the SIGHUP reopen that the static check of the packaging files cannot.
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).
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.fipsquery.do-ip6: yes— the daemon binds::1only.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.confmust list a loopback nameserver (ideally only127.0.0.1), or nothing ever queries unbound and.fipscannot 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 edns0from/etc/resolv.confif present, and setresolv_conf_options=""in/etc/resolvconf.confso resolvconf(8) does not re-add it — against an EDNS-broken router it breaks libc resolution outright. - Never run
local-unbound-setupwith 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.