test(net): TUN-mode docker stack and ignored e2e test

- docker-compose.yml: run peer-a and peer-b with TS_USERSPACE=false +
  /dev/net/tun device + cap_add. Pin peer-a's WG listen port to 41641
  via TS_TAILSCALED_EXTRA_ARGS and publish it to the host so direct
  UDP from outside docker has somewhere to land.
- run.sh: use an ephemeral pre-auth key for the test client so
  Headscale auto-deletes the test node when its map stream drops
  (instead of accumulating hundreds of stale entries that eventually
  slow netmap propagation to a crawl). Disable shields-up on both
  peers so the kernel firewall doesn't drop inbound tailnet TCP. Tweak
  the JSON key extraction to handle pretty-printed output.
- integration.rs: add `test_e2e_tcp_through_tunnel` that brings up
  the daemon, dials peer-a's echo server through the proxy, and
  asserts the echo body comes back. Currently `#[ignore]`d — the
  docker stack runs Headscale over plain HTTP, but Tailscale's client
  unconditionally tries TLS to DERP relays ("tls: first record does
  not look like a TLS handshake"), so peer-a can never receive
  packets we forward via the relay. Unblocking needs either TLS
  termination on the docker DERP or running the test inside the same
  docker network as peer-a. Test stays in the tree because everything
  it tests up to the read timeout is real verified behavior.
This commit is contained in:
2026-04-07 14:33:59 +01:00
parent dca8c3b643
commit f1668682b7
3 changed files with 136 additions and 3 deletions

View File

@@ -33,6 +33,9 @@ services:
# ── Tailscale peer A (validates that Headscale is working) ──────────
# This peer registers with Headscale and stays online so our Rust
# client can discover it in the netmap and attempt WireGuard tunnels.
# Runs in TUN mode (TS_USERSPACE=false) so the host kernel actually
# routes packets to peer-a's tailnet IP — this is what makes inbound
# TCP from other tailnet members work end-to-end.
peer-a:
image: tailscale/tailscale:stable
hostname: peer-a
@@ -42,10 +45,19 @@ services:
environment:
TS_AUTHKEY: "${PEER_A_AUTH_KEY}"
TS_STATE_DIR: /var/lib/tailscale
TS_USERSPACE: "false"
TS_EXTRA_ARGS: --login-server=http://headscale:8080
# Pin the WireGuard listen port (passed to tailscaled itself) so we
# can publish it to the host — without this our test daemon (running
# outside docker) can't reach peer-a's UDP endpoint.
TS_TAILSCALED_EXTRA_ARGS: --port=41641
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
ports:
- "41641:41641/udp"
volumes:
- peer-a-state:/var/lib/tailscale
# Tailscale doesn't have a great healthcheck, but it registers fast
@@ -65,10 +77,13 @@ services:
environment:
TS_AUTHKEY: "${PEER_B_AUTH_KEY}"
TS_STATE_DIR: /var/lib/tailscale
TS_USERSPACE: "false"
TS_EXTRA_ARGS: --login-server=http://headscale:8080
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- peer-b-state:/var/lib/tailscale
healthcheck: