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

132 lines
5.6 KiB
Markdown

# 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/`](../pfsense/README.md)
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
```sh
./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
```sh
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:
```sh
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
```sh
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.