mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
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.
478 lines
17 KiB
Markdown
478 lines
17 KiB
Markdown
# Host a Service of Your Own
|
|
|
|
In [reach-mesh-services](reach-mesh-services.md) you used
|
|
ordinary IPv6 tools to reach services on other mesh nodes. This
|
|
tutorial flips the direction. You will bring up a small HTTP
|
|
server on your machine, make a deliberate choice about which
|
|
interface it binds to, and turn on the mesh firewall to keep
|
|
the exposure to what you intended. By the end you will have
|
|
hosted your first peer-reachable service and made an informed
|
|
decision about who can reach it.
|
|
|
|
The whole exercise should take about twenty minutes. You should
|
|
have already worked through
|
|
[persistent-identity](persistent-identity.md) so that the npub
|
|
your service is reachable at does not change between restarts.
|
|
|
|
## What you'll build
|
|
|
|
```text
|
|
┌──────────────────────────────────────────┐
|
|
│ your fips node │
|
|
│ │
|
|
│ python3 -m http.server │
|
|
│ --bind <fips0-addr> 8080 │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ fips0 fd97:….:Y ◀─── port 8080 open │
|
|
└────────────┬─────────────────────────────┘
|
|
│
|
|
│ reachable as
|
|
│ http://<your-npub>.fips:8080/
|
|
▼
|
|
any mesh peer that can route to <your-npub>.fips
|
|
```
|
|
|
|
You will have:
|
|
|
|
- A single-page HTTP server bound to `fips0` only — not the
|
|
public internet, not your LAN, just the mesh.
|
|
- The mesh firewall baseline active, default-deny on `fips0`
|
|
inbound, with one explicit drop-in that allows TCP/8080.
|
|
- A clear understanding of which interface choice corresponds
|
|
to which audience.
|
|
|
|
## Why bind interface matters
|
|
|
|
The single most important decision when hosting a service is
|
|
**which interface (and therefore which audience) the service is
|
|
exposed to**. This is true for every IPv6 service, not just
|
|
FIPS — but FIPS makes it stark because your machine often has
|
|
several interfaces with very different exposure profiles.
|
|
|
|
> **Bind interface = exposure surface.** A typical FIPS host
|
|
> has at least two distinct audiences:
|
|
>
|
|
> - `fips0` (`fd97:…`, reachable as `<your-npub>.fips`) —
|
|
> reachable only from FIPS peers that have a working link to
|
|
> your node. Bound by Noise authentication and (optionally)
|
|
> the peer ACL.
|
|
> - `eth0` / `wlan0` (your LAN address) — reachable from anyone
|
|
> on your local network segment, with no FIPS auth in the
|
|
> way.
|
|
>
|
|
> When you run a server, the `--bind` argument decides which of
|
|
> these audiences sees the service. Binding to a *specific*
|
|
> address opts in to one audience. Binding to `[::]` or
|
|
> `0.0.0.0` opts in to **all** of them at once — including any
|
|
> you forgot you had.
|
|
>
|
|
> **Audit what's already listening.** Bringing up `fips0` adds
|
|
> a new audience to every service on this host that was already
|
|
> bound to `0.0.0.0` or `[::]`. SSH, your web server, a database
|
|
> — if any of them was listening on all interfaces before you
|
|
> joined the mesh, they are now reachable from mesh peers too.
|
|
> A quick `ss -tulnp` will show you everything currently
|
|
> listening and on which addresses. The mesh firewall (Step 5
|
|
> below) is one way to bring those exposures back under explicit
|
|
> control; rebinding the affected services to a specific
|
|
> non-mesh address is another.
|
|
|
|
There is nothing FIPS-specific about this rule; it applies to
|
|
SSH, web servers, databases, anything. FIPS just gives you the
|
|
option of "mesh peers only" as a distinct audience, which most
|
|
hosts otherwise don't have.
|
|
|
|
## Step 1: Find your node's mesh address
|
|
|
|
You need the `fd97:...` address assigned to your `fips0`
|
|
adapter. Two equivalent ways to get it:
|
|
|
|
```sh
|
|
ip -6 addr show fips0
|
|
```
|
|
|
|
Look for the `inet6 fd97:...` line. The address up to the `/`
|
|
is what you want.
|
|
|
|
Or via the daemon:
|
|
|
|
```sh
|
|
sudo fipsctl show identities
|
|
```
|
|
|
|
The first JSON entry has `local: true` and a `ula` field — that
|
|
is your address.
|
|
|
|
For the rest of this tutorial we will write the address as
|
|
`<your-fips0-addr>`. Substitute the actual `fd97:...` value
|
|
when you run the commands. Save it to a shell variable for
|
|
convenience:
|
|
|
|
```sh
|
|
FIPS0_ADDR=$(ip -6 addr show fips0 | awk '/inet6 fd97:/ {print $2}' | cut -d/ -f1)
|
|
echo "$FIPS0_ADDR"
|
|
```
|
|
|
|
You should also know your npub from
|
|
[persistent-identity](persistent-identity.md):
|
|
|
|
```sh
|
|
NPUB=$(sudo cat /etc/fips/fips.pub)
|
|
echo "$NPUB"
|
|
```
|
|
|
|
`<your-npub>.fips` and `<your-fips0-addr>` are two names for
|
|
the same destination.
|
|
|
|
## Step 2: Bring up an HTTP server bound to fips0
|
|
|
|
Make a small directory with one file in it so the server has
|
|
something to serve:
|
|
|
|
```sh
|
|
mkdir -p /tmp/mesh-demo
|
|
echo '<h1>Hello from the mesh</h1>' > /tmp/mesh-demo/index.html
|
|
cd /tmp/mesh-demo
|
|
```
|
|
|
|
Start a Python HTTP server bound to your `fips0` address:
|
|
|
|
```sh
|
|
python3 -m http.server --bind "$FIPS0_ADDR" 8080
|
|
```
|
|
|
|
Leave the server running. The terminal will show:
|
|
|
|
```text
|
|
Serving HTTP on fd97:... port 8080 (http://[fd97:...]:8080/) ...
|
|
```
|
|
|
|
Two things to notice:
|
|
|
|
- The "Serving HTTP on …" line names your `fd97:...` address
|
|
explicitly. Python is binding only to that one address.
|
|
- The default would have been `0.0.0.0` — which is **not**
|
|
what you want here. Without `--bind`, the server would be
|
|
reachable from your LAN and any public IP this host has,
|
|
not just from the mesh.
|
|
|
|
## Step 3: Verify the service locally
|
|
|
|
Open a second terminal. From the same host, fetch the page:
|
|
|
|
```sh
|
|
curl -6 "http://[$FIPS0_ADDR]:8080/"
|
|
```
|
|
|
|
Expect:
|
|
|
|
```text
|
|
<h1>Hello from the mesh</h1>
|
|
```
|
|
|
|
Now fetch it by name. Both forms should work:
|
|
|
|
```sh
|
|
curl -6 "http://${NPUB}.fips:8080/"
|
|
```
|
|
|
|
The daemon's local DNS responder turned the npub-form name into
|
|
the `fd97:...` address, and the request landed at your HTTP
|
|
server.
|
|
|
|
> **What just happened.** The kernel routed your local request
|
|
> via the loopback path because the destination address is
|
|
> assigned to one of your own interfaces. You exercised the
|
|
> client side (DNS, IPv6 socket open, HTTP request) and the
|
|
> server side (HTTP listener, response). What you have not yet
|
|
> verified is reachability *from another mesh node*. That is
|
|
> the next concern.
|
|
|
|
## Step 4: Reachability from a mesh node
|
|
|
|
Any mesh node — a direct peer, or a node several hops away —
|
|
reaches your service the same way you reached `test-us01` in
|
|
[reach-mesh-services](reach-mesh-services.md): it looks up
|
|
`<your-npub>.fips`, gets back your `fd97:...` address, opens
|
|
a TCP connection to it, and the FIPS data plane carries the
|
|
packets across the mesh to you. From the remote host the curl
|
|
looks identical to yours:
|
|
|
|
```sh
|
|
curl -6 "http://${NPUB}.fips:8080/"
|
|
```
|
|
|
|
If you have a second machine on the mesh — or you can ask
|
|
another operator to try it — this is the moment to confirm.
|
|
A node that can already `ping6 ${NPUB}.fips` should also be
|
|
able to fetch your page. If it can ping but the curl times
|
|
out, jump to [Troubleshooting](#troubleshooting) — but most
|
|
likely the firewall step in this tutorial has not happened
|
|
yet, so a remote attempt right now will succeed straight
|
|
through to your HTTP server.
|
|
|
|
That is the problem. With no firewall in place, *any* mesh
|
|
node that can route to you — your direct peers, and every node
|
|
beyond them in the mesh — can reach port 8080. You have not
|
|
yet made a deliberate decision about whether you want that.
|
|
The rest of this tutorial replaces the implicit "every port
|
|
with a listener is reachable" with an explicit "only the ports
|
|
I have opened are reachable, optionally only from specific
|
|
mesh nodes." That is the firewall's job, and it is the only
|
|
mechanism in play in the rest of this tutorial.
|
|
|
|
(There is a separate, unrelated control called the *peer ACL*
|
|
that decides which npubs may establish a peer connection with
|
|
your node at the transport layer. It is not part of the
|
|
firewall and does not affect what is described below; Step 7
|
|
is a brief signpost to it.)
|
|
|
|
## Step 5: Activate the mesh firewall baseline
|
|
|
|
FIPS ships a default-deny nftables baseline at
|
|
`/etc/fips/fips.nft` that restricts inbound traffic on `fips0`
|
|
to ICMPv6 echo and conntrack replies. The baseline is **not**
|
|
enabled by default — activation is an explicit step the
|
|
operator has to take.
|
|
|
|
Activate it:
|
|
|
|
```sh
|
|
sudo systemctl enable --now fips-firewall.service
|
|
```
|
|
|
|
This loads the table immediately and arranges for it to load
|
|
on every subsequent boot. Confirm:
|
|
|
|
```sh
|
|
sudo nft list table inet fips
|
|
```
|
|
|
|
You will see one chain named `inbound` hooked at `input`,
|
|
roughly:
|
|
|
|
```text
|
|
table inet fips {
|
|
chain inbound {
|
|
type filter hook input priority filter; policy accept;
|
|
iifname != "fips0" return
|
|
ct state established,related accept
|
|
icmpv6 type echo-request accept
|
|
counter packets 0 bytes 0 drop
|
|
}
|
|
}
|
|
```
|
|
|
|
The chain admits ICMPv6 echo (so `ping6` from any mesh node
|
|
still works) and conntrack replies (so your *outbound*
|
|
connections still get their replies back). Everything else
|
|
inbound on `fips0` hits the final `counter ... drop`.
|
|
|
|
> **What this changed.** Your HTTP server is still running and
|
|
> still reachable *from this same host* (same-host traffic to
|
|
> `fd97:...` goes via the loopback path, which has
|
|
> `iifname != "fips0"` and short-circuits at the first rule).
|
|
> But any mesh node trying to reach `fd97:...:8080` now has its
|
|
> TCP SYN dropped before it can reach your server. From the
|
|
> remote end the connection times out.
|
|
|
|
So the firewall is in the right shape but in the wrong state
|
|
for our purpose: we *want* mesh nodes to reach port 8080. The
|
|
next step opens that one port.
|
|
|
|
## Step 6: Open port 8080 via a drop-in
|
|
|
|
Drop-ins live under `/etc/fips/fips.d/` with the `.nft`
|
|
suffix. Each file is included into the `inbound` chain at the
|
|
marked point and may contain any nftables rule lines valid in
|
|
that context.
|
|
|
|
Create one for your HTTP service:
|
|
|
|
```sh
|
|
sudo tee /etc/fips/fips.d/http-mesh-demo.nft >/dev/null <<'EOF'
|
|
tcp dport 8080 accept
|
|
EOF
|
|
```
|
|
|
|
Reload the firewall:
|
|
|
|
```sh
|
|
sudo systemctl reload-or-restart fips-firewall.service
|
|
```
|
|
|
|
Confirm the rule is live:
|
|
|
|
```sh
|
|
sudo nft list table inet fips
|
|
```
|
|
|
|
The `inbound` chain now contains your `tcp dport 8080 accept`
|
|
rule between the conntrack rule and the final `counter drop`.
|
|
|
|
A curl from any mesh node will now reach the HTTP server. The
|
|
path is: remote node's mesh data plane → forwarded across the
|
|
mesh → your direct peer's link to you → `fips0` ingress →
|
|
`inbound` chain → matches `tcp dport 8080 accept` → delivered
|
|
to the HTTP server.
|
|
|
|
If you only want to expose the service to a *specific* node
|
|
or set of nodes, source-filter the rule. The address filter
|
|
applies to the mesh-source address as it arrives on `fips0`,
|
|
which is the originating node's address — not necessarily a
|
|
direct peer. Replace the drop-in contents with something like:
|
|
|
|
```nft
|
|
ip6 saddr fd97:1234:5678:9abc:def0:1234:5678:9abc tcp dport 8080 accept
|
|
```
|
|
|
|
The source address is the node's mesh address, which it
|
|
publishes in its `fips.pub` (and which you can resolve from
|
|
its npub). For multiple nodes, use a set:
|
|
|
|
```nft
|
|
ip6 saddr {
|
|
fd97:1111:2222:3333:4444:5555:6666:7777,
|
|
fd97:8888:9999:aaaa:bbbb:cccc:dddd:eeee
|
|
} tcp dport 8080 accept
|
|
```
|
|
|
|
For the worked example, leave the drop-in unfiltered — any
|
|
mesh node that can route to you can fetch your page.
|
|
|
|
## Step 7: A note on the peer ACL
|
|
|
|
The firewall you just configured is the only control in scope
|
|
for this tutorial. There is a separate, optional control
|
|
called the *peer ACL* that you may run across in other docs;
|
|
it is unrelated to the firewall and worth a sentence here only
|
|
so you do not confuse the two.
|
|
|
|
The peer ACL decides which npubs may establish a peer
|
|
connection with your node at the transport layer. It does not
|
|
look at ports, drop-ins, or `fips0` traffic. You do not need
|
|
it for this tutorial.
|
|
|
|
For when you do:
|
|
|
|
- [../how-to/enable-nostr-discovery.md](../how-to/enable-nostr-discovery.md)
|
|
— operator recipe.
|
|
- [../reference/security.md § Peer ACL](../reference/security.md#peer-acl)
|
|
— file format, evaluation order, alias handling.
|
|
|
|
## Step 8: Stop the server and tidy up
|
|
|
|
When you are done, stop the HTTP server in the first terminal
|
|
with `Ctrl-C`. The drop-in stays in place; remove it if you
|
|
do not want port 8080 reachable after the demo:
|
|
|
|
```sh
|
|
sudo rm /etc/fips/fips.d/http-mesh-demo.nft
|
|
sudo systemctl reload-or-restart fips-firewall.service
|
|
```
|
|
|
|
The `fips-firewall.service` itself can stay enabled —
|
|
default-deny on `fips0` is a sensible posture even with no
|
|
extra services running. To turn it back off:
|
|
|
|
```sh
|
|
sudo systemctl disable --now fips-firewall.service
|
|
```
|
|
|
|
## What you've learned
|
|
|
|
- **Bind interface = audience.** Binding to a specific address
|
|
opts in to one audience; binding to wildcard
|
|
(`0.0.0.0` / `[::]`) opts in to *all* of them, including
|
|
ones you forgot you had. For mesh-only exposure, bind to
|
|
your `fd97:...` address.
|
|
- **Same-host loopback is misleading.** A local curl to your
|
|
own `fd97:...` address goes via the loopback path, not
|
|
through `fips0` ingress. To actually verify mesh-side
|
|
reachability you need a second machine, or to read what
|
|
the firewall is doing in `nft list table inet fips`.
|
|
- **The mesh firewall is opt-in.** `fips-firewall.service` is
|
|
not enabled by default. Once it is enabled, `fips0` is
|
|
default-deny except for ICMPv6 echo and conntrack replies.
|
|
- **Ports open via drop-ins.** Each file under
|
|
`/etc/fips/fips.d/*.nft` adds rules into the `inbound`
|
|
chain. Source-filter with `ip6 saddr` to scope a port to
|
|
specific mesh nodes.
|
|
- **Two independent controls at two different layers.** The
|
|
firewall is a layer-3 filter on `fips0`: it controls which
|
|
TCP/UDP ports are reachable and (optionally) which mesh
|
|
source addresses may reach them. The peer ACL is a
|
|
transport-layer admission filter on Noise handshakes: it
|
|
controls which npubs may become direct peers of your node.
|
|
They are unrelated — the ACL does not touch fips0 traffic,
|
|
and the firewall does not look at npubs.
|
|
|
|
You now have the mental model for hosting any IPv6 service
|
|
behind a deliberate exposure policy. The mechanics generalize:
|
|
SSH on port 22, a database on port 5432, a custom protocol on
|
|
its own port — same `--bind` rule, same drop-in shape.
|
|
|
|
## Troubleshooting
|
|
|
|
- **A remote mesh node cannot reach the service after the
|
|
firewall reload.** Check the drop-in syntax with
|
|
`sudo nft -c -f /etc/fips/fips.nft` before reloading; a
|
|
syntax error in any drop-in causes the whole table to fail
|
|
to load and the previous rules persist. Then
|
|
`sudo nft list table inet fips` to confirm your
|
|
`tcp dport 8080 accept` rule is present in the `inbound`
|
|
chain.
|
|
- **Local curl works, remote curl times out.** The packet is
|
|
reaching `fips0` ingress and being dropped by the baseline.
|
|
Either your drop-in did not load (see above) or it has a
|
|
source filter that excludes the remote node's address.
|
|
- **Local curl fails after binding to fips0.** Double-check
|
|
that your `FIPS0_ADDR` matches the address shown in
|
|
`ip -6 addr show fips0`. The Python server message also
|
|
echoes the bound address — confirm it starts with `fd97:`,
|
|
not `127.0.0.1` or `::`.
|
|
- **`Address already in use` from Python.** Another process
|
|
holds port 8080. Pick a different port (`8081`, `9000`, …)
|
|
for both the `python3 -m http.server` invocation and the
|
|
drop-in.
|
|
- **Watch the firewall counter to confirm drops.** The
|
|
`counter ... drop` line at the bottom of the chain
|
|
increments on every dropped inbound packet. After a remote
|
|
mesh node attempts to reach a port you have not opened,
|
|
`sudo nft list table inet fips` will show the counter
|
|
packet count rising.
|
|
|
|
## What's next
|
|
|
|
- [ground-up-mesh.md](ground-up-mesh.md) — Bring up two devices
|
|
on a shared physical link — Ethernet, WiFi, or Bluetooth —
|
|
with no pre-existing IP infrastructure between them. The
|
|
second deployment mode of FIPS, where the mesh is the
|
|
network rather than an overlay on top of one. Coexists with
|
|
overlay peers; the same daemon carries both.
|
|
|
|
For more depth on the firewall and ACL surface:
|
|
|
|
- [../how-to/enable-mesh-firewall.md](../how-to/enable-mesh-firewall.md)
|
|
— operator recipes for the baseline, drop-in patterns,
|
|
and how to fold the baseline into an existing
|
|
`nftables.conf`.
|
|
- [../reference/security.md](../reference/security.md) —
|
|
consolidated security reference: nftables baseline rules,
|
|
drop-in format, peer ACL semantics, default exposures by
|
|
transport, threat-resistance matrix.
|
|
- [../design/fips-security.md](../design/fips-security.md) —
|
|
threat model, why the baseline is opt-in, the metadata-
|
|
privacy posture.
|
|
|
|
If you want to host a service that is *not* on a FIPS node — say,
|
|
an existing HTTP server on a regular LAN box — and expose it to
|
|
mesh peers through a `fips-gateway`, that's the inbound
|
|
port-forward mode: the gateway runs a mesh-side listener on `fips0`
|
|
and forwards to a LAN target. The operator recipe is at
|
|
[../how-to/deploy-gateway.md#inbound-port-forwarding](../how-to/deploy-gateway.md#inbound-port-forwarding);
|
|
a hand-held walk-through on an OpenWrt AP is at
|
|
[deploy-fips-gateway.md](deploy-fips-gateway.md) under "Advanced"
|
|
in [README.md](README.md).
|