Repository navigation
test(mesh): keep host_network nodes from learning endpoints through the runner NAT - #195
Merged
Merged
Conversation
Mearman
force-pushed
the
fix/host-network-direct-path
branch
2 times, most recently
from
November 3, 2026 14:30
990c4bf to
f201b8b
Compare
… their own addresses Each node's Tailscale client advertised, beside its interface addresses, the runner's public address a STUN server on a public DERP region saw it come from, which hairpins back through the runner's NAT. magicsock picks between the two by measured latency with a bonus for a private address, and sub-millisecond jitter can outweigh it, so the path a node used varied between runs. Each VM now drops UDP to port 3478, as on a LAN whose NAT does not hairpin; DERP over TCP still carries the disco messages.
…ir own VM assert_direct_paths checked the path tailscale ping settled on, which depends on latency measured during the run. It now also checks that every endpoint each node advertises for itself is an address of its own VM's interfaces, so an endpoint learnt over STUN, which can carry a path through the runner's NAT, fails the scenario on every run that has one rather than on the runs where it happens to win. The bridge addresses its Docker and k3s create are interface addresses of the VM too, so they are allowed.
Mearman
force-pushed
the
fix/host-network-direct-path
branch
from
November 3, 2026 15:14
cebcc5d to
31218db
Compare
Mearman
marked this pull request as ready for review
November 3, 2026 15:57
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins. |
|
🎉 This PR is included in version 2.27.2 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #189
host_network asserts that every node reaches every other directly at its daemon's address on the test network. Each node's Tailscale client also advertises the address a STUN server on a public DERP region sees it come from, which on a hosted runner is the runner's public address, and that hairpins back through the runner's NAT. magicsock chooses between the two by measured latency, giving a private address a 20 point bonus, and a latency ratio from sub-millisecond jitter can outweigh that (
betterAddrinwgengine/magicsock/endpoint.go, Tailscale v1.102.4). So the path a node settled on varied between runs.The first commit drops UDP to port 3478 in each VM, as on a LAN whose NAT doesn't hairpin, so a node only ever advertises addresses of its own VM. DERP still runs over TCP and still carries the disco messages that set the direct path up. The second makes the scenario check the endpoints themselves: every endpoint each node advertises for itself must be an address of its own VM's interfaces (the test network address and the Docker and k3s bridges). A STUN-learnt address belongs to the runner's NAT, so the check fails on every run that has one, not just the ones where the public endpoint wins. This is a harness change: on a real LAN either path is direct, and the role has nothing to pin.