From e422e9f4c94910cef7b0a0002e450082927a4320 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 29 Jul 2021 10:45:07 -0700 Subject: [PATCH] cmd/derper: mesh over VPC network Updates #2414 Signed-off-by: Brad Fitzpatrick --- cmd/derper/mesh.go | 30 ++++++++++++++++++++++++++++++ derp/derphttp/derphttp_client.go | 15 ++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cmd/derper/mesh.go b/cmd/derper/mesh.go index b442d1b36..aadde1263 100644 --- a/cmd/derper/mesh.go +++ b/cmd/derper/mesh.go @@ -9,7 +9,9 @@ import ( "errors" "fmt" "log" + "net" "strings" + "time" "tailscale.com/derp" "tailscale.com/derp/derphttp" @@ -39,6 +41,34 @@ func startMeshWithHost(s *derp.Server, host string) error { return err } c.MeshKey = s.MeshKey() + + // For meshed peers within a region, connect via VPC addresses. + c.SetURLDialer(func(ctx context.Context, network, addr string) (net.Conn, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + var d net.Dialer + var r net.Resolver + if port == "443" && strings.HasSuffix(host, ".tailscale.com") { + base := strings.TrimSuffix(host, ".tailscale.com") + subCtx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + vpcHost := base + "-vpc.tailscale.com" + ips, _ := r.LookupIP(subCtx, "ip", vpcHost) + if len(ips) > 0 { + vpcAddr := net.JoinHostPort(ips[0].String(), port) + c, err := d.DialContext(ctx, network, vpcAddr) + if err == nil { + log.Printf("connected to %v (%v) instead of %v", vpcHost, ips[0], base) + return c, nil + } + log.Printf("failed to connect to %v (%v): %v; trying non-VPC route", vpcHost, ips[0], err) + } + } + return d.DialContext(ctx, network, addr) + }) + add := func(k key.Public) { s.AddPacketForwarder(k, c) } remove := func(k key.Public) { s.RemovePacketForwarder(k, c) } go c.RunWatchConnectionLoop(context.Background(), s.PublicKey(), logf, add, remove) diff --git a/derp/derphttp/derphttp_client.go b/derp/derphttp/derphttp_client.go index ca249dcc7..e8177c943 100644 --- a/derp/derphttp/derphttp_client.go +++ b/derp/derphttp/derphttp_client.go @@ -54,6 +54,7 @@ type Client struct { privateKey key.Private logf logger.Logf + dialer func(ctx context.Context, network, addr string) (net.Conn, error) // Either url or getRegion is non-nil: url *url.URL @@ -363,10 +364,22 @@ func (c *Client) connect(ctx context.Context, caller string) (client *derp.Clien return c.client, c.connGen, nil } +// SetURLDialer sets the dialer to use for dialing URLs. +// This dialer is only use for clients created with NewClient, not NewRegionClient. +// If unset or nil, the default dialer is used. +// +// The primary use for this is the derper mesh mode to connect to each +// other over a VPC network. +func (c *Client) SetURLDialer(dialer func(ctx context.Context, network, addr string) (net.Conn, error)) { + c.dialer = dialer +} + func (c *Client) dialURL(ctx context.Context) (net.Conn, error) { host := c.url.Hostname() + if c.dialer != nil { + return c.dialer(ctx, "tcp", net.JoinHostPort(host, urlPort(c.url))) + } hostOrIP := host - dialer := netns.NewDialer() if c.DNSCache != nil {