Add macOS support, fix bloom filter routing and MMP intervals

macOS platform:
- Platform-native TUN interface management with shutdown pipe
- Raw Ethernet transport with macOS socket backend (socket_macos.rs)
- EthernetTransport and TransportHandle::Ethernet ungated from Linux-only
- macOS .pkg packaging (build-pkg.sh, launchd plist, uninstall script)
- CI: macOS build and unit test jobs; x86_64 cross-compiled from
  macos-latest via rustup target add x86_64-apple-darwin

Gateway feature flag:
- New opt-in `gateway` Cargo feature activates optional `rustables` dep
- `pub mod gateway` and `Config.gateway` gated behind the feature so
  macOS builds never pull in Linux-only nftables bindings
- `fips-gateway` bin has `required-features = ["gateway"]`
- All Linux/OpenWrt/AUR packaging passes `--features gateway`

CI / packaging:
- package-linux, package-macos, package-openwrt now trigger on push to
  master/maint/next and on pull requests; release uploads remain tag-gated
- Bloom filter routing fix: fall through to tree routing when no candidate
  is strictly closer
- MMP intervals: raise MIN to 1s / MAX to 5s with 5-sample cold-start phase
This commit is contained in:
Origami74
2026-04-09 20:03:42 +01:00
parent 1e4f375dcc
commit e693f4fb7e
35 changed files with 2552 additions and 636 deletions
+5 -1
View File
@@ -8,6 +8,7 @@
# make tarball Build a systemd install tarball
# make ipk Build an OpenWrt .ipk package
# make aur Build fips-git AUR package and validate with namcap
# make pkg Build a macOS .pkg installer
# make all Build deb and tarball (default)
# make clean Remove deploy/ directory
@@ -15,7 +16,7 @@ SHELL := /bin/bash
PACKAGING_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
PROJECT_ROOT := $(abspath $(PACKAGING_DIR)/..)
.PHONY: all deb tarball ipk aur clean
.PHONY: all deb tarball ipk aur pkg clean
all: deb tarball
@@ -31,5 +32,8 @@ ipk:
aur:
@bash $(PACKAGING_DIR)/aur/build-aur.sh
pkg:
@bash $(PACKAGING_DIR)/macos/build-pkg.sh
clean:
rm -rf $(PROJECT_ROOT)/deploy
+21
View File
@@ -10,6 +10,7 @@ make deb # Debian/Ubuntu .deb
make tarball # systemd install tarball
make ipk # OpenWrt .ipk
make aur # Arch Linux AUR package (fips-git, local build + namcap)
make pkg # macOS .pkg installer
make all # deb + tarball (default)
```
@@ -20,6 +21,7 @@ packaging/
aur/ Arch Linux AUR packaging (PKGBUILD, supporting files)
common/ Shared assets (default config, hosts file)
debian/ Debian/Ubuntu .deb packaging via cargo-deb
macos/ macOS .pkg installer via pkgbuild
systemd/ Generic Linux systemd tarball packaging
openwrt/ OpenWrt .ipk packaging via cargo-zigbuild
```
@@ -80,6 +82,25 @@ bash packaging/openwrt/build-ipk.sh --arch mipsel
See [openwrt/README.md](openwrt/README.md) for router-specific
installation instructions.
### macOS (`.pkg`)
Built with `pkgbuild` (included with Xcode command-line tools). Installs
binaries to `/usr/local/bin/`, config to `/usr/local/etc/fips/`, sets up
the `/etc/resolver/fips` DNS resolver for `.fips` domains, and loads a
launchd daemon. The TUN device is named `utun<N>` (kernel-assigned)
rather than `fips0`.
```sh
# Build
make pkg
# Install
sudo installer -pkg deploy/fips-<version>-macos-<arch>.pkg -target /
# Remove
sudo packaging/macos/uninstall.sh
```
### Arch Linux (AUR)
Two AUR packages are maintained: `fips` (release, builds from tagged
+2 -2
View File
@@ -29,13 +29,13 @@ build() {
cd "$pkgname-$pkgver"
export CARGO_TARGET_DIR=target
export SOURCE_DATE_EPOCH=$(stat -c %Y Cargo.toml)
cargo build --frozen --release
cargo build --frozen --release --features gateway
}
check() {
cd "$pkgname-$pkgver"
export CARGO_TARGET_DIR=target
cargo test --frozen --lib
cargo test --frozen --lib --features gateway
}
package() {
+1 -1
View File
@@ -66,7 +66,7 @@ transports:
peers: []
# Static peers for bootstrapping (UDP or TCP):
# - npub: "npub1..."
# - npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
# alias: "gateway"
# addresses:
# - transport: udp
+1 -1
View File
@@ -71,7 +71,7 @@ echo "Building .deb package..."
OUTPUT_DIR="$(mktemp -d)"
trap 'rm -rf "${OUTPUT_DIR}"' EXIT
cargo_args=(deb --output "${OUTPUT_DIR}")
cargo_args=(deb --output "${OUTPUT_DIR}" --features gateway)
if [[ -n "${TARGET_TRIPLE}" ]]; then
cargo_args+=(--target "${TARGET_TRIPLE}")
fi
+195
View File
@@ -0,0 +1,195 @@
#!/usr/bin/env bash
# Build a macOS .pkg installer for FIPS.
#
# Usage: ./packaging/macos/build-pkg.sh [--version <version>] [--no-build]
# Output: deploy/fips-<version>-macos-<arch>.pkg
#
# Prerequisites: Xcode command-line tools (pkgbuild is included)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PACKAGING_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
PROJECT_ROOT="$(cd "${PACKAGING_DIR}/.." && pwd)"
usage() {
cat <<'EOF'
Usage: packaging/macos/build-pkg.sh [options]
Options:
--version <version> Override package version
--target <triple> Rust target triple (e.g. x86_64-apple-darwin)
--no-build Package existing binaries without running cargo build
-h, --help Show this help
EOF
}
VERSION_OVERRIDE=""
TARGET_TRIPLE=""
NO_BUILD=0
while [[ $# -gt 0 ]]; do
case "$1" in
--version)
VERSION_OVERRIDE="${2:?missing value for --version}"
shift 2
;;
--target)
TARGET_TRIPLE="${2:?missing value for --target}"
shift 2
;;
--no-build)
NO_BUILD=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
esac
done
VERSION="${VERSION_OVERRIDE:-$(grep '^version' "${PROJECT_ROOT}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)"/\1/')}"
ARCH="$(uname -m)"
PKG_NAME="fips-${VERSION}-macos-${ARCH}"
DEPLOY_DIR="${PROJECT_ROOT}/deploy"
STAGING_DIR="$(mktemp -d)"
SCRIPTS_DIR="$(mktemp -d)"
trap 'rm -rf "${STAGING_DIR}" "${SCRIPTS_DIR}"' EXIT
if [[ -n "${TARGET_TRIPLE}" ]]; then
BINARY_DIR="${PROJECT_ROOT}/target/${TARGET_TRIPLE}/release"
else
BINARY_DIR="${PROJECT_ROOT}/target/release"
fi
echo "Building FIPS v${VERSION} for macOS ${ARCH}..."
# Build release binaries
if [[ "${NO_BUILD}" -eq 0 ]]; then
cargo_args=(build --release --manifest-path="${PROJECT_ROOT}/Cargo.toml" --no-default-features --features tui)
[[ -n "${TARGET_TRIPLE}" ]] && cargo_args+=(--target "${TARGET_TRIPLE}")
cargo "${cargo_args[@]}"
fi
# Verify binaries exist
for bin in fips fipsctl fipstop; do
if [[ ! -f "${BINARY_DIR}/${bin}" ]]; then
echo "Missing binary: ${BINARY_DIR}/${bin}" >&2
exit 1
fi
done
# Stage the payload (mirrors installed filesystem layout)
mkdir -p "${STAGING_DIR}/usr/local/bin"
mkdir -p "${STAGING_DIR}/usr/local/etc/fips"
mkdir -p "${STAGING_DIR}/usr/local/var/log/fips"
mkdir -p "${STAGING_DIR}/Library/LaunchDaemons"
mkdir -p "${STAGING_DIR}/etc/resolver"
# Binaries
for bin in fips fipsctl fipstop; do
cp "${BINARY_DIR}/${bin}" "${STAGING_DIR}/usr/local/bin/"
strip "${STAGING_DIR}/usr/local/bin/${bin}"
done
# Config (marked as conf file via postinstall logic — won't overwrite on upgrade)
cp "${PACKAGING_DIR}/common/fips.yaml" "${STAGING_DIR}/usr/local/etc/fips/fips.yaml.default"
cp "${PACKAGING_DIR}/common/hosts" "${STAGING_DIR}/usr/local/etc/fips/hosts.default"
# LaunchDaemon plist
cp "${SCRIPT_DIR}/com.fips.daemon.plist" "${STAGING_DIR}/Library/LaunchDaemons/"
# DNS resolver
cat > "${STAGING_DIR}/etc/resolver/fips" <<EOF
nameserver 127.0.0.1
port 5354
EOF
# Create postinstall script
cat > "${SCRIPTS_DIR}/postinstall" <<'POSTINSTALL'
#!/bin/sh
set -e
LOG="/var/log/fips-install.log"
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; logger -t fips-install "$*"; }
log "postinstall started"
CONFDIR="/usr/local/etc/fips"
# Install default config only if none exists (preserve on upgrade)
if [ ! -f "$CONFDIR/fips.yaml" ]; then
cp "$CONFDIR/fips.yaml.default" "$CONFDIR/fips.yaml"
chmod 600 "$CONFDIR/fips.yaml"
log "installed default config"
fi
if [ ! -f "$CONFDIR/hosts" ]; then
cp "$CONFDIR/hosts.default" "$CONFDIR/hosts"
fi
# Flush DNS cache so macOS picks up the new /etc/resolver/fips file
dscacheutil -flushcache
killall -HUP mDNSResponder 2>/dev/null || true
log "flushed DNS cache"
# Create fips group if it doesn't exist
if ! dscl . -read /Groups/fips > /dev/null 2>&1; then
dscl . -create /Groups/fips RecordName fips
dscl . -create /Groups/fips PrimaryGroupID 999
log "created group fips"
fi
# stat /dev/console gives the user logged into the GUI session —
# logname/SUDO_USER are not set in pkg postinstall context
REAL_USER="$(stat -f '%Su' /dev/console 2>/dev/null || true)"
log "console user: ${REAL_USER:-unknown}"
if [ -n "$REAL_USER" ] && [ "$REAL_USER" != "root" ]; then
if ! dscl . -read /Groups/fips GroupMembership 2>/dev/null | grep -qw "$REAL_USER"; then
dscl . -append /Groups/fips GroupMembership "$REAL_USER"
log "added $REAL_USER to group fips"
else
log "$REAL_USER already in group fips"
fi
fi
# Load the launchd service
launchctl bootout system /Library/LaunchDaemons/com.fips.daemon.plist 2>/dev/null || true
launchctl bootstrap system /Library/LaunchDaemons/com.fips.daemon.plist 2>/dev/null || true
log "launchd service loaded"
log "postinstall complete"
exit 0
POSTINSTALL
chmod +x "${SCRIPTS_DIR}/postinstall"
# Create preinstall script (stop service before upgrade)
cat > "${SCRIPTS_DIR}/preinstall" <<'PREINSTALL'
#!/bin/sh
# Stop service before upgrade
launchctl bootout system /Library/LaunchDaemons/com.fips.daemon.plist 2>/dev/null || true
exit 0
PREINSTALL
chmod +x "${SCRIPTS_DIR}/preinstall"
# Build the .pkg
mkdir -p "${DEPLOY_DIR}"
pkgbuild \
--root "${STAGING_DIR}" \
--scripts "${SCRIPTS_DIR}" \
--identifier com.fips.pkg \
--version "${VERSION}" \
--ownership recommended \
"${DEPLOY_DIR}/${PKG_NAME}.pkg"
echo ""
echo "Package built: deploy/${PKG_NAME}.pkg"
ls -lh "${DEPLOY_DIR}/${PKG_NAME}.pkg"
echo ""
echo "Install with: sudo installer -pkg deploy/${PKG_NAME}.pkg -target /"
echo "Remove with: sudo packaging/macos/uninstall.sh"
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.fips.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fips</string>
<string>--config</string>
<string>/usr/local/etc/fips/fips.yaml</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>StandardOutPath</key>
<string>/usr/local/var/log/fips/fips.log</string>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/fips/fips.log</string>
<key>WorkingDirectory</key>
<string>/usr/local/etc/fips</string>
</dict>
</plist>
+42
View File
@@ -0,0 +1,42 @@
#!/bin/sh
# FIPS uninstall script for macOS
#
# Run with: sudo ./uninstall.sh
set -e
PLIST="/Library/LaunchDaemons/com.fips.daemon.plist"
# Require root
if [ "$(id -u)" -ne 0 ]; then
echo "Error: must run as root (sudo $0)" >&2
exit 1
fi
echo "Uninstalling FIPS..."
# Stop and unload the service
if launchctl list com.fips.daemon >/dev/null 2>&1; then
launchctl unload "$PLIST" 2>/dev/null || true
echo " Service stopped"
fi
# Remove launchd plist
rm -f "$PLIST"
echo " Removed $PLIST"
# Remove DNS resolver
rm -f /etc/resolver/fips
dscacheutil -flushcache
killall -HUP mDNSResponder 2>/dev/null || true
echo " Removed /etc/resolver/fips"
# Remove binaries
for bin in fips fipsctl fipstop; do
rm -f "/usr/local/bin/$bin"
done
echo " Removed binaries from /usr/local/bin/"
echo ""
echo "FIPS uninstalled."
echo "Config preserved at /usr/local/etc/fips/ (remove manually if desired)"
echo "Logs preserved at /usr/local/var/log/fips/ (remove manually if desired)"
+1
View File
@@ -82,6 +82,7 @@ define Build/Compile
cargo build \
--release \
--target $(RUST_TARGET) \
--features gateway \
--bin fips \
--bin fipsctl \
--bin fipstop \
+1 -1
View File
@@ -110,7 +110,7 @@ cargo zigbuild \
--release \
--target "$RUST_TARGET" \
--no-default-features \
--features tui \
--features tui,gateway \
--bin fips \
--bin fipsctl \
--bin fipstop \
+1 -1
View File
@@ -86,7 +86,7 @@ echo "Building FIPS v${VERSION} for ${ARCH}..."
# Build release binaries (tui is a default feature, includes fipstop)
if [[ "${NO_BUILD}" -eq 0 ]]; then
cargo_args=(build --release --manifest-path="${PROJECT_ROOT}/Cargo.toml")
cargo_args=(build --release --manifest-path="${PROJECT_ROOT}/Cargo.toml" --features gateway)
if [[ -n "${TARGET_TRIPLE}" ]]; then
cargo_args+=(--target "${TARGET_TRIPLE}")
fi