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.
5.8 KiB
Run the FIPS Daemon as an Unprivileged User
By default, the FIPS daemon runs as root — the shipped Debian
systemd unit configures this, and no further setup is required.
The trade-off is that the daemon has full root authority,
including outside its actual network needs. Acceptable for
single-purpose hosts; less desirable for shared hosts.
This guide covers the alternative: drop privileges and run the
daemon under a dedicated unprivileged user account. The TUN
device that the FIPS IPv6 adapter creates requires
CAP_NET_ADMIN on Linux; the recipe below grants that privilege
via a file capability on the binary, plus everything else the
daemon needs to keep working without root: a service user
account, file permissions on the config directory, and a systemd
unit override to drop privileges.
For the design context (why the adapter needs a TUN, how the adapter integrates with the kernel routing table), see ../design/fips-ipv6-adapter.md.
Prerequisites
- FIPS package installed (the postinst already creates the
fipssystem group used for control-socket access). setcapavailable (apt install libcap2-binon Debian/Ubuntu; it is a standard utility on most distributions).- Operator access to systemd unit overrides (
systemctl edit).
Step 1: Create a fips system user
The package creates a fips system group but no matching user.
Add a system user that belongs to the fips group:
sudo useradd --system --gid fips --no-create-home --shell /usr/sbin/nologin fips
The user has no home directory and no login shell — this account exists only to run the daemon.
Step 2: Grant CAP_NET_ADMIN to the binary
Apply the file capability so the daemon can create the TUN device without root authority:
sudo setcap cap_net_admin+ep /usr/bin/fips
Verify:
getcap /usr/bin/fips
# /usr/bin/fips cap_net_admin=ep
The binary can now create TUN devices when run by any user.
File-capability caveats:
- The capability is attached to the binary file. Re-applying
the capability after every package upgrade is required,
because package upgrades replace the binary file and lose the
cap. The systemd override in Step 4 includes an
ExecStartPreline that automates this. - File capabilities are stripped when the binary is copied across
most filesystems and when it is downloaded via web tooling. If
you build from source and install manually, remember to
re-
setcapafter each rebuild. LD_LIBRARY_PATHand similar environment-driven loader controls are stripped at exec time when file capabilities are present; this is normally what you want, but development workflows that rely on custom library paths may be surprised.
Step 3: Adjust config-file permissions
The shipped /etc/fips/fips.yaml is mode 0600 and owned by
root:root. The daemon needs to read it and, if persistent
identity is enabled, write /etc/fips/fips.key into the same
directory.
sudo chown -R fips:fips /etc/fips
sudo chmod 0640 /etc/fips/fips.yaml
If node.identity.persistent: true is set and fips.key does
not exist yet, leave /etc/fips itself writable by the fips
user so the daemon can create it on first start. After the key
file exists, you can tighten further:
sudo chmod 0600 /etc/fips/fips.key
Step 4: Drop privileges in the systemd unit
Create an override:
sudo systemctl edit fips.service
Add:
[Service]
User=fips
Group=fips
AmbientCapabilities=CAP_NET_ADMIN
NoNewPrivileges=no
ExecStartPre=/sbin/setcap cap_net_admin+ep /usr/bin/fips
User= / Group= set the service identity.
AmbientCapabilities= ensures the file capability granted in
Step 2 actually carries into the daemon's process tree.
NoNewPrivileges=no is required for file-capability execution
to work — systemd defaults this to yes for hardened units,
which would block the setcap from taking effect.
ExecStartPre= re-applies the capability before each start,
which makes the package-upgrade path self-heal.
The unit's RuntimeDirectory=fips directive already arranges
for /run/fips/ to be created with the right ownership at
service start, now as fips:fips 0750 instead of
root:fips 0750.
Reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart fips
Step 5: Verify
Confirm the daemon is running as fips:
ps -eo user,cmd | grep '[/]usr/bin/fips'
# fips /usr/bin/fips --config /etc/fips/fips.yaml
Confirm the TUN device came up (the setcap worked):
ip link show fips0
# fips0: <POINTOPOINT,UP,...> mtu 1280 ...
Confirm the control socket is bound and accessible to the fips
group:
ls -la /run/fips/control.sock
# srwxrwx--- 1 fips fips ... /run/fips/control.sock
Add yourself to the fips group so you can use fipsctl /
fipstop without sudo:
sudo usermod -aG fips $USER
# log out and back in for the group change to take effect
Then:
fipsctl show node
Caveats
fips-firewall.servicestill runs as root. Loading nftables rules into the kernel requires root regardless. The firewall unit is intentionally separate from the daemon unit.- Bluetooth peers (
transports.ble.*) require additional privileges theCAP_NET_ADMINsetcap doesn't cover. If you use the BLE transport, you'll likely need to keep running as root or layer additional capability/D-Bus configuration; that path is not covered here.
See also
- persistent-identity.md — how the
daemon manages
/etc/fips/fips.key - ../design/fips-ipv6-adapter.md — IPv6 adapter design, TUN interface architecture
- ../reference/security.md — consolidated security surface
- ../reference/configuration.md
—
tun.*configuration block