mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 00:04:54 +00:00
Cover the previously untested overlay advert publish/relay/consume
round-trip. The bilateral publish/subscribe path was a v0.3.0 release
gap: malformed adverts could panic consumers, broken signatures could
go undetected, and reverse-direction subscription was unverified.
Adds testing/nat/scripts/nostr-relay-test.sh (290 lines):
Phase 1+2 (combined): wait_for_peers on both nodes; pass on
bidirectional advert publish/subscribe round-trip + dial completed;
ping6 both directions confirms TUN-level reachability.
Phase 3 (malformed advert resilience): stdlib-only Python WebSocket
client publishes a syntactically valid Schnorr-signed Kind-37195
event whose `content` is gibberish (cannot deserialize as
OverlayAdvert). The relay enforces BIP-340 signature validity, so the
event reaches the consumers (rather than being dropped at the relay)
— a trivially-junk content payload is the right adversarial input.
Required ~80 lines of stdlib-only secp256k1 + BIP-340 in the script
(no new container deps). Asserts pidof fips on both nodes after the
publish, scans logs for panic markers, re-pings to prove the existing
peer link survives.
testing/nat/docker-compose.yml: new profile nostr-publish-consume
with two daemon services (nostr-pub-a 172.31.10.20, nostr-pub-b
172.31.10.21) on shared-lan, reusing the existing strfry relay
(172.31.10.30:7777) and STUN service (172.31.10.40:3478).
testing/nat/scripts/generate-configs.sh: 2-line allowlist update so
the new scenario flows through the existing config generator (rather
than forking a parallel one). Generated node-{a,b}.yaml + npubs.env
smoke-tested cleanly.
testing/ci-local.sh: NOSTR_RELAY_SUITES=(nostr-publish-consume)
array, run_nostr_publish_consume runner, dispatch in run_integration
and run_suite. Mirrors existing run_nat shape.
.github/workflows/ci.yml: one matrix row + 3 steps in the integration
job, gated on matrix.type == 'nostr-publish-consume'. Consumes the
same fips-linux artifact and fips-test:latest image as the existing
NAT suites.
Tor/TCP transport variants kept out of v0.3.0 scope; the structure
leaves room for nostr-publish-consume-tcp/-tor siblings later without
disturbing this baseline.
NAT Lab Harness
Real Docker-based NAT traversal integration tests for the mainline FIPS Nostr/STUN bootstrap path.
This harness spins up:
- two FIPS nodes
- a local Nostr relay
- a local STUN server
- one or two Linux router containers performing NAT with
iptables
For the NAT scenarios, the node LAN interfaces are not attached to
Docker bridge networks. The harness creates explicit veth pairs and
moves them into the node and router namespaces after docker compose up
so every packet must traverse the router namespace.
It covers three scenarios:
cone: both peers behind explicit namespace/veth full-cone emulation, UDP traversal succeedssymmetric: both peers behind symmetric-style NAT, UDP traversal fails, TCP fallback succeedslan: both peers share a LAN subnet, LAN targets are preferred over reflexive addresses
NAT model notes
The harness does not rely on plain Docker MASQUERADE for the cone case.
cone- uses explicit full-cone emulation in the router namespace
- outbound UDP is
SNATed to the router WAN address while preserving the source port - inbound UDP to the router WAN address is
DNATed back to the single LAN host regardless of remote source
symmetric- uses UDP
MASQUERADE --random-fully - outbound mappings may be port-randomized and are only reopened by matching conntrack state
- uses UDP
This distinction matters because plain MASQUERADE is convenient source NAT, but it does not by itself model the "accept from any remote once mapped" behavior expected from a full-cone NAT.
Prerequisites
- Docker with Compose support
- locally built
fips-test:latest
Build the test image with:
./testing/scripts/build.sh
Run
Run all scenarios:
./testing/nat/scripts/nat-test.sh
Run one scenario:
./testing/nat/scripts/nat-test.sh cone
./testing/nat/scripts/nat-test.sh symmetric
./testing/nat/scripts/nat-test.sh lan
Layout
docker-compose.yml- relay/STUN/WAN topology plus container definitions
node/- node bootstrap wrapper that waits for the injected veth interface
router/- NAT router image and
iptablessetup
- NAT router image and
stun/- minimal STUN binding responder
relay/- local
strfryconfig
- local
scripts/generate-configs.sh- derives ephemeral identities and writes per-scenario FIPS configs
scripts/setup-topology.sh- injects and configures the NAT LAN
vethpairs in the container namespaces
- injects and configures the NAT LAN
scripts/nat-test.sh- boots the lab, waits for convergence, and asserts the resulting path
Assertions
-
cone- both nodes connect
- connected transport is UDP
- active link remote addresses are on the WAN NAT subnet
-
symmetric- NAT bootstrap does not establish a UDP link
- fallback converges
- connected transport is TCP via router-published WAN addresses
-
lan- both nodes connect
- connected transport is UDP
- active link remote addresses stay on the shared LAN subnet