Skip to content

feat: route Fleet SSH over existing Tailscale - #1

Merged
extoci merged 7 commits into
mainfrom
feat/tailscale
Aug 4, 2026
Merged

feat: route Fleet SSH over existing Tailscale#1
extoci merged 7 commits into
mainfrom
feat/tailscale

Conversation

@extoci

@extoci extoci commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Keep ssh <name>.local as the only normal Fleet SSH interface.
  • Add a local OpenSSH ProxyCommand that uses LAN first and an existing Tailscale route when a confirmed mapping is available.
  • Learn exact member Tailscale FQDNs over the existing pinned SSH connection; persist no peer IPs or Tailscale credentials.
  • Preserve the existing .local host-key pin and OpenSSH authentication path.
  • Add forced-route recovery with fleet connect <member> --via lan|tailscale.
  • Reject join requests arriving from Tailscale address space and keep mapping refresh best-effort.
  • Document the design and rollout in docs/tailscale-integration-spec.md.

Verification

  • cargo test --all-targets (76 unit tests, 20 CLI tests)
  • cargo clippy --all-targets -- -D warnings

The implementation deliberately does not install, authenticate, administer, or relay through Tailscale.

Greptile Summary

This PR adds optional Tailscale routing for Fleet SSH without installing or administering Tailscale. Fleet learns each member's Tailscale FQDN over the existing pinned SSH channel, stores only the FQDN (never IPs), and resolves it live through the captain's local Tailscale client at connect time.

  • Adds fleet transport connect as an OpenSSH ProxyCommand that races LAN (150 ms head start) against the mapped Tailscale peer, passing the winning TCP socket to the outer SSH process unchanged.
  • Adds validate_join_source to enforce LAN-only joins and hardens the generated SSH config with Port 22, UpdateHostKeys no, and a shell-safe quoted ProxyCommand.

Confidence Score: 5/5

Safe to merge; all findings are diagnostic edge cases with no impact on correctness, data integrity, or security.

The transport selector, FQDN mapping, ProxyCommand generation, and join-source validation all implement their stated contracts correctly. The two flagged items — an uninformative error message when both routes time out simultaneously, and an opaque server error when OS interface enumeration fails during a join — degrade diagnostics only and do not cause incorrect behavior or security regression.

Files Needing Attention: src/ssh_client.rs (connect_auto error path) and src/service.rs (validate_join_source error propagation) are worth a second glance but neither requires a blocking fix.

Important Files Changed

Filename Overview
src/tailscale.rs New module: read-only Tailscale adapter with FQDN mapping persistence, range-validated IP resolution, bounded subprocess execution, and concurrent-safe file locking.
src/ssh_client.rs Adds transport selection (LAN race vs Tailscale), ProxyCommand generation with shell-safe quoting, proxy_stdio, and refresh_mapping. connect_auto error message is empty when both routes time out without replying.
src/service.rs Adds validate_join_source to enforce LAN-only joins; propagates get_if_addrs failures via ?, blocking joins when interface enumeration is transiently unavailable.
src/network.rs New module: on-link peer validation with subnet matching, IPv4-mapped normalization, and interface name/IP-based exclusion lists. Well-tested.
src/commands.rs Wires new commands and refactors status --check to use the transport selector; calls refresh_mapping after update-all.
src/remote.rs Adds ssh_output_direct (ProxyCommand=none) for bootstrap mapping pulls over raw LAN.

Reviews (2): Last reviewed commit: "fix: address Tailscale transport review ..." | Re-trigger Greptile

@extoci

extoci commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@greptile review

Comment thread src/ssh_client.rs
Comment thread src/ssh_client.rs
@extoci
extoci merged commit 372441f into main Aug 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant