Implement TCP Wrappers-style peer access control using
/etc/fips/peers.allow and /etc/fips/peers.deny files. Evaluation
order: allow overrides deny, default permit when no files exist.
Three enforcement points: outbound connect (before dialing), inbound
handshake (msg1 receipt, after restart/rekey classification), and
outbound handshake completion (msg2, before peer promotion). Files
support npub, hex pubkey, host alias, and ALL wildcard entries with
automatic mtime-based reload.
Adds fipsctl acl show query, 954-line acl module with unit tests,
and a 6-node Docker integration harness (testing/acl-allowlist/)
exercising insider, outsider, and allowed-remote scenarios. CI
matrix entry included.
Closes#50
Co-authored-by: Johnathan Corgan <johnathan@corganlabs.com>
Bump KSXGitHub/github-actions-deploy-aur from v4.1.1 to v4.1.2,
which fixes the runuser invocation that caused "bash: --command:
invalid option" during SSH initialization.
Gate platform-specific code behind cfg attributes and add full Windows
support: TUN device via wintun, TCP control socket on localhost:21210,
Windows Service lifecycle (--install-service/--uninstall-service/--service),
CI build and test matrix, and packaging with ZIP builder and PowerShell
service management scripts.
Key changes:
- Cargo.toml: move tun/libc/rtnetlink behind cfg(unix); add wintun and
windows-service dependencies for Windows
- upper/tun.rs: wintun-based TUN implementation with netsh configuration
for IPv6 address, MTU, and fd00::/8 routing
- control/mod.rs: split into unix_impl/windows_impl; Windows uses TCP on
localhost:21210 with shared connection handler
- bin/fips.rs: refactor main() into run_daemon() accepting a shutdown
signal; add Windows Service support via windows-service crate
- transport/udp/socket.rs: platform-gated modules; Windows uses
tokio::net::UdpSocket (kernel drop count unavailable, returns 0)
- transport/ethernet: gate to cfg(unix); add Windows stub types
- config: platform-conditional default paths (socket, hosts) for Windows
- CI: add windows-latest to build matrix and test-windows job with
cargo-nextest
- packaging/windows: build-zip.ps1, install-service.ps1,
uninstall-service.ps1, and package-windows.yml workflow
- README/docs: Windows build instructions, service management, and
control socket platform differences
Linux and macOS behavior is unchanged.
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
Add fips-gateway binary to CI artifact and Docker build. Systemd
service unit with After=fips.service dependency and security
hardening. Debian and AUR package entries.
OpenWrt packaging: procd init script managing dnsmasq forwarding,
proxy NDP, RA route advertisements for the virtual IP pool, and a
global IPv6 prefix on br-lan to work around Android suppressing AAAA
queries on ULA-only networks. Sysctl config for IPv6 forwarding.
Gateway enabled by default in OpenWrt config. Ethernet transport
enabled by default.
Default gateway config section (commented out) in common fips.yaml.
- Add package-linux.yml: builds tarball and .deb for x86_64 and aarch64
on v* tag push, uploads artifacts to GitHub release with checksums
- Make build-tarball.sh target-aware: --target, --version, --arch, --no-build
- Make build-deb.sh target-aware: --target, --version, --no-build
- Configurable strip binary via STRIP env var
- Add package-linux.yml: builds tarball and .deb for x86_64 and aarch64
on v* tag push, uploads artifacts to GitHub release with checksums
- Make build-tarball.sh target-aware: --target, --version, --arch, --no-build
- Make build-deb.sh target-aware: --target, --version, --no-build
- Configurable strip binary via STRIP env var
BLE transport implementation using L2CAP Connection-Oriented Channels
(SeqPacket mode) via the bluer crate, behind cfg(feature = "ble").
Core transport:
- BleTransport<I> generic over BleIo trait (BluerIo prod, MockBleIo test)
- Connection pool with priority eviction (static > discovered, max 7)
- Connect-on-send via connect_inline() matching TCP behavior
- Per-connection receive loops with pool cleanup on disconnect
Discovery and probing:
- Combined scan_probe_loop using select! over scanner events and a
BinaryHeap delay queue with per-entry random jitter (0-5s) to prevent
herd effects when multiple nodes see the same beacon simultaneously
- Pre-handshake pubkey exchange ([0x00][pubkey:32]) for IK identity
- Cross-probe tie-breaker: smaller NodeAddr's outbound wins (same
convention as FMP/FSP rekey dual-initiation)
- Probed peers reported to DiscoveryBuffer; pool fills through normal
node-layer auto-connect -> send_async -> connect_inline path
Beacon management:
- Periodic advertising: 1s burst every 30s (configurable via
beacon_interval_secs / beacon_duration_secs)
- FIPS service UUID for scan filtering
Configuration (all fields optional with defaults):
- adapter, psm, mtu, max_connections, connect_timeout_ms
- advertise, scan, auto_connect, accept_connections
- beacon_interval_secs (30), beacon_duration_secs (1)
Hardware validated with two BLE nodes:
- 2048-byte MTU, ~60-160ms RTT, zero-config auto-connect
- BLE spike tool at testing/ble/ for standalone adapter validation
42 unit tests + 4 node-level integration tests, all CI-compatible
via MockBleIo (no hardware required). tokio test-util added for
time-dependent scan/probe tests.
Add versioning job with release channel detection, Blossom upload,
NIP-94 event publishing, and relay verification. Replace setup-zig
action with direct download for act runner compatibility. Pin
cargo-zigbuild and zig versions for reproducible builds.
Pin Rust toolchain to 1.94.0 via rust-toolchain.toml for deterministic
compiler output across environments. Set SOURCE_DATE_EPOCH from git
commit timestamp in CI workflows and packaging scripts to normalize
embedded timestamps.
Make packaging archives reproducible: pass --mtime=@$SOURCE_DATE_EPOCH
to tar in .deb, .ipk, and systemd tarball builds. Normalize ownership
to root:root in systemd tarballs. Pin cargo-zigbuild to 0.19.8 for
cross-compilation stability.
Add SHA-256 hash output to CI build and OpenWrt packaging workflows
for binary verification.
Add a complete example running a strfry Nostr relay exclusively over
the FIPS mesh using the sidecar pattern. Includes Docker Compose
stack, network isolation via iptables, .fips DNS resolution, nak CLI,
build script with cross-compilation support, and documentation.
Also fix the package-openwrt.yml workflow path to match the
openwrt → openwrt-ipk directory rename.
Adds packaging/openwrt/ with everything needed to produce and distribute
FIPS .ipk packages for OpenWrt routers:
- Makefile: OpenWrt feed package definition using rust/host toolchain,
maps OpenWrt ARCH to Rust musl target triples
- build-ipk.sh: local build script using cargo-zigbuild + direct tar
assembly — no OpenWrt SDK or Docker required. Handles macOS BSD tar
(ustar format, resource fork suppression) and portable ar header
generation for cross-platform .ipk creation. Accepts PKG_VERSION
env var override for CI use.
- files/: procd init script, default fips.yaml (persistent identity,
br-lan and wwan interface examples), firewall helper, dnsmasq .fips
forwarding, br_netfilter sysctl, hotplug for fips0, UCI defaults for
first-boot setup, sysupgrade keeplist
- .github/workflows/package-openwrt.yml: CI workflow (ubuntu-latest +
cargo-zigbuild) building aarch64 and x86_64 .ipk packages using
build-ipk.sh; llvm-strip for cross-compiled binaries; triggers on
every push; publishes to GitHub Releases on version tags. MIPS
targets disabled pending portable-atomic crate (32-bit MIPS lacks
native AtomicU64), with nightly/rust-src toolchain steps prepared
for re-enablement.
- .gitignore: add reference/, dist/, *.ipk
Implement periodic full rekey at both protocol layers using fresh DH
key exchanges. Uses the existing K-bit flag (FLAG_KEY_EPOCH /
FSP_FLAG_K) to coordinate cutover between peers.
FMP layer (IK pattern):
- ActivePeer gains rekey state: pending/previous sessions, K-bit epoch
tracking, drain window, dampening timer
- Handshake state stored on ActivePeer with msg1 sent on existing link
- Encrypted frame handler detects K-bit flips, promotes pending
sessions, falls back to previous session during drain
- Handshake handlers distinguish rekey from new connections using
addr_to_link lookup with identity-based fallback
- Free all session indices (current, rekey, pending, previous) on
peer removal
FSP layer (XK pattern):
- SessionEntry gains parallel rekey fields with XK-specific state
for the 3-message handshake
- Route availability check before FSP rekey initiation
- Encrypted session handler adds K-bit flip detection and dual-session
decrypt fallback
- SessionSetup/Ack/Msg3 handlers extended for rekey paths
Defense-in-depth:
- Consecutive decryption failure detector (threshold=20) triggers
forced peer removal instead of waiting for link-dead timeout
- Identity-based rekey detection as fallback when addr_to_link
doesn't match (e.g., TCP ephemeral ports)
Configuration: RekeyConfig with enabled flag, after_secs (default 120),
and after_messages (default 65536) thresholds.
Logging: info for successful K-bit cutover completions, warn for
failures, debug for intermediate handshake steps, trace for routine
operations (resends, drain cleanup).
Rekey lifecycle:
1. Timer/counter fires -> initiator starts new handshake
2. Old session continues handling traffic during handshake
3. Handshake completes -> initiator cuts over, flips K-bit
4. Responder sees flipped K-bit -> promotes new session
5. Both keep old session for 10s drain window
6. After drain, old session discarded
Integration test: Docker-based multi-phase test exercising both FMP
and FSP rekey with aggressive timers (35s). Verifies connectivity
across all 20 directed pairs survives two consecutive rekey cycles.
Includes rekey topology, docker-compose profile, and CI matrix entry.
Increase ping test convergence wait from 3s to 5s for CI reliability.
Tailscale-style sidecar pattern: a FIPS container provides mesh
networking, and a companion app container shares its network namespace
via network_mode: service:fips.
Security model:
- iptables enforces strict isolation — the app container can only
communicate over the FIPS mesh (fd::/8 via fips0)
- No IPv4 access: eth0 restricted to FIPS UDP transport (port 2121)
- No IPv6 on eth0: ip6tables blocks all eth0 IPv6 traffic
- Only fips0 and loopback are reachable from the app container
The sidecar accepts peer configuration via environment variables
(FIPS_NSEC, FIPS_PEER_NPUB, FIPS_PEER_ADDR), so it can be pointed
at any FIPS node without config file generation.
Files:
- testing/sidecar/: Dockerfile, Dockerfile.app, docker-compose.yml,
entrypoint.sh, .env, resolv.conf, scripts/build.sh
- testing/sidecar/README.md: security model, quick-start, architecture,
DNS resolution, troubleshooting, production considerations
- testing/sidecar/scripts/test-sidecar.sh: 3-node chain integration
test verifying link establishment, multi-hop connectivity, and
network isolation on each app container
- .github/workflows/ci.yml: sidecar integration test matrix entry
Three-job pipeline:
- build: matrix over ubuntu/macos/windows, native cargo build per runner;
macOS and Windows are continue-on-error (best-effort). Uploads Linux
binary as artifact for downstream jobs.
- test: cargo test --all on ubuntu, gated on build succeeding.
- integration: parallel matrix of static-mesh, static-chain, and
chaos-smoke-10; runs only when build + test both pass. Static jobs
use docker compose + ping-test.sh; chaos job runs the stochastic
simulation via chaos.sh.