mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
76d7e43d60724dab0e5be68b24b806b27f50832a
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f624013b83 |
feat(openwrt): open !FIPS access SSID layer (#126)
Access layer for phones and laptops to reach FIPS routers on OpenWrt, stacked on the 802.11s mesh backhaul from #123. Squashed from five commits by Arjen (Origami74); their original messages follow. * feat(openwrt): open !FIPS access SSID — fips-ap-setup helper, default transport binding, how-to Client access layer for phones and laptops: every FIPS router broadcasts the same open SSID ('!FIPS' — the leading '!' sorts it to the top of alphabetically ordered network pickers), forming one standard ESS. Clients save it once and roam between all FIPS routers natively, with FIPS's Noise IK handshake as the only security layer: - fips-ap-setup: opt-in UCI helper that creates the 'fips-ap0' open AP (encryption none — security type must be uniform across routers or clients treat the ESS as different saved networks), an isolated network with a static ULA /64, RA-only odhcpd addressing (stateless SLAAC, no DHCP — the minimum that satisfies Android's provisioning check; no internet by design, so phones keep cellular as default route), and a locked-down fips_ap firewall zone (no path to br-lan or the WAN; only ICMPv6, mDNS, and the FIPS transports reachable). 'remove' subcommand undoes it. Radio setup stays opt-in; a package must not commandeer radios on install. - fips.yaml: ship 'ap0'/'ap1' Ethernet-transport entries commented out (matching the 802.11s mesh backhaul) so a stock install that never creates fips-ap* logs no per-boot "interface missing" bind warning; fips-ap-setup uncomments the matching block when it creates the interface and re-comments it on remove. - Regression test: extend shipped_openwrt_config_parses to assert the ap0/ap1 entries ship commented out and still parse once uncommented, alongside mesh0/mesh1. - Packaging: install the helper in ipk/apk/buildroot (three synced copies), extend CI structural checks and shellcheck targets. - docs/how-to/set-up-open-access-ssid.md: full guide, including the one-time 'no internet, stay connected' acceptance (stored per SSID, covers every FIPS router) and the security-type-uniformity constraint. * docs(openwrt): correct open-SSID/mesh security framing — peering is open by design The fips-ap-setup/fips-mesh-setup comments and both how-tos claimed a stranger "cannot pass the FIPS handshake" / "their frames die at the handshake" / the handshake surface "drops them". That is wrong: FIPS peer admission is open. An inbound handshake from any net-new identity is promoted (node::handlers::handshake::promote_connection), gated only by the daemon's max-peers cap — there is no allowlist, no PSK, and the AuthChallenge path is not wired to admission. The Noise IK handshake provides authentication (no impersonation of another identity, no MITM), not authorization. Restate the model accurately in all five places: a stranger on the open SSID (or the open mesh) can associate AND form a FIPS peer link — that is the point of open access. Containment is the isolated fips_ap zone (no path to br-lan or the WAN) plus the max-peers cap, not the handshake. Clarify that AP client isolation is an L2 control only: a peered stranger is an overlay peer like any other, so the FIPS overlay, not L2, is the trust boundary between clients. * feat(openwrt): serve DHCPv4 on the access SSID from a fixed roamable subnet RA-only addressing satisfied Android's provisioning check but left anything expecting IPv4 with a self-assigned address and a "no IP" complaint. dnsmasq now leases out of 10.21.<N>.0/24 (N = radio index; prefix echoes FIPS port 2121), deliberately identical on every router: a roaming client keeps its lease across the ESS, and dnsmasq's authoritative mode — the OpenWrt default, pinned by the helper — ACKs the renew a foreign router never issued. Lease collisions across routers surface as a NAK on renew and the client re-DHCPs. The dhcp section's 'dhcpv4 server' is read by both dnsmasq (default images) and odhcpd (only with maindhcp), so either arrangement serves. A DHCPv4/udp-67 accept rule joins the fips_ap zone; DHCPv6 stays off, the ULA RA stays as-is, and nothing depends on an upstream. The zone remains isolated — no forwardings, no internet. * feat(openwrt): enable mDNS rendezvous from fips-ap-setup Phone FIPS apps cannot open raw-Ethernet sockets, so DNS-SD is how they find the router's daemon — but node.rendezvous.lan defaults to off. Ship the lan block commented in fips.yaml (consistent with the apN transport entries) and have fips-ap-setup uncomment it when creating the access SSID. The awk match is scoped to node.rendezvous because transports.ethernet carries a 'lan' entry at the same indent. The switch is daemon-wide, so 'remove' deliberately leaves it on rather than guess whether other transports rely on it. * fix(openwrt): bind UDP dual-stack [::]:2121 so access-SSID clients reach it The shipped router config bound the UDP transport "0.0.0.0:2121" (IPv4 wildcard) while the mDNS LAN advert announces every interface address, including the router's IPv6 link-local — which phones on the !FIPS access SSID rightly prefer (their cellular default route swallows v4, and fd00::/8 is captured by the Myco mesh TUN). Result: the client's Noise msg1 arrives on an unbound v6 port and is silently lost; the handshake resends and times out. Symptom chain (observed on-device): mDNS resolve OK, platform push OK, "Sent Noise handshake message 1" to [fe80::…%N]:2121, four resends, no reply, 30 s stale-timeout. OpenWrt is Linux (bindv6only=0), so "[::]" accepts IPv4 via v4-mapped addresses too — nothing is lost. packaging/common is deliberately left on "0.0.0.0" for now: Windows defaults IPV6_V6ONLY=1, where "[::]" would drop v4 instead. |
||
|
|
054d17aac5 |
feat(openwrt): 802.11s open-mesh backhaul support
Router-to-router radio backhaul over an open 802.11s mesh interface, with FIPS providing all encryption (Noise IK), authentication, and routing on top of bare L2 neighbor links. The mesh runs OPEN with mesh_fwding 0 — SAE would duplicate the Noise layer and force ath10k raw mode, and FIPS is the routing layer — so the Noise handshake is the real auth/encryption boundary and FIPS's spanning tree does the routing. fips-mesh-setup: an opt-in UCI helper that creates a per-radio mesh-point interface (radio0 -> fips-mesh0, radio1 -> fips-mesh1; trailing-digit derivation with a free-index fallback and a collision guard). Radio setup stays opt-in — a package must not commandeer radios on install. 'remove' takes an optional radio and otherwise removes all instances. Dual-band routers get one instance per radio; FIPS treats the two backhaul paths as failover, not multipath: it keeps one active link per peer (cross-connection resolution picks a single winner), and the second band stands by, re-establishing the peer after keepalive timeout — traffic never uses both bands at once. fips.yaml ships the mesh0/mesh1 Ethernet-transport entries commented out, so a stock install that never creates fips-mesh* logs no per-boot "interface missing" bind warning. fips-mesh-setup uncomments the matching meshN block when it creates the interface and re-comments it on remove, so the flash-and-drop-in flow needs no manual config edit. The file is rewritten 0600-first (it may hold an inline nsec) via an atomic replace. Two field-found silent non-peering causes are surfaced by the helper and the guide: - Same channel: mesh points only peer on a shared channel, and 'auto' lets each radio pick its own. The helper prints the radio's band/channel and warns loudly on 'auto' with the exact uci command to pin one; the how-to gains an ordered no-peers triage (channel mismatch, on-air scan check, DFS CAC wait, regdomain). - STA channel capture: a client (sta) interface drags the whole radio to its upstream AP's channel, so a mesh pinned elsewhere never joins and does not recover until the STA disconnects. The helper warns when the target radio carries a STA; the guide documents the incompatibility of a roaming uplink with a fixed-channel mesh on the same radio. Both the create and remove paths run 'wifi reload', which briefly drops every client AP on all radios; the how-to sets that expectation. Regression test: the shipped OpenWrt fips.yaml must parse via the real Config deserializer in both states — as shipped (mesh inactive) and after the uncomment the helper performs. Packaging: the helper is installed across the ipk/apk/buildroot paths (three synced copies), with the CI structural checks and shellcheck targets extended to cover it. Full guide in docs/how-to/set-up-80211s-mesh-backhaul.md. |
||
|
|
507086e39d |
docs: refresh tutorials, how-to, design, reference, and examples for v0.4.0
Pre-cut documentation pass for the 0.4.0 release, verified against current source. Corrections: - fipsctl: stale 'show identities'/'show node' -> 'show status' (host-a-service, run-as-unprivileged-user) - mesh address derivation: first 16 bytes of SHA-256(pubkey) with the leading byte set to 0xfd, not a fixed fd97: prefix (reach-mesh-services, ipv6-adapter-walkthrough) - gateway control socket mode 0660 -> 0770 (troubleshoot-gateway) - Tor example: add advertised_port: 8443 so the published port matches the prose (enable-nostr-discovery) - bloom mesh-size estimate rewritten to the OR-union-of-peer-filters algorithm; plus mtu deep-link, gateway pool wording, and a NAT failure-mode line - examples: delete orphaned nostr-rs-relay config, accept inbound to the local 8443 TCP listener, fix fd::/8 -> fd00::/8 typos, dotless wireguard alias Additions: - new Nym mixnet transport section (fips-transport-layer) and the architecture transport list - new LAN/mDNS discovery section (fips-nostr-discovery) - reference docs: Nym transport, LAN discovery, and new control/stats surfaces; drop ble from the connect transport list |
||
|
|
bf4e0df8c5 |
docs: add how-to for tuning the file-descriptor limit
A busy node opens roughly three file descriptors per established UDP peer (a connect()-ed socket plus a 2-FD drain self-pipe), so the default 1024 soft RLIMIT_NOFILE is exhausted near 320 peers and further peer admission, handshakes, and discovery fail with EMFILE. Document the FD budget, the symptom, and the systemd (LimitNOFILE drop-in) and OpenWrt (procd nofile) procedures to raise it, plus how to verify the per-peer ratio is bounded. Link the new guide from the how-to index. |
||
|
|
77fdd52fe0 |
docs: reviewer feedback pass on Nostr-discovery surface
Walk through reviewer feedback on the Nostr-discovery docs and land 18 items. Bulk patterns: - `external_addr` / `public: true` semantics consistently misdescribed. The advert path is gated on `cfg.is_public()`; inside that branch the daemon picks an address by precedence (`external_addr`, non-wildcard `bind_addr`, STUN). The docs treated `public: true` and `external_addr` as alternatives when they are stacked: `public: true` is the master switch and `external_addr` populates the address inside it. Reconciled across `enable-nostr-discovery.md` and `advertise-your-node.md`: add `public: true` to the `external_addr` examples; replace "STUN as a logging cross-check" with "STUN is skipped entirely"; fix "neither flag is needed" for direct public bind (both flags still required); make the publish-tutorial Step 3 conditional on the chosen Step 2 path (STUN runs only on the `public: true` path); rewrite the troubleshooting "wrong public IP advertised" bullet with two coherent fixes. - `udp:nat` overpromised as a symmetric-NAT solution. Symmetric NAT on either side typically defeats the punch. Reframe `udp:nat` as best-effort hole-punching for nodes without a directly reachable UDP endpoint in the how-to, the publish tutorial (intro, callout, section heading rewrite from "If you're behind symmetric NAT" to "If your direct UDP advert isn't reachable"), the consume tutorial's "What's next" pointer, and `tutorials/README.md`. Promote reachability over named NAT classes: STUN can confirm the public IP but not that the listener-port mapping is open. - YAML "silently ignores unknown keys" is wrong. Config parser rejects unknown fields via `serde(deny_unknown_fields)` on the per-section structs; misspelled fields refuse the daemon's start with a parse-error line in the journal. Fixed in the publish tutorial's troubleshooting and the open-discovery tutorial's `policy` typo bullet. Mechanical fixes: - Repoint stale anchors. `getting-started.md` and `configuration.md` linked to `#installation` / `#inspect` on the README; the README has no such headings. Repoint to `#quick-start` and `cli-fipsctl.md`. Two stale anchors in the publish tutorial pointing at non-existent sub-scenarios in the how-to (`#sub-scenario-2c-...`, `#sub-scenario-2b-tor-onion-node`) repointed to the correct anchors. - Drop the `fipsctl show status` claim from the open-discovery troubleshooting bullet (`show_status` doesn't include `discovery.nostr.policy`). Replace with daemon startup logs. - Fix the `advertise: false` parenthetical in the consume-only tutorial (`default_advertise()` returns `true`; we set `false` explicitly for the consume-only path). - Drop the "supplies a relay list" overstatement in two activation paragraphs (the how-to and the design doc). Default relay / STUN-server lists ship in the config; both are optional overrides. - Add the missing `transports.udp.public` entry to the open-discovery tutorial's prerequisites checklist. Tutorial users coming out of advertise-your-node could be on either the direct-UDP (`public: true`) or `udp:nat` (`public: false`) path; list both. Files: docs/getting-started.md, docs/reference/configuration.md, docs/how-to/enable-nostr-discovery.md, docs/tutorials/README.md, docs/tutorials/advertise-your-node.md, docs/tutorials/resolve-peers-via-nostr.md, docs/tutorials/open-discovery.md, docs/design/fips-nostr-discovery.md. |
||
|
|
53ad528f7d |
fipstop: add "Listening on fips0" panel to Node tab
Surfaces local services reachable from the mesh, paired with their
current `inet fips` baseline filter classification. Lands to the
right of the existing TUN section in the Traffic block.
A new daemon control query `show_listening_sockets` returns IPv6
listeners bound to either `::` (wildcard) or the node's fd00::/8
address, each classified as Accept / Drop / Unknown / NoFirewall
against the running inbound chain. fipstop renders the result as a
table beside the Traffic counters: Accept rows in default White,
Drop / Unknown in DarkGray, a yellow banner above the table when
`fips-firewall.service` is inactive, and a trailing `*` on
wildcard binds to remind the operator the bind is not
fips0-specific.
Daemon side:
- `src/control/listening.rs` walks `/proc/net/tcp6` and
`/proc/net/udp6` via the procfs crate (LISTEN state for TCP,
wildcard remote for UDP), filters to fips0-reachable binds, and
resolves inodes to PID / comm via `/proc/<pid>/fd`.
- `src/control/firewall_state.rs` shells out to
`nft -j list table inet fips` and walks the inbound chain.
Recognises canonical accepts (`tcp/udp dport N accept`,
`dport { ... } accept`, `dport A-B accept`), the iifname-scoping
line, conntrack and icmpv6 lines (skipped). Any rule with
unrecognised matchers (saddr filters, jumps, daddr filters) or
non-terminal verdicts forces Unknown classification for the
ports it references. Eleven unit tests cover the classification
logic; the listening enumerator carries a /proc-parsing test of
its own.
- `show_listening_sockets` emits
`{fips0_addr, firewall_active, sockets[]}` with per-row
`{proto, local_addr, port, pid, process, filter, wildcard_bind}`.
fipstop side:
- `src/bin/fipstop/ui/dashboard.rs` splits the Traffic block into
a 50/50 horizontal layout; the existing TUN + Forwarded panel
occupies the left half.
- `src/bin/fipstop/ui/listening.rs` renders the right half.
- `main.rs` fetches the new query each tick when the Node tab is
active. Errors are non-fatal: an old daemon without the query
leaves the payload at None and the panel renders "loading...".
`Cargo.toml` gains `procfs = "0.18"` on the Linux target. IPv4
listeners are not enumerated — fips0 is IPv6-only.
Folded in: revert the default-socket lookup from writability-probe
back to existence-based selection. The previous tempfile-probe on
`/run/fips` silently steered fipstop / fipsctl onto an XDG path
the daemon never bound for any user in the `fips` group whose
shell session had not yet picked up the supplementary group (no
re-login after `usermod -aG`). `XDG_RUNTIME_DIR` is set on every
modern systemd-managed user session, so this hit the common case.
The kernel checks actual group membership at `connect(2)`, so a
user who genuinely cannot connect now gets a clear `EACCES`
rather than a silent path mismatch. Drops the now-unused
`is_writable_dir` helper. `XDG_RUNTIME_DIR` existence validation
is preserved.
Documentation:
- `docs/reference/cli-fipstop.md` — Node-tab row updated, new
"Listening on fips0 panel" section.
- `docs/reference/control-socket.md` — `show_listening_sockets`
added to the read-only queries table.
- `docs/how-to/enable-mesh-firewall.md` — new "Verify with
fipstop" section.
- `docs/tutorials/host-a-service.md` — fipstop callouts at
Steps 3, 5, 6 + Troubleshooting bullet + wildcard-bind reminder
under "What you've learned".
- `CHANGELOG.md` — new bullet under `Added / Operator Tooling`,
resolver `Fixed` entry rewritten to describe the
existence-based final shape.
|
||
|
|
0fcf0f6f8f |
gateway: change dns.listen default to [::1]:5353
The gateway is designed for systems already serving DHCP and DNS to a LAN segment (canonically an OpenWrt AP). On those systems port 53 is already taken by the existing resolver, so the prior `[::]:53` default conflicted with the gateway's intended deployment target out of the box. The OpenWrt ipk previously overrode this in its packaged config as a workaround; matching the source default to what the canonical deployment actually wants makes the override redundant and removes a foot-gun for fresh manual Linux-host installs. The redundant `dns.listen` line in `packaging/openwrt-ipk/files/etc/fips/fips.yaml` is dropped along with this change. Operators on a host without a pre-existing resolver on port 53 can opt back into the wildcard bind by setting `dns.listen: "[::]:53"` explicitly. The new default binds IPv6 loopback only — Linux IPv6 sockets bound to explicit `::1` do not accept v4-mapped traffic, so forwarders that reach the gateway over IPv4 loopback need to be pointed at an explicit IPv4 listen address instead. Touches the gateway config struct and its default-value test, the commented-out gateway example in the Debian common fips.yaml, the OpenWrt ipk config (override removed), the gateway reference / how-to / design / tutorial / troubleshoot docs, and a CHANGELOG entry under [Unreleased] -> Changed. |
||
|
|
5abf9a9325 |
docs: four-section /docs/ restructure with new-user content, accuracy pass, and gateway feature-set rewrite
Restructures /docs/ by reader purpose (tutorials, how-to, reference, design), adds the new-user-progression and operator-recipe content the prior layout lacked, runs an accuracy pass against current source across the pre-existing design docs, and rewrites the gateway feature-set documentation end-to-end around its actual operational profile (a niche feature designed for systems already serving DHCP/DNS to a LAN, with two independent halves — outbound LAN→mesh, inbound mesh→LAN — sharing one nftables table, one binary, and one control socket). Top-level README and getting-started rewritten around two equally-weighted deployment modes (overlay on existing IP networks; ground-up over non-IP transports). ## Additions - 11 new tutorials in docs/tutorials/: an 8-step new-user progression from single-daemon test-mesh peering through to a ground-up two-device mesh, an IPv6-adapter side-trip walkthrough, an Advanced Tutorials index, and a hand-held OpenWrt walk-through for fips-gateway deployment that exercises both halves of the feature. - 12 new how-tos in docs/how-to/: firewall activation, Nostr discovery (resolve / advertise / open across five scenarios), Tor onion (directory + control_port modes), UDP buffer tuning, unprivileged-user setup, persistent identity, host aliases, Bluetooth LE peering, MTU diagnostics, manual Linux-host gateway deployment (covers both halves), gateway troubleshooting (organised by half), and a section index. - 9 new reference docs in docs/reference/: configuration, wire formats, control-socket protocol, four CLI references (fips, fipsctl, fipstop, fips-gateway), security posture matrix, and Nostr events catalog. Configuration and wire-formats are renamed-and-extended from prior design/ versions; the other seven are net-new. - 6 new design docs: fips-concepts, fips-architecture, and fips-prior-work split out of the deleted fips-intro.md; consolidated fips-mmp and fips-mtu aggregations; and a new generic port-advertisement-and-nat-traversal doc (Nostr-signaled port advertisement plus UDP NAT-traversal protocol, FIPS as an example implementation, suitable for eventual NIP submission). - Top-level docs/getting-started.md walking through the binary-installer-only Install story. - packaging/common/hosts pre-populated with the eight public test-mesh nodes so shortnames resolve out of the box on every fresh install. ## Changes - 23 wire-format diagrams relocated to reference/diagrams/ alongside the wire-formats move. - 4 design diagrams corrected against source code (fips-protocol-stack, fips-identity-derivation, fips-coordinate-discovery, fips-routing-decision). - 10 pre-existing design docs reconciled with current source. Numeric corrections: stale link-MMP report bounds (now [1s, 5s] with 200 ms cold-start floor); UDP default MTU (now 1280, IPv6 minimum); node_addr formula (SHA-256(pubkey)[..16]); Noise patterns (IK at link, XK at session); peer-ACL semantics (strict allowlist requires ALL in peers.deny); daemon DNS upstream ([::1]:5354); on-the-wire bloom-filter size (1,071 bytes); obsolete Cargo-feature references (PR #79 dropped them) removed. - Transport framing tightened across the docs: TCP is for UDP-filtered networks (not NAT traversal); Tor is a deployment mode (not failover); WebSocket dropped (not a shipped FIPS transport); WiFi promoted to Implemented via Ethernet in infrastructure mode; classic-Bluetooth row removed (BLE is the only Bluetooth-mode transport). - docs/design/fips-gateway.md rewritten end-to-end to lead with the niche-feature framing and the two-halves structure. Title moved from "FIPS Outbound LAN Gateway" to "FIPS Gateway"; architecture section describes the common machinery (the fips-gateway service, the nftables table, the control socket) before splitting into separate "Outbound Half" and "Inbound Half" sections of equal weight; security considerations split per-half; no Future Work section (speculative directions live in the project tracker, not in protocol design docs). Inbound port forwarding is a first-class half rather than a buried "Implemented Extensions" subsection. - Gateway terminology unified across all gateway docs as a separate Linux service running alongside the fips daemon (its own systemd unit / OpenWrt init script). Container- pattern terms (sidecar) are reserved for the Docker/Kubernetes sidecar deployment examples — the testing/sidecar/ tree, examples/k8s-sidecar/, examples/sidecar-nostr-relay/, examples/wireguard-sidecar-macos/, and the related CHANGELOG / top-level README entries — where the term carries its standard container meaning. - Net-new design body content: rekey section in fips-mesh-layer (Noise IK msg1/msg2 over the established link, K-bit cutover, drain window, smaller-NodeAddr-wins tie-breaker on dual-init); Mesh Size Estimation and Antipoison FPR Cap sections in fips-bloom-filters; Mesh-Interface Query Filter subsection in fips-ipv6-adapter; failure-suppression knobs and clock- skew tolerance in fips-nostr-discovery; loop-rejection and mid-chain ancestor swap added to spanning-tree propagation / stability rules; Priority Chain in fips-mesh-operation renumbered to match the routing-decision diagram. - Top-level README: dropped the stale nostr-discovery cargo-feature parenthetical. docs/README.md and the four section READMEs (tutorials, how-to, reference, design) refreshed for the new structure; index rows reflect both halves of the gateway feature and the new fips-gateway CLI reference. - Cargo.toml [package.metadata.deb] assets path updated for the fips-security.md move; .gitignore /reference/ rule anchored to repo root so docs/reference/ is trackable. - packaging/openwrt-ipk/files/etc/fips/fips.yaml configuration-doc URL updated to the new docs/reference/configuration.md location. ## Deletions - docs/design/fips-intro.md (split into the three new intro design docs). - docs/design/document-relationships.svg (orphan, no longer referenced). - docs/proposals/ tree removed; the only proposal it contained (the Nostr UDP hole-punch protocol) was rewritten as the new generic design/port-advertisement-and-nat-traversal.md. |