mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Merge branch 'master' into next
This commit is contained in:
@@ -480,9 +480,33 @@ with v0.2.x peers.
|
||||
bind by setting `dns.listen: "[::]:53"` explicitly. The new
|
||||
default binds IPv6 loopback only — forwarders that reach the
|
||||
gateway over IPv4 loopback need an explicit IPv4 listen address.
|
||||
- Generic systemd install tarball brought to feature parity with
|
||||
the `.deb` and AUR packages. The tarball now ships the
|
||||
`fips-gateway` binary with its (operator-opt-in)
|
||||
`fips-gateway.service`, a `fips-firewall.service` unit with the
|
||||
`/etc/fips/fips.nft` mesh-interface nftables baseline (also
|
||||
opt-in), an `/etc/fips/fips.d/` operator drop-in directory for
|
||||
per-service nft rules, and the multi-backend `fips-dns-setup` /
|
||||
`fips-dns-teardown` helpers. `install.sh` and `uninstall.sh`
|
||||
handle the new units and conffile (preserve-on-upgrade for
|
||||
`fips.nft`, like `fips.yaml`). `README.install.md` documents
|
||||
the gateway, firewall, and DNS-routing services. Closes the
|
||||
longest-standing parity gap for non-Debian / non-Arch systemd
|
||||
Linux distros (Fedora, RHEL/CentOS, openSUSE, etc.) installing
|
||||
from the release-distribution tarball.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Generic systemd install tarball: `install.sh` now correctly
|
||||
resolves the `fips-dns-setup` and `fips-dns-teardown` helpers
|
||||
from the tarball staging directory. Previously the script
|
||||
referenced them at `${SCRIPT_DIR}/../common/`, a path that
|
||||
exists only in the source-repo layout, not in the extracted
|
||||
tarball. Bug latent since the multi-backend DNS helpers
|
||||
landed in `7260ad2`; only manifested when operators ran
|
||||
`install.sh` from an extracted tarball rather than from a
|
||||
source checkout.
|
||||
|
||||
- Adopted NAT-traversed UDP transports inherit the primary listener's
|
||||
MTU and buffer config. `Node::adopt_established_traversal`
|
||||
constructed the adopted UDP transport with `UdpConfig::default()`
|
||||
|
||||
+2
-1
@@ -17,7 +17,7 @@ Before changing code, read the protocol docs in this order:
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Rust 1.94.0 and Linux with TUN support
|
||||
- Rust 1.94.1 and Linux with TUN support
|
||||
- Use the pinned toolchain from [rust-toolchain.toml](rust-toolchain.toml) for deterministic builds
|
||||
- For the default BLE-enabled build on Debian/Ubuntu:
|
||||
`sudo apt install bluez libdbus-1-dev pkg-config`
|
||||
@@ -74,6 +74,7 @@ See [testing/README.md](testing/README.md) for the available integration and cha
|
||||
- Keep commits focused — one logical change per commit.
|
||||
- Add tests for new functionality.
|
||||
- Reference relevant design docs if the change touches protocol behavior.
|
||||
- Pull requests are merged via squash-merge.
|
||||
- Update docs in the same change when you modify:
|
||||
- protocol or routing behavior
|
||||
- wire formats
|
||||
|
||||
@@ -66,9 +66,17 @@ same way it would on a local network.
|
||||
Protocol.
|
||||
- **ECN congestion signaling.** Hop-by-hop CE-flag relay with RFC
|
||||
3168 IPv6 marking and transport kernel-drop detection.
|
||||
- **Operator visibility.** `fipsctl` CLI for control and inspection,
|
||||
`fipstop` TUI for live status, and a JSON-line control socket on
|
||||
each binary for direct programmatic access.
|
||||
- **Mesh-interface security baseline.** Optional default-deny
|
||||
nftables policy for `fips0` shipped as a packaged conffile
|
||||
(`/etc/fips/fips.nft`) with an operator drop-in directory
|
||||
(`/etc/fips/fips.d/`) and a disabled-by-default
|
||||
`fips-firewall.service`. The baseline polices only the mesh
|
||||
interface, leaving Docker, Tor, and the host firewall untouched.
|
||||
- **Operator visibility.** `fipsctl` CLI for control and inspection
|
||||
with time-series stats history queryable for any metric,
|
||||
`fipstop` TUI for live status with inline sparkline dashboards,
|
||||
and a JSON-line control socket on each binary for direct
|
||||
programmatic access.
|
||||
- **Reproducible builds** with toolchain pinning and
|
||||
`SOURCE_DATE_EPOCH`.
|
||||
|
||||
@@ -103,7 +111,7 @@ tutorial progression starting at
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Requires Rust 1.85+ (edition 2024). Linux, macOS, and Windows are
|
||||
Requires Rust 1.94.1+ (edition 2024). Linux, macOS, and Windows are
|
||||
supported; transport availability varies by platform.
|
||||
|
||||
| Transport | Linux | macOS | Windows | OpenWrt |
|
||||
|
||||
@@ -15,8 +15,9 @@ every supported platform and ships in every stock packaging artifact
|
||||
`.zip`). It is runtime-opt-in: the YAML configuration defaults to
|
||||
disabled (`node.discovery.nostr.enabled: false`), so the discovery
|
||||
runtime stays dormant — and opens no relay connections — until an
|
||||
operator flips the flag and supplies a relay list. When disabled, nodes
|
||||
behave exactly as before: only the static `peers[]` addresses are used.
|
||||
operator flips the flag. Default relay and STUN-server lists ship in
|
||||
the config; both are optional overrides. When disabled, nodes behave
|
||||
exactly as before: only the static `peers[]` addresses are used.
|
||||
|
||||
## Role
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ running nodes can mesh.
|
||||
|
||||
- A Linux, macOS, or Windows host. Linux is the most exercised
|
||||
platform; macOS and Windows installers are available.
|
||||
- The pre-built installer for your platform (see the
|
||||
[Releases page](../README.md#installation)), **or** a source
|
||||
checkout if you want to build the installer yourself.
|
||||
- The pre-built installer for your platform (see the project
|
||||
README's [Quick start](../README.md#quick-start) section for
|
||||
download links), **or** a source checkout if you want to build
|
||||
the installer yourself.
|
||||
- For the source-build path only: a working Rust toolchain (the
|
||||
version pinned in `rust-toolchain.toml` is auto-installed by
|
||||
rustup), and the platform-specific build dependencies listed in
|
||||
@@ -63,7 +64,7 @@ per-platform installer:
|
||||
- Windows — `.zip` with service-install scripts
|
||||
- Generic systemd Linux — `.tar.gz` with an `install.sh` script
|
||||
|
||||
See the [project README's Installation section](../README.md#installation)
|
||||
See the [project README's Quick start section](../README.md#quick-start)
|
||||
for download links and per-platform invocations.
|
||||
|
||||
### From source
|
||||
|
||||
@@ -4,7 +4,8 @@ Nostr-mediated discovery lets FIPS nodes find each other (and punch
|
||||
through UDP NAT) using public Nostr relays as the signaling channel.
|
||||
The feature ships in every stock packaging artifact but is **off by
|
||||
default** — it activates when an operator sets
|
||||
`node.discovery.nostr.enabled: true` and provides a relay list. See
|
||||
`node.discovery.nostr.enabled: true`. Default relay and STUN-server
|
||||
lists ship in the config; both are optional overrides. See
|
||||
[../design/fips-nostr-discovery.md](../design/fips-nostr-discovery.md)
|
||||
for the design and rationale; see
|
||||
[../reference/configuration.md](../reference/configuration.md) for the
|
||||
@@ -126,15 +127,20 @@ transports:
|
||||
udp:
|
||||
bind_addr: "0.0.0.0:2121"
|
||||
advertise_on_nostr: true
|
||||
external_addr: "203.0.113.45:2121" # ← explicit
|
||||
public: true # ← required, master switch
|
||||
external_addr: "203.0.113.45:2121" # ← explicit address
|
||||
```
|
||||
|
||||
`external_addr` accepts a bare IP (combined with the bind port) or a
|
||||
full `host:port`. Setting both `public: true` and `external_addr`
|
||||
together is allowed — the explicit override wins, with STUN as a
|
||||
logging cross-check. If UDP is bound directly to a public IP rather
|
||||
than to a wildcard, neither flag is needed; the daemon advertises
|
||||
the bound endpoint.
|
||||
full `host:port`. `public: true` is the master switch that gates UDP
|
||||
advertisement; inside that branch, the daemon picks the advertised
|
||||
address in precedence order: explicit `external_addr` (no STUN
|
||||
observation), a non-wildcard `bind_addr`, or STUN auto-discovery.
|
||||
Setting `external_addr` alongside `public: true` skips STUN entirely
|
||||
— there is no logging cross-check. If UDP is bound directly to a
|
||||
public IP rather than to a wildcard, neither `external_addr` nor STUN
|
||||
is needed — but `advertise_on_nostr: true` and `public: true` are
|
||||
still both required for the daemon to publish the endpoint.
|
||||
|
||||
What this achieves: the node publishes a single
|
||||
`udp:<public-ip>:2121` endpoint to the three default advert relays
|
||||
@@ -202,9 +208,17 @@ proceeds normally.
|
||||
> a `udp:nat` advert without signaling relays or STUN servers is
|
||||
> unreachable by construction.
|
||||
|
||||
Works best with full-cone NAT on at least one side. Symmetric NAT on
|
||||
both sides is not reliably traversable with this protocol and will
|
||||
time out after `punch_duration_ms`.
|
||||
Hole-punching is best-effort. It works reliably when both sides are
|
||||
full-cone or port-restricted NATs. Symmetric NAT on either side
|
||||
typically defeats the punch — the public port a peer sees varies per
|
||||
remote endpoint, so the address learned via STUN does not match the
|
||||
mapping the peer actually needs. The punch attempt times out after
|
||||
`punch_duration_ms`. `udp:nat` is the only NAT-traversal mechanism
|
||||
in FIPS; when it can't succeed, there's no in-protocol substitute.
|
||||
Being reachable then becomes a deployment-prerequisite question
|
||||
rather than a transport question — a publicly reachable port (UDP
|
||||
or TCP — both require the same kind of network resource) published
|
||||
as a direct advert per Sub-scenario 2a or 2b.
|
||||
|
||||
### Sub-scenario 2b: TCP
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ peers: # Static peer list
|
||||
The control socket provides access to node state and runtime management
|
||||
via the `fipsctl` command-line tool. In addition to read-only status
|
||||
queries, `fipsctl connect` and `fipsctl disconnect` enable runtime peer
|
||||
management. See the project [README](../../README.md#inspect) for the
|
||||
management. See the [`fipsctl` reference](cli-fipsctl.md) for the
|
||||
command list.
|
||||
|
||||
On Linux, the control socket is a Unix domain socket with filesystem
|
||||
|
||||
@@ -25,7 +25,7 @@ cover every option.
|
||||
| 1 | [join-the-test-mesh.md](join-the-test-mesh.md) | Add one public test peer to your config, watch the link come up, ping that peer and a second mesh node it routes you to. The starting point for everything else. |
|
||||
| 2 | [persistent-identity.md](persistent-identity.md) | Pin your daemon to a stable Nostr keypair so your address stops changing on every restart. Other operators can now add you to their `peers:` lists; the services you host get a fixed name. |
|
||||
| 3 | [resolve-peers-via-nostr.md](resolve-peers-via-nostr.md) | Stop hard-coding peer addresses. Drop the address line from your peer entry and let the daemon look up the current endpoint from public Nostr relays at dial time. |
|
||||
| 4 | [advertise-your-node.md](advertise-your-node.md) | Publish your own UDP endpoint to Nostr so any operator who knows your npub can reach you, with a short final section on `udp:nat` traversal for symmetric-NAT networks. |
|
||||
| 4 | [advertise-your-node.md](advertise-your-node.md) | Publish your own UDP endpoint to Nostr so any operator who knows your npub can reach you, with a short final section on `udp:nat` best-effort hole-punching for nodes without a directly reachable UDP endpoint. |
|
||||
| 5 | [open-discovery.md](open-discovery.md) | Switch to `policy: open` and let your peer list populate itself from the ambient `fips-overlay-v1` namespace. Hands-off mesh participation. |
|
||||
| 6 | [reach-mesh-services.md](reach-mesh-services.md) | Drive ordinary IPv6 tools — `ping6`, `nc`, `traceroute6`, `curl`, `ssh` — at mesh nodes by `<npub>.fips`. Get a feel for the daemon's IPv6 adapter, which makes unmodified IPv6 software work over the mesh. |
|
||||
| 7 | [host-a-service.md](host-a-service.md) | Bring up an HTTP server bound to `fips0` so mesh nodes can reach it, with a deliberate exposure decision (mesh-only vs every interface), and the mesh firewall as a default-deny baseline. The peer ACL (a separate, transport-layer control over which npubs may peer with your node) is briefly mentioned alongside. |
|
||||
|
||||
@@ -8,8 +8,9 @@ your own endpoint(s), so any other operator who knows your
|
||||
npub can dial you the same way you dialed `test-us01`.
|
||||
|
||||
The whole exercise should take about ten minutes if you have
|
||||
a public IP or full-cone home NAT. A short final section
|
||||
covers the alternative path for symmetric-NAT networks.
|
||||
a public IP or a UDP listener that's reachable from outside.
|
||||
A short final section covers `udp:nat` best-effort hole-punching
|
||||
for the cases where direct UDP advertising isn't an option.
|
||||
|
||||
## What you'll build
|
||||
|
||||
@@ -74,9 +75,13 @@ port to put in the advert:
|
||||
> - `public: true` — daemon does a one-shot STUN observation
|
||||
> against the configured STUN servers and uses the reflexive
|
||||
> IPv4 it learns. Right when your public IP is dynamic or
|
||||
> you'd rather not pin it in config. Works for nodes with a
|
||||
> directly-bound public IP and for nodes behind full-cone
|
||||
> NAT (most home routers).
|
||||
> you'd rather not pin it in config. Note: STUN observes the
|
||||
> reflexive IP from an ephemeral socket, then pairs it with
|
||||
> the listener's bind port for the advert — the advert is
|
||||
> only useful if your listener really is reachable at that
|
||||
> public IP/port, which the daemon can't tell from STUN
|
||||
> alone. A manual probe from a second host is the only sure
|
||||
> check.
|
||||
> - `external_addr: "<ip>[:<port>]"` — explicit override.
|
||||
> Right when you already know your public IP — a static
|
||||
> residential IP, an Elastic IP behind 1:1 NAT, a cloud
|
||||
@@ -86,8 +91,9 @@ port to put in the advert:
|
||||
> to the public IP returns `EADDRNOTAVAIL`.
|
||||
>
|
||||
> If you bind UDP to a specific public IP rather than
|
||||
> `0.0.0.0`, neither flag is needed — the daemon advertises
|
||||
> whatever it's bound to.
|
||||
> `0.0.0.0`, neither STUN nor `external_addr` is needed — but
|
||||
> `advertise_on_nostr: true` and `public: true` are still both
|
||||
> required for the daemon to publish the endpoint.
|
||||
|
||||
Adverts don't sit on the relays forever:
|
||||
|
||||
@@ -167,14 +173,17 @@ transports:
|
||||
udp:
|
||||
bind_addr: "0.0.0.0:2121"
|
||||
advertise_on_nostr: true
|
||||
public: true
|
||||
external_addr: "203.0.113.45:2121"
|
||||
```
|
||||
|
||||
Replace `203.0.113.45:2121` with your actual public IP and
|
||||
port. The bare-IP form `external_addr: "203.0.113.45"` is also
|
||||
accepted; the daemon combines it with the bind port. You may
|
||||
set both `public: true` and `external_addr` together — the
|
||||
explicit override wins, with STUN as a logging cross-check.
|
||||
accepted; the daemon combines it with the bind port. `public:
|
||||
true` is still required as the master switch that gates UDP
|
||||
advertisement; setting `external_addr` alongside it wins, and
|
||||
STUN auto-discovery is skipped entirely (no logging
|
||||
cross-check).
|
||||
|
||||
`advertise_on_nostr: true` is the bit that says "include this
|
||||
transport in my published advert" — common to both paths.
|
||||
@@ -191,8 +200,11 @@ sudo systemctl status fips
|
||||
Status should show `active (running)`. Within a few seconds the
|
||||
daemon will:
|
||||
|
||||
1. Run a one-shot STUN observation against the default STUN
|
||||
servers to learn its public IP.
|
||||
1. Determine the address to advertise. If you set `external_addr`,
|
||||
the daemon uses it directly and skips STUN. If you set only
|
||||
`public: true`, the daemon runs a one-shot STUN observation
|
||||
against the default STUN servers and uses the reflexive IPv4 it
|
||||
learns.
|
||||
2. Build a Kind 37195 advert listing
|
||||
`udp:<public-ip>:2121` (and any other transports you have
|
||||
`advertise_on_nostr: true` on).
|
||||
@@ -200,12 +212,13 @@ daemon will:
|
||||
4. Publish it to the three default advert relays.
|
||||
5. Schedule a refresh every 30 minutes.
|
||||
|
||||
If STUN fails (for example, if the network blocks outbound
|
||||
UDP/3478), the daemon emits a WARN line in the journal and
|
||||
suppresses the UDP entry from the advert rather than publishing
|
||||
a wrong address. The link to `test-us01` from the previous
|
||||
tutorial keeps working regardless — only the publish side is
|
||||
gated on STUN.
|
||||
If you took the `public: true` path and STUN fails (for example,
|
||||
the network blocks outbound UDP/3478), the daemon emits a WARN
|
||||
line in the journal and suppresses the UDP entry from the advert
|
||||
rather than publishing a wrong address. The link to `test-us01`
|
||||
from the previous tutorial keeps working regardless — only the
|
||||
publish side is gated on STUN, and only on the STUN path. The
|
||||
`external_addr` path doesn't depend on STUN reachability at all.
|
||||
|
||||
Quick sanity check on the journal:
|
||||
|
||||
@@ -295,22 +308,24 @@ verifiable in Step 4.
|
||||
to them specifically or not. The test mesh's open-discovery
|
||||
nodes will pick you up automatically.
|
||||
|
||||
## If you're behind symmetric NAT
|
||||
## If your direct UDP advert isn't reachable
|
||||
|
||||
`public: true` + STUN works on most home and office NATs (the
|
||||
full-cone variety) and on nodes with a directly-bound public
|
||||
IP. It does *not* work on symmetric NAT, where the NAT mapping
|
||||
is keyed on (source-port, destination-host) so the IP/port
|
||||
your STUN server saw isn't the IP/port a different peer would
|
||||
see.
|
||||
`public: true` advertises the IP STUN observes paired with your
|
||||
listener's bind port. That advert is only useful if your listener
|
||||
really is reachable at that public IP/port — STUN can confirm the
|
||||
public IP but not that an unsolicited inbound packet to the bind
|
||||
port will make it through. The most common cause of the listener
|
||||
being unreachable is symmetric NAT (where the public port a peer
|
||||
sees varies per remote endpoint), but other configurations can
|
||||
have the same effect.
|
||||
|
||||
For symmetric-NAT networks the alternative is `udp:nat` mode,
|
||||
which advertises a placeholder `udp:nat` endpoint along with
|
||||
the daemon's signaling-relay and STUN-server lists, and
|
||||
performs UDP hole-punching at dial time. Both sides need to be
|
||||
running matching configs and at least one side needs a
|
||||
non-symmetric NAT for the punch to succeed; symmetric on both
|
||||
sides is not reliably traversable and will time out.
|
||||
When direct UDP advertising can't be relied on, the alternative
|
||||
is `udp:nat` mode, which advertises a placeholder `udp:nat`
|
||||
endpoint along with the daemon's signaling-relay and STUN-server
|
||||
lists, and performs UDP hole-punching at dial time. Hole-punching
|
||||
is best-effort — it works reliably when both sides are full-cone
|
||||
or port-restricted, and symmetric NAT on either side typically
|
||||
defeats it. Both sides need matching configs.
|
||||
|
||||
The minimal config switch:
|
||||
|
||||
@@ -339,7 +354,7 @@ discovery:
|
||||
|
||||
For the full setup including peer-side config and the punch-
|
||||
duration knob, see
|
||||
[../how-to/enable-nostr-discovery.md § Capability 2c](../how-to/enable-nostr-discovery.md#sub-scenario-2c-udp-hole-punching-for-nodes-behind-nat).
|
||||
[../how-to/enable-nostr-discovery.md § When the node is behind NAT](../how-to/enable-nostr-discovery.md#when-the-node-is-behind-nat).
|
||||
|
||||
Separately from NAT considerations, FIPS supports running a
|
||||
node behind a Tor onion service as a deployment shape in its
|
||||
@@ -347,7 +362,7 @@ own right — chosen for the privacy, anonymity, and
|
||||
censorship-resistance properties it brings, not as a fallback
|
||||
when UDP or TCP fail. If those properties are an independent
|
||||
goal for your node, see
|
||||
[../how-to/enable-nostr-discovery.md § Sub-scenario 2b](../how-to/enable-nostr-discovery.md#sub-scenario-2b-tor-onion-node)
|
||||
[../how-to/enable-nostr-discovery.md § Tor onion node](../how-to/enable-nostr-discovery.md#tor-onion-node)
|
||||
and
|
||||
[../how-to/deploy-tor-onion.md](../how-to/deploy-tor-onion.md).
|
||||
|
||||
@@ -364,10 +379,12 @@ If your advert doesn't appear on the relays:
|
||||
with a non-public address (e.g., `10.x.x.x` or
|
||||
`192.168.x.x`), STUN didn't see your real public IP — likely
|
||||
you're behind a CGNAT that NATs your STUN traffic too, or a
|
||||
corporate firewall that proxies it. Switch to the
|
||||
`external_addr` form from Step 2 with your actual public
|
||||
IP, or replace `public: true` with the bound interface IP
|
||||
directly under `bind_addr`.
|
||||
corporate firewall that proxies it. Two correct fixes:
|
||||
(a) keep `public: true` and add `external_addr: <your-IP>`
|
||||
(the explicit override wins and skips STUN); or (b) bind
|
||||
directly to your public interface
|
||||
(`bind_addr: <pub-ip>:2121`) and keep `advertise_on_nostr:
|
||||
true` and `public: true`. Don't drop those flags.
|
||||
|
||||
- **Relay reachability.** `nak req` against a relay you can
|
||||
reach but no events return — possibly the publish failed
|
||||
@@ -379,9 +396,14 @@ If your advert doesn't appear on the relays:
|
||||
nak req ... wss://offchain.pub
|
||||
```
|
||||
|
||||
- **`advertise_on_nostr` typo.** YAML is case-sensitive and
|
||||
silently ignores unknown keys. If `nak` returns no advert at
|
||||
all, double-check the spelling on the UDP block and that
|
||||
- **`advertise_on_nostr` typo.** YAML is case-sensitive. The
|
||||
config parser rejects unknown keys via
|
||||
`serde(deny_unknown_fields)` on the per-section structs, so a
|
||||
misspelled field will refuse the daemon's start with a
|
||||
parse-error line in the journal naming the unknown field.
|
||||
If the daemon is running but `nak` returns no advert, the
|
||||
field was accepted but something else is wrong; double-check
|
||||
the spelling on the UDP block and that
|
||||
`discovery.nostr.advertise: true` is also set.
|
||||
|
||||
## What's next
|
||||
|
||||
@@ -110,8 +110,12 @@ You should be coming out of
|
||||
[advertise-your-node](advertise-your-node.md) with:
|
||||
|
||||
- Persistent identity, advertising enabled
|
||||
(`discovery.nostr.advertise: true`), UDP advertising on
|
||||
Nostr (`transports.udp.advertise_on_nostr: true`).
|
||||
(`discovery.nostr.advertise: true`), and either the
|
||||
direct-UDP path
|
||||
(`transports.udp.advertise_on_nostr: true`,
|
||||
`transports.udp.public: true`) or the `udp:nat` path
|
||||
(`transports.udp.advertise_on_nostr: true`,
|
||||
`transports.udp.public: false`) from the previous tutorial.
|
||||
- A static `test-us01` peer entry that the daemon dials
|
||||
outbound; possibly an inbound `test-us03` peer (the
|
||||
open-discovery test mesh node that dialed in after seeing
|
||||
@@ -288,11 +292,12 @@ the previous tutorial:
|
||||
WebSocket connection to the relays is failing repeatedly,
|
||||
no adverts arrive. Look for relay-connection errors in
|
||||
`sudo journalctl -u fips -n 200`.
|
||||
- **`policy: open` typo.** YAML accepts and ignores unknown
|
||||
values silently. If `fipsctl show status` (or the daemon's
|
||||
startup log) shows `policy: configured_only`, the YAML
|
||||
didn't parse the new value — re-check spelling and
|
||||
indentation.
|
||||
- **`policy: open` typo.** YAML is case-sensitive, and the
|
||||
`policy` field is a serde enum that rejects unknown values —
|
||||
a misspelled value produces a config-parse error at startup
|
||||
rather than a silent fall-back. If the daemon refuses to
|
||||
start, check `sudo journalctl -u fips -n 200` for the
|
||||
parse-error line naming the field and value.
|
||||
|
||||
If too many peers are appearing and you want to dial down:
|
||||
|
||||
|
||||
@@ -125,9 +125,10 @@ Two knobs, one job each:
|
||||
`wss://offchain.pub`) and is now able to query and consume
|
||||
adverts.
|
||||
- `advertise: false` keeps the publish side off. Your daemon
|
||||
will not publish an advert of its own at this stage. (This
|
||||
is the default, but it is good practice to make the choice
|
||||
explicit while you're learning.)
|
||||
will not publish an advert of its own at this stage. The
|
||||
default is `true`, so we are setting it explicitly to
|
||||
disable advertising for this consume-only tutorial. The next
|
||||
tutorial flips it back on.
|
||||
|
||||
## Step 3: Switch the peer entry to `via_nostr`
|
||||
|
||||
@@ -270,8 +271,9 @@ If the link does not come up:
|
||||
[advertise-your-node](advertise-your-node.md) publishes your
|
||||
daemon's UDP endpoint on Nostr so other operators can add
|
||||
you to their `peers:` list with `via_nostr: true` and reach
|
||||
you the way you just reached `test-us01`. Includes a section
|
||||
on `udp:nat` NAT traversal for symmetric-NAT networks.
|
||||
you the way you just reached `test-us01`. Includes a short
|
||||
final section on `udp:nat`, the best-effort hole-punching
|
||||
path for nodes without a directly reachable UDP endpoint.
|
||||
|
||||
- **Discover peers with no prior configuration.**
|
||||
[open-discovery](open-discovery.md) switches your daemon to
|
||||
|
||||
@@ -10,7 +10,7 @@ WORKDIR /build
|
||||
COPY Cargo.toml Cargo.lock rust-toolchain.toml build.rs ./
|
||||
COPY src ./src
|
||||
|
||||
RUN cargo build --release --no-default-features --features tui && \
|
||||
RUN cargo build --release && \
|
||||
cp target/release/fips target/release/fipsctl target/release/fipstop /usr/local/bin/
|
||||
|
||||
# ── Runtime stage ──
|
||||
|
||||
@@ -10,7 +10,7 @@ depends=('gcc-libs' 'glibc')
|
||||
makedepends=('cargo')
|
||||
optdepends=('systemd-resolved: .fips DNS resolution')
|
||||
conflicts=('fips-git')
|
||||
backup=('etc/fips/fips.yaml' 'etc/fips/hosts')
|
||||
backup=('etc/fips/fips.yaml' 'etc/fips/hosts' 'etc/fips/fips.nft')
|
||||
install=fips.install
|
||||
source=("$pkgname-$pkgver.tar.gz::https://github.com/jmcorgan/fips/archive/v$pkgver.tar.gz"
|
||||
"fips.sysusers"
|
||||
@@ -51,10 +51,12 @@ package() {
|
||||
install -Dm0644 packaging/debian/fips.service "$pkgdir/usr/lib/systemd/system/fips.service"
|
||||
install -Dm0644 packaging/debian/fips-dns.service "$pkgdir/usr/lib/systemd/system/fips-dns.service"
|
||||
install -Dm0644 packaging/debian/fips-gateway.service "$pkgdir/usr/lib/systemd/system/fips-gateway.service"
|
||||
install -Dm0644 packaging/debian/fips-firewall.service "$pkgdir/usr/lib/systemd/system/fips-firewall.service"
|
||||
|
||||
# Config files (from packaging/common/)
|
||||
install -Dm0600 packaging/common/fips.yaml "$pkgdir/etc/fips/fips.yaml"
|
||||
install -Dm0644 packaging/common/hosts "$pkgdir/etc/fips/hosts"
|
||||
install -Dm0644 packaging/common/fips.nft "$pkgdir/etc/fips/fips.nft"
|
||||
|
||||
# System integration (from local source files in $srcdir)
|
||||
install -Dm0644 "$srcdir/fips.sysusers" "$pkgdir/usr/lib/sysusers.d/fips.conf"
|
||||
|
||||
@@ -11,7 +11,7 @@ makedepends=('cargo' 'git')
|
||||
optdepends=('systemd-resolved: .fips DNS resolution')
|
||||
provides=('fips')
|
||||
conflicts=('fips')
|
||||
backup=('etc/fips/fips.yaml' 'etc/fips/hosts')
|
||||
backup=('etc/fips/fips.yaml' 'etc/fips/hosts' 'etc/fips/fips.nft')
|
||||
install=fips.install
|
||||
source=("fips::git+https://github.com/jmcorgan/fips.git"
|
||||
"fips.sysusers"
|
||||
@@ -52,14 +52,18 @@ package() {
|
||||
install -Dm0755 target/release/fips "$pkgdir/usr/bin/fips"
|
||||
install -Dm0755 target/release/fipsctl "$pkgdir/usr/bin/fipsctl"
|
||||
install -Dm0755 target/release/fipstop "$pkgdir/usr/bin/fipstop"
|
||||
install -Dm0755 target/release/fips-gateway "$pkgdir/usr/bin/fips-gateway"
|
||||
|
||||
# Systemd service files (from packaging/debian/ -- correct /usr/bin/ paths)
|
||||
install -Dm0644 packaging/debian/fips.service "$pkgdir/usr/lib/systemd/system/fips.service"
|
||||
install -Dm0644 packaging/debian/fips-dns.service "$pkgdir/usr/lib/systemd/system/fips-dns.service"
|
||||
install -Dm0644 packaging/debian/fips-gateway.service "$pkgdir/usr/lib/systemd/system/fips-gateway.service"
|
||||
install -Dm0644 packaging/debian/fips-firewall.service "$pkgdir/usr/lib/systemd/system/fips-firewall.service"
|
||||
|
||||
# Config files (from packaging/common/)
|
||||
install -Dm0600 packaging/common/fips.yaml "$pkgdir/etc/fips/fips.yaml"
|
||||
install -Dm0644 packaging/common/hosts "$pkgdir/etc/fips/hosts"
|
||||
install -Dm0644 packaging/common/fips.nft "$pkgdir/etc/fips/fips.nft"
|
||||
|
||||
# System integration (from local source files in $srcdir)
|
||||
install -Dm0644 "$srcdir/fips.sysusers" "$pkgdir/usr/lib/sysusers.d/fips.conf"
|
||||
|
||||
@@ -23,6 +23,22 @@ Both PKGBUILDs reference files from `packaging/debian/` (service files) and
|
||||
`packaging/common/` (config files) at build time. These are pulled from the
|
||||
source tree during `package()`, not from this directory.
|
||||
|
||||
### What Gets Installed
|
||||
|
||||
Both PKGBUILDs install the same payload, kept at parity with the Debian
|
||||
package:
|
||||
|
||||
- Binaries: `fips`, `fipsctl`, `fipstop`, `fips-gateway`
|
||||
- Systemd units: `fips.service`, `fips-dns.service`, `fips-gateway.service`,
|
||||
`fips-firewall.service`
|
||||
- Config: `/etc/fips/fips.yaml`, `/etc/fips/hosts`, `/etc/fips/fips.nft`
|
||||
- sysusers/tmpfiles fragments for the `fips` group and `/run/fips/`
|
||||
|
||||
The `fips.nft` baseline is shipped as a conffile (listed in `backup=()`) so
|
||||
operator edits to the nftables ruleset survive package upgrades.
|
||||
`fips-firewall.service` is shipped disabled by default, matching the Debian
|
||||
package: operators opt in by enabling it explicitly.
|
||||
|
||||
## Local Build and Validation
|
||||
|
||||
Build and validate the `-git` package locally using the Makefile target:
|
||||
|
||||
@@ -15,12 +15,24 @@ sudo ./install.sh
|
||||
| fips (daemon) | /usr/local/bin/fips |
|
||||
| fipsctl (CLI) | /usr/local/bin/fipsctl |
|
||||
| fipstop (TUI) | /usr/local/bin/fipstop |
|
||||
| fips-gateway (LAN bridge) | /usr/local/bin/fips-gateway |
|
||||
| Configuration | /etc/fips/fips.yaml |
|
||||
| Identity key | /etc/fips/fips.key (auto-generated) |
|
||||
| Public key | /etc/fips/fips.pub (auto-generated) |
|
||||
| systemd unit | /etc/systemd/system/fips.service |
|
||||
| Hosts file | /etc/fips/hosts |
|
||||
| Firewall baseline | /etc/fips/fips.nft |
|
||||
| Firewall drop-in directory | /etc/fips/fips.d/ |
|
||||
| Daemon unit | /etc/systemd/system/fips.service (enabled) |
|
||||
| DNS routing unit | /etc/systemd/system/fips-dns.service (enabled) |
|
||||
| Gateway unit | /etc/systemd/system/fips-gateway.service (NOT enabled) |
|
||||
| Firewall unit | /etc/systemd/system/fips-firewall.service (NOT enabled) |
|
||||
| DNS helpers | /usr/lib/fips/fips-dns-{setup,teardown} |
|
||||
|
||||
A system group `fips` is created for control socket access.
|
||||
A system group `fips` is created for control socket access. By
|
||||
default, only `fips.service` and `fips-dns.service` are enabled at
|
||||
install time. `fips-gateway.service` and `fips-firewall.service`
|
||||
are installed but require explicit operator opt-in (see the
|
||||
sections below).
|
||||
|
||||
## Post-Install Configuration
|
||||
|
||||
@@ -101,26 +113,69 @@ peers:
|
||||
connect_policy: auto_connect
|
||||
```
|
||||
|
||||
### 5. DNS Resolver (optional, requires systemd-resolved)
|
||||
### 5. DNS Resolver
|
||||
|
||||
FIPS includes a DNS responder for `.fips` domain names (port 5354).
|
||||
On systems running `systemd-resolved`, the installer automatically enables
|
||||
`fips-dns.service` to route `.fips` queries to the FIPS resolver.
|
||||
FIPS includes a DNS responder for `.fips` domain names that listens on
|
||||
`fips0` and on `[::1]:5354`. The `fips-dns.service` helper detects the
|
||||
host's DNS routing system and configures it to forward `.fips` queries
|
||||
to the responder. Backends tried in order:
|
||||
|
||||
If `systemd-resolved` is not running at install time, DNS integration is
|
||||
skipped. To enable it later (after starting `systemd-resolved`):
|
||||
1. systemd `dns-delegate` drop-in (systemd >= 258, declarative)
|
||||
2. `systemd-resolved` global drop-in via `/etc/systemd/resolved.conf.d/`
|
||||
3. `systemd-resolved` per-link `resolvectl` (legacy fallback)
|
||||
4. `dnsmasq` (standalone, drops a config in `/etc/dnsmasq.d/`)
|
||||
5. NetworkManager with the `dnsmasq` plugin
|
||||
|
||||
If none of the supported backends is detected, `fips-dns-setup` logs a
|
||||
warning with manual instructions and exits cleanly. The daemon itself
|
||||
keeps working; only the host's `.fips` resolution is left unwired.
|
||||
|
||||
The installer enables `fips-dns.service` automatically. To disable
|
||||
or re-enable later:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now fips-dns.service
|
||||
sudo systemctl disable --now fips-dns.service # disable
|
||||
sudo systemctl enable --now fips-dns.service # re-enable
|
||||
```
|
||||
|
||||
For manual configuration without `fips-dns.service`:
|
||||
### 6. Mesh-interface firewall baseline (optional)
|
||||
|
||||
`fips.nft` is a default-deny baseline for inbound traffic on the
|
||||
`fips0` mesh interface. It is shipped as `/etc/fips/fips.nft` (not
|
||||
loaded by default) along with a disabled `fips-firewall.service`
|
||||
unit. Enable it explicitly:
|
||||
|
||||
```bash
|
||||
sudo resolvectl dns fips0 127.0.0.1:5354
|
||||
sudo resolvectl domain fips0 ~fips
|
||||
sudo systemctl enable --now fips-firewall.service
|
||||
```
|
||||
|
||||
The baseline polices only `fips0`, leaving Docker, Tor, the host
|
||||
firewall, and other interfaces untouched. Outbound from `fips0` is
|
||||
unrestricted; inbound is dropped except for replies to outbound
|
||||
flows, ICMPv6 echo-request, and any operator drop-ins under
|
||||
`/etc/fips/fips.d/*.nft`. Read the comments at the top of
|
||||
`/etc/fips/fips.nft` for the full policy and how to add per-service
|
||||
allow rules.
|
||||
|
||||
### 7. Outbound LAN gateway (optional)
|
||||
|
||||
`fips-gateway` bridges unmodified LAN hosts to `.fips` destinations
|
||||
through a DNS-allocated virtual IPv6 pool and kernel nftables NAT.
|
||||
The binary is installed at `/usr/local/bin/fips-gateway` and a
|
||||
`fips-gateway.service` unit ships disabled by default.
|
||||
|
||||
To enable it, configure the gateway block in `/etc/fips/fips.yaml`,
|
||||
then:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable --now fips-gateway.service
|
||||
```
|
||||
|
||||
The unit `Requires=fips.service`, waits up to 30 seconds for `fips0`
|
||||
to come up, and runs `fips-gateway --config /etc/fips/fips.yaml`.
|
||||
Inbound port-forward rules can be added in the same `gateway:`
|
||||
block.
|
||||
|
||||
## Firewall Ports
|
||||
|
||||
| Port | Protocol | Purpose |
|
||||
@@ -130,14 +185,28 @@ sudo resolvectl domain fips0 ~fips
|
||||
|
||||
## Service Management
|
||||
|
||||
The install ships four units. `fips.service` and `fips-dns.service`
|
||||
are enabled at install time. `fips-gateway.service` and
|
||||
`fips-firewall.service` are installed but disabled until the
|
||||
operator opts in.
|
||||
|
||||
```bash
|
||||
# Start / stop / restart
|
||||
# Daemon
|
||||
sudo systemctl start fips
|
||||
sudo systemctl stop fips
|
||||
sudo systemctl restart fips
|
||||
|
||||
# View logs
|
||||
# DNS routing helper
|
||||
sudo systemctl restart fips-dns
|
||||
|
||||
# Optional services (opt-in)
|
||||
sudo systemctl enable --now fips-firewall # mesh-interface nftables baseline
|
||||
sudo systemctl enable --now fips-gateway # outbound LAN gateway
|
||||
|
||||
# View logs (any of the units above)
|
||||
sudo journalctl -u fips -f
|
||||
sudo journalctl -u fips-gateway -f
|
||||
sudo journalctl -u fips-firewall -f
|
||||
|
||||
# Switch to debug logging
|
||||
sudo systemctl set-environment RUST_LOG=debug
|
||||
|
||||
@@ -98,7 +98,7 @@ rm -rf "${STAGING_DIR}"
|
||||
mkdir -p "${STAGING_DIR}"
|
||||
|
||||
# Copy binaries
|
||||
for bin in fips fipsctl fipstop; do
|
||||
for bin in fips fipsctl fipstop fips-gateway; do
|
||||
if [[ ! -f "${BINARY_DIR}/${bin}" ]]; then
|
||||
echo "Missing binary: ${BINARY_DIR}/${bin}" >&2
|
||||
exit 1
|
||||
@@ -111,15 +111,24 @@ if ! command -v "${STRIP_BIN}" &>/dev/null; then
|
||||
echo "Strip tool not found: ${STRIP_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
"${STRIP_BIN}" "${STAGING_DIR}/fips" "${STAGING_DIR}/fipsctl" "${STAGING_DIR}/fipstop"
|
||||
"${STRIP_BIN}" \
|
||||
"${STAGING_DIR}/fips" \
|
||||
"${STAGING_DIR}/fipsctl" \
|
||||
"${STAGING_DIR}/fipstop" \
|
||||
"${STAGING_DIR}/fips-gateway"
|
||||
|
||||
# Copy packaging files
|
||||
cp "${SCRIPT_DIR}/install.sh" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/uninstall.sh" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/fips.service" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/fips-dns.service" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/fips-gateway.service" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/fips-firewall.service" "${STAGING_DIR}/"
|
||||
cp "${PACKAGING_DIR}/common/fips.yaml" "${STAGING_DIR}/"
|
||||
cp "${PACKAGING_DIR}/common/hosts" "${STAGING_DIR}/"
|
||||
cp "${PACKAGING_DIR}/common/fips.nft" "${STAGING_DIR}/"
|
||||
cp "${PACKAGING_DIR}/common/fips-dns-setup" "${STAGING_DIR}/"
|
||||
cp "${PACKAGING_DIR}/common/fips-dns-teardown" "${STAGING_DIR}/"
|
||||
cp "${SCRIPT_DIR}/README.install.md" "${STAGING_DIR}/"
|
||||
|
||||
chmod +x "${STAGING_DIR}/install.sh" "${STAGING_DIR}/uninstall.sh"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Firewall is not enabled by default. Enable with: systemctl enable --now fips-firewall
|
||||
[Unit]
|
||||
Description=FIPS mesh-interface nftables baseline
|
||||
Documentation=file:///etc/fips/fips.nft
|
||||
Before=fips.service
|
||||
ConditionPathExists=/etc/fips/fips.nft
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/sbin/nft -f /etc/fips/fips.nft
|
||||
ExecStop=-/usr/sbin/nft delete table inet fips
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,33 @@
|
||||
# Gateway is not enabled by default. Enable with: systemctl enable --now fips-gateway
|
||||
[Unit]
|
||||
Description=FIPS Outbound LAN Gateway
|
||||
After=fips.service
|
||||
Requires=fips.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Wait up to 30s for the fips daemon's TUN to come up before launching
|
||||
# the gateway. fips-gateway hard-fails on a missing fips0 interface; with
|
||||
# Requires=fips.service the daemon unit is started, but the TUN device
|
||||
# itself is brought up some time after the daemon process starts. Without
|
||||
# this wait the gateway's first ExecStart tends to lose a race on fresh
|
||||
# boot, exit, and rely on Restart=on-failure for ~5s of recovery. Bounded
|
||||
# wait avoids the noisy restart cycle while still surfacing real failures
|
||||
# (if fips0 never appears, the wait expires and the gateway's existing
|
||||
# error path runs).
|
||||
ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do ip link show fips0 >/dev/null 2>&1 && exit 0; sleep 1; done; echo "fips0 did not appear within 30s" >&2; exit 1'
|
||||
ExecStart=/usr/local/bin/fips-gateway --config /etc/fips/fips.yaml
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
TimeoutStopSec=15
|
||||
|
||||
# Security hardening (needs CAP_NET_ADMIN for nftables and proxy NDP)
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=no
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -9,10 +9,15 @@
|
||||
# /usr/local/bin/fips Daemon binary
|
||||
# /usr/local/bin/fipsctl CLI query tool
|
||||
# /usr/local/bin/fipstop TUI monitor
|
||||
# /usr/local/bin/fips-gateway Outbound LAN gateway binary (opt-in)
|
||||
# /etc/fips/fips.yaml Configuration (preserved if exists)
|
||||
# /etc/fips/hosts Host-to-npub mappings (preserved if exists)
|
||||
# /etc/systemd/system/fips.service systemd unit
|
||||
# /etc/systemd/system/fips-dns.service DNS routing for .fips domain
|
||||
# /etc/fips/fips.nft Mesh-interface nftables baseline (preserved if exists)
|
||||
# /etc/fips/fips.d/ Operator drop-in directory for nft rules
|
||||
# /etc/systemd/system/fips.service Daemon unit (enabled)
|
||||
# /etc/systemd/system/fips-dns.service DNS routing for .fips domain (enabled)
|
||||
# /etc/systemd/system/fips-gateway.service Gateway unit (NOT enabled; opt-in)
|
||||
# /etc/systemd/system/fips-firewall.service Firewall baseline unit (NOT enabled; opt-in)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -60,6 +65,9 @@ install -m 0755 "${SCRIPT_DIR}/fipsctl" "${INSTALL_PREFIX}/bin/fipsctl"
|
||||
if [ -f "${SCRIPT_DIR}/fipstop" ]; then
|
||||
install -m 0755 "${SCRIPT_DIR}/fipstop" "${INSTALL_PREFIX}/bin/fipstop"
|
||||
fi
|
||||
if [ -f "${SCRIPT_DIR}/fips-gateway" ]; then
|
||||
install -m 0755 "${SCRIPT_DIR}/fips-gateway" "${INSTALL_PREFIX}/bin/fips-gateway"
|
||||
fi
|
||||
|
||||
# --- Install configuration ---
|
||||
|
||||
@@ -82,7 +90,27 @@ else
|
||||
echo "Hosts file installed to ${HOSTS_FILE}"
|
||||
fi
|
||||
|
||||
# --- Install systemd unit ---
|
||||
# Mesh-interface nftables baseline. Preserved on upgrade like fips.yaml
|
||||
# so operator edits aren't clobbered.
|
||||
NFT_FILE="${CONFIG_DIR}/fips.nft"
|
||||
if [ -f "${NFT_FILE}" ]; then
|
||||
echo "Firewall baseline exists at ${NFT_FILE}, not overwriting."
|
||||
install -m 0644 "${SCRIPT_DIR}/fips.nft" "${CONFIG_DIR}/fips.nft.template"
|
||||
echo " New template installed as ${CONFIG_DIR}/fips.nft.template"
|
||||
elif [ -f "${SCRIPT_DIR}/fips.nft" ]; then
|
||||
install -m 0644 "${SCRIPT_DIR}/fips.nft" "${NFT_FILE}"
|
||||
echo "Firewall baseline installed to ${NFT_FILE}"
|
||||
fi
|
||||
|
||||
# Drop-in directory for operator nftables rules included by
|
||||
# /etc/fips/fips.nft. Empty by default; the include glob matches
|
||||
# nothing cleanly out of the box.
|
||||
if [ ! -d "${CONFIG_DIR}/fips.d" ]; then
|
||||
install -d -m 0755 "${CONFIG_DIR}/fips.d"
|
||||
echo "Drop-in directory created at ${CONFIG_DIR}/fips.d/"
|
||||
fi
|
||||
|
||||
# --- Install systemd units ---
|
||||
|
||||
was_active=false
|
||||
if systemctl is-active --quiet fips.service 2>/dev/null; then
|
||||
@@ -100,9 +128,23 @@ fi
|
||||
|
||||
install -m 0644 "${SCRIPT_DIR}/fips.service" "${SYSTEMD_DIR}/fips.service"
|
||||
install -m 0644 "${SCRIPT_DIR}/fips-dns.service" "${SYSTEMD_DIR}/fips-dns.service"
|
||||
if [ -f "${SCRIPT_DIR}/fips-gateway.service" ]; then
|
||||
install -m 0644 "${SCRIPT_DIR}/fips-gateway.service" "${SYSTEMD_DIR}/fips-gateway.service"
|
||||
fi
|
||||
if [ -f "${SCRIPT_DIR}/fips-firewall.service" ]; then
|
||||
install -m 0644 "${SCRIPT_DIR}/fips-firewall.service" "${SYSTEMD_DIR}/fips-firewall.service"
|
||||
fi
|
||||
# DNS helpers ship flat in the tarball alongside install.sh; from a
|
||||
# source checkout they live under packaging/common/. Resolve from
|
||||
# either layout.
|
||||
install -d -m 0755 /usr/lib/fips
|
||||
install -m 0755 "${SCRIPT_DIR}/../common/fips-dns-setup" /usr/lib/fips/fips-dns-setup
|
||||
install -m 0755 "${SCRIPT_DIR}/../common/fips-dns-teardown" /usr/lib/fips/fips-dns-teardown
|
||||
if [ -f "${SCRIPT_DIR}/fips-dns-setup" ]; then
|
||||
install -m 0755 "${SCRIPT_DIR}/fips-dns-setup" /usr/lib/fips/fips-dns-setup
|
||||
install -m 0755 "${SCRIPT_DIR}/fips-dns-teardown" /usr/lib/fips/fips-dns-teardown
|
||||
else
|
||||
install -m 0755 "${SCRIPT_DIR}/../common/fips-dns-setup" /usr/lib/fips/fips-dns-setup
|
||||
install -m 0755 "${SCRIPT_DIR}/../common/fips-dns-teardown" /usr/lib/fips/fips-dns-teardown
|
||||
fi
|
||||
systemctl daemon-reload
|
||||
echo "systemd units and DNS scripts installed."
|
||||
|
||||
@@ -149,6 +191,16 @@ echo ""
|
||||
echo "Start the service:"
|
||||
echo " sudo systemctl start fips"
|
||||
echo ""
|
||||
echo "Optional services (NOT enabled by default):"
|
||||
echo ""
|
||||
echo " Mesh-interface firewall baseline (default-deny on fips0):"
|
||||
echo " sudo systemctl enable --now fips-firewall.service"
|
||||
echo " Operator drop-ins under /etc/fips/fips.d/*.nft"
|
||||
echo ""
|
||||
echo " Outbound LAN gateway (bridge unmodified LAN hosts to .fips):"
|
||||
echo " sudo systemctl enable --now fips-gateway.service"
|
||||
echo " Configure under the gateway: section of ${CONFIG_FILE}"
|
||||
echo ""
|
||||
echo "Monitor:"
|
||||
echo " sudo journalctl -u fips -f"
|
||||
echo " fipsctl show status"
|
||||
|
||||
@@ -18,30 +18,26 @@ if [ "$(id -u)" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Stop and disable service ---
|
||||
# --- Stop and disable services ---
|
||||
# Stop dependents (firewall, gateway, dns) before the daemon to avoid
|
||||
# noisy "fips0 disappeared" cascades during the teardown.
|
||||
|
||||
if systemctl is-active --quiet fips-dns.service 2>/dev/null; then
|
||||
echo "Stopping fips-dns service..."
|
||||
systemctl stop fips-dns.service
|
||||
fi
|
||||
|
||||
if systemctl is-enabled --quiet fips-dns.service 2>/dev/null; then
|
||||
systemctl disable fips-dns.service
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet fips.service 2>/dev/null; then
|
||||
echo "Stopping fips service..."
|
||||
systemctl stop fips.service
|
||||
fi
|
||||
|
||||
if systemctl is-enabled --quiet fips.service 2>/dev/null; then
|
||||
systemctl disable fips.service
|
||||
fi
|
||||
for unit in fips-gateway.service fips-firewall.service fips-dns.service fips.service; do
|
||||
if systemctl is-active --quiet "${unit}" 2>/dev/null; then
|
||||
echo "Stopping ${unit}..."
|
||||
systemctl stop "${unit}"
|
||||
fi
|
||||
if systemctl is-enabled --quiet "${unit}" 2>/dev/null; then
|
||||
systemctl disable "${unit}"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Remove systemd units ---
|
||||
|
||||
rm -f /etc/systemd/system/fips.service
|
||||
rm -f /etc/systemd/system/fips-dns.service
|
||||
rm -f /etc/systemd/system/fips-gateway.service
|
||||
rm -f /etc/systemd/system/fips-firewall.service
|
||||
rm -rf /usr/lib/fips/
|
||||
systemctl daemon-reload
|
||||
echo "systemd units and DNS scripts removed."
|
||||
@@ -57,7 +53,7 @@ rm -f /etc/tmpfiles.d/fips.conf
|
||||
|
||||
# --- Remove binaries ---
|
||||
|
||||
rm -f /usr/local/bin/fips /usr/local/bin/fipsctl /usr/local/bin/fipstop
|
||||
rm -f /usr/local/bin/fips /usr/local/bin/fipsctl /usr/local/bin/fipstop /usr/local/bin/fips-gateway
|
||||
echo "Binaries removed."
|
||||
|
||||
# --- Optionally remove configuration and group ---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FIPS Tor test node A — socks5-outbound
|
||||
#
|
||||
# Connects outbound to vps-chi (217.77.8.91:443) via Tor SOCKS5 proxy.
|
||||
# Connects outbound to test-us01 (217.77.8.91:443) via Tor SOCKS5 proxy.
|
||||
# Identity generated per-run to avoid mesh clashes with parallel tests.
|
||||
|
||||
node:
|
||||
@@ -21,7 +21,7 @@ transports:
|
||||
|
||||
peers:
|
||||
- npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
|
||||
alias: "vps-chi"
|
||||
alias: "test-us01"
|
||||
addresses:
|
||||
- transport: tor
|
||||
addr: "217.77.8.91:443"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FIPS Tor test node B — socks5-outbound
|
||||
#
|
||||
# Connects outbound to vps-chi (217.77.8.91:443) via Tor SOCKS5 proxy.
|
||||
# Connects outbound to test-us01 (217.77.8.91:443) via Tor SOCKS5 proxy.
|
||||
# Identity generated per-run to avoid mesh clashes with parallel tests.
|
||||
|
||||
node:
|
||||
@@ -21,7 +21,7 @@ transports:
|
||||
|
||||
peers:
|
||||
- npub: "npub1qmc3cvfz0yu2hx96nq3gp55zdan2qclealn7xshgr448d3nh6lks7zel98"
|
||||
alias: "vps-chi"
|
||||
alias: "test-us01"
|
||||
addresses:
|
||||
- transport: tor
|
||||
addr: "217.77.8.91:443"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Tor transport integration test — socks5-outbound
|
||||
#
|
||||
# Topology:
|
||||
# [fips-a] --tor/socks5--> vps-chi (217.77.8.91:443) <--tor/socks5-- [fips-b]
|
||||
# [fips-a] --tor/socks5--> test-us01 (217.77.8.91:443) <--tor/socks5-- [fips-b]
|
||||
#
|
||||
# Both FIPS nodes connect outbound through a local Tor daemon's SOCKS5
|
||||
# proxy to vps-chi's TCP listener. vps-chi routes between them.
|
||||
# proxy to test-us01's TCP listener. test-us01 routes between them.
|
||||
# Ping between fips-a and fips-b validates the full Tor transport path.
|
||||
|
||||
networks:
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# Tor transport integration test.
|
||||
#
|
||||
# Validates end-to-end connectivity through a real Tor network:
|
||||
# fips-a --tor/socks5--> vps-chi <--tor/socks5-- fips-b
|
||||
# fips-a --tor/socks5--> test-us01 <--tor/socks5-- fips-b
|
||||
#
|
||||
# Both local FIPS nodes connect outbound through a local Tor daemon
|
||||
# to vps-chi's TCP listener (217.77.8.91:443). Once both are peered
|
||||
# with vps-chi, traffic between fips-a and fips-b is routed through it.
|
||||
# to test-us01's TCP listener (217.77.8.91:443). Once both are peered
|
||||
# with test-us01, traffic between fips-a and fips-b is routed through it.
|
||||
#
|
||||
# Each run generates ephemeral identities to avoid mesh clashes when
|
||||
# multiple instances of this test run concurrently.
|
||||
@@ -107,7 +107,7 @@ fi
|
||||
echo ""
|
||||
|
||||
# ── Phase 3: Wait for FIPS peers via Tor ─────────────────────────
|
||||
echo "Phase 3: Waiting for FIPS nodes to peer with vps-chi via Tor (up to ${MAX_WAIT_PEER}s)..."
|
||||
echo "Phase 3: Waiting for FIPS nodes to peer with test-us01 via Tor (up to ${MAX_WAIT_PEER}s)..."
|
||||
echo " (First SOCKS5 attempt may timeout while Tor builds circuits)"
|
||||
|
||||
peers_a=0
|
||||
@@ -199,7 +199,7 @@ print(f'{sum(trimmed)/len(trimmed):.1f}')
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo " Ping via Tor (routed through vps-chi):"
|
||||
echo " Ping via Tor (routed through test-us01):"
|
||||
ping_series fips-tor-a "$NPUB_B" "A → B"
|
||||
ping_series fips-tor-b "$NPUB_A" "B → A"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user