mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Admit rekey msg1 from established peers when addr forms differ
Companion to the ethernet `accept_connections: false` rekey-deadlock fix from earlier this release: the same dual-init failure mode shows up over UDP when peers register by hostname, and the existing addr_to_link-only carve-out in `should_admit_msg1` doesn't cover it. The carve-out's first predicate keys `addr_to_link` by the literal `TransportAddr` that `initiate_connection` inserted, which is the hostname-form when a peer config carries a hostname (e.g., `core-vm.tail65015.ts.net:2121`). Inbound packets always arrive with numeric source addrs because `udp_receive_loop` builds the `TransportAddr` from the `SocketAddr` the kernel reports via `recvfrom`. `TransportAddr` equality is byte-exact, so the two forms don't match and the lookup misses. With `udp.accept_connections: false` (or `udp.outbound_only: true`, which forces it false) the gate then rejects the rekey msg1 from an established peer. The dual-init tie-breaker stalls because the loser side never produces msg2; both sides retry indefinitely and the winner side keeps logging "Dual rekey initiation: we win, dropping their msg1" at 1Hz. The earlier ethernet fix didn't generalize to this variant because ethernet TransportAddrs are always numeric MAC bytes — both the config-time form and the inbound-arrival form match identically. Add a second predicate to `should_admit_msg1`: an active peer's `current_addr()` matching `(transport_id, remote_addr)`. `current_addr` is updated and refreshed from inbound encrypted-frame source addrs (`handlers/encrypted.rs`), which are always numeric `SocketAddr`-form, so this catches the established peer regardless of how its `addr_to_link` key was originally inserted. The fast `addr_to_link` check stays first; the iteration over peers is bounded by peer count and only runs when the first predicate misses. Regression coverage in this commit: - Unit test `test_should_admit_msg1_admits_rekey_when_addr_form_differs` in `src/node/tests/handshake.rs`. Constructs the failing scenario in-process: `addr_to_link` populated with hostname-form key, peer's `current_addr` at the resolved numeric form, query with numeric form. Without the new predicate this fails immediately. - New integration topology `rekey-outbound-only` plus matching docker-compose profile. Same 5-node mesh shape as `rekey-accept-off` but `inject-config` sets `udp.outbound_only: true` on node-b and rewrites node-b's peer-c address from the numeric docker IP to the docker hostname (`node-c:2121`), reproducing the production hostname-vs-numeric mismatch. The test asserts no sustained "Dual rekey initiation: we win" log lines on any node (>10 = bug) and the existing rekey health checks catch the connectivity loss the loop produces. - `testing/ci-local.sh` and `.github/workflows/ci.yml` extended to run the new variant in the local sweep and the GitHub CI integration matrix alongside `rekey` and `rekey-accept-off`. Verified locally: full `bash testing/ci-local.sh` sweep passes 29/29 suites (23m 12s) with the new variant green; 1084 unit tests pass.
This commit is contained in:
+41
-3
@@ -15,7 +15,8 @@
|
||||
# -h, --help Show this help
|
||||
#
|
||||
# Integration suites (default coverage):
|
||||
# static-mesh, static-chain, rekey, rekey-accept-off, gateway,
|
||||
# static-mesh, static-chain, rekey, rekey-accept-off,
|
||||
# rekey-outbound-only, gateway,
|
||||
# acl-allowlist, nat-cone, nat-symmetric, nat-lan,
|
||||
# chaos-smoke-10, chaos-churn-mixed-10, chaos-ethernet-mesh,
|
||||
# chaos-ethernet-only, chaos-tcp-mesh, chaos-bottleneck-parent,
|
||||
@@ -53,7 +54,7 @@ ONLY_SUITE=""
|
||||
|
||||
# All integration suites matching ci.yml
|
||||
STATIC_SUITES=(static-mesh static-chain)
|
||||
REKEY_SUITES=(rekey rekey-accept-off)
|
||||
REKEY_SUITES=(rekey rekey-accept-off rekey-outbound-only)
|
||||
# Each entry: "display-name scenario [--flag value ...]"
|
||||
CHAOS_SUITES=(
|
||||
"smoke-10 smoke-10"
|
||||
@@ -376,6 +377,40 @@ run_rekey_accept_off() {
|
||||
record "rekey-accept-off" $rc
|
||||
}
|
||||
|
||||
# Run the rekey-outbound-only integration variant. Same harness as
|
||||
# run_rekey but with udp.outbound_only=true on node-b plus its peer
|
||||
# addrs rewritten from numeric docker IPs to docker hostnames so the
|
||||
# addr_to_link key form mismatches inbound packet source addrs (the
|
||||
# production trigger for the rekey-msg1 carve-out gap).
|
||||
run_rekey_outbound_only() {
|
||||
local compose="testing/static/docker-compose.yml"
|
||||
local rc=0
|
||||
|
||||
info "[rekey-outbound-only] Generating configs"
|
||||
bash testing/static/scripts/generate-configs.sh rekey-outbound-only || \
|
||||
{ record "rekey-outbound-only" 1; return; }
|
||||
REKEY_TOPOLOGY=rekey-outbound-only REKEY_OUTBOUND_ONLY_NODES=b \
|
||||
bash testing/static/scripts/rekey-test.sh inject-config || \
|
||||
{ record "rekey-outbound-only" 1; return; }
|
||||
|
||||
info "[rekey-outbound-only] Starting containers"
|
||||
docker compose -f "$compose" --profile rekey-outbound-only up -d || \
|
||||
{ record "rekey-outbound-only" 1; return; }
|
||||
|
||||
info "[rekey-outbound-only] Running rekey test"
|
||||
if REKEY_TOPOLOGY=rekey-outbound-only REKEY_OUTBOUND_ONLY_NODES=b \
|
||||
bash testing/static/scripts/rekey-test.sh; then
|
||||
rc=0
|
||||
else
|
||||
rc=1
|
||||
info "[rekey-outbound-only] Collecting failure logs"
|
||||
docker compose -f "$compose" --profile rekey-outbound-only logs --no-color 2>&1 | tail -100
|
||||
fi
|
||||
|
||||
docker compose -f "$compose" --profile rekey-outbound-only down --volumes --remove-orphans 2>/dev/null
|
||||
record "rekey-outbound-only" $rc
|
||||
}
|
||||
|
||||
# Run ACL allowlist integration test
|
||||
run_acl_allowlist() {
|
||||
info "[acl-allowlist] Running integration test"
|
||||
@@ -462,9 +497,10 @@ run_integration() {
|
||||
run_static "$topology"
|
||||
done
|
||||
|
||||
# Rekey + rekey-accept-off variant
|
||||
# Rekey + rekey-accept-off + rekey-outbound-only variants
|
||||
run_rekey
|
||||
run_rekey_accept_off
|
||||
run_rekey_outbound_only
|
||||
|
||||
# Gateway
|
||||
run_gateway
|
||||
@@ -553,6 +589,8 @@ run_suite() {
|
||||
run_rekey ;;
|
||||
rekey-accept-off)
|
||||
run_rekey_accept_off ;;
|
||||
rekey-outbound-only)
|
||||
run_rekey_outbound_only ;;
|
||||
gateway)
|
||||
run_gateway ;;
|
||||
acl-allowlist)
|
||||
|
||||
Reference in New Issue
Block a user