Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 78 additions & 5 deletions pkg/vpn/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import (
"net"
"net/netip"
"os"
"sort"
"strings"
"time"

"github.com/avast/retry-go/v4"
"github.com/google/uuid"
"tailscale.com/tailcfg"
"tailscale.com/tsnet"
)

Expand Down Expand Up @@ -64,6 +66,7 @@ func Connect(ctx context.Context, target, controllerURL, authkey string, opts ..
ControlURL: controllerURL,
AuthKey: authkey,
Dir: tempDir,
Ephemeral: true,
}

// now disable logging, maybe altogether later
Expand All @@ -76,35 +79,105 @@ func Connect(ctx context.Context, target, controllerURL, authkey string, opts ..
if err != nil {
return nil, err
}
var firewallVPNIP netip.Addr

var firewallVPNIPs []netip.Addr

err = retry.Do(
func() error {
_, _ = fmt.Fprintf(out, ".")
status, err := lc.Status(ctx)
if err != nil {
return err
}

if status.BackendState != "Running" {
_, _ = fmt.Fprintf(out, "backend is not yet running, but: %s\n", status.BackendState)
return fmt.Errorf("backend state did not reach running, only %q", status.BackendState)
}

if status.Self.Online {
for _, peer := range status.Peer {
if strings.HasPrefix(peer.HostName, target) {
firewallVPNIP = peer.TailscaleIPs[0]
_, _ = fmt.Fprintf(out, " connected to %s (ip %s) took: %s\n", target, firewallVPNIP, time.Since(start))
firewallVPNIPs = peer.TailscaleIPs
_, _ = fmt.Fprintf(out, " connected to %s (ips %s) took: %s\n", target, firewallVPNIPs, time.Since(start))

return nil
}
}
}

return fmt.Errorf("did not get online")
},
retry.Attempts(50),
)
if err != nil {
return nil, err
}

// disable logging after successful connect
s.Logf = func(format string, args ...any) {}

conn, err := lc.DialTCP(ctx, firewallVPNIP.String(), 22)
return &vpn{Conn: conn, server: s, tempDir: tempDir, TargetIP: firewallVPNIP.String()}, err
// prefer ipv6 over ipv4 addresses for connection
sort.SliceStable(firewallVPNIPs, func(i, j int) bool {
if firewallVPNIPs[i].Is6() && firewallVPNIPs[j].Is4() {
return true
}
return false
})

var errs []error

for _, vpnIP := range firewallVPNIPs {
vpn, err := func() (*vpn, error) {
connectCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

ip := vpnIP.String()

_, _ = fmt.Fprintf(out, " attempting to ping %s\n", ip)

_, err := lc.Ping(connectCtx, vpnIP, tailcfg.PingPeerAPI)
if err != nil {
return nil, fmt.Errorf("unable to ping ip %q: %w", ip, err)
}

var conn net.Conn

err = retry.Do(
func() error {
dialCtx, cancel := context.WithTimeout(connectCtx, 10*time.Second)
defer cancel()

_, _ = fmt.Fprintf(out, " attempting to dial to %s\n", ip)

conn, err = lc.DialTCP(dialCtx, ip, 22)
if err != nil {
return fmt.Errorf("unable to dial %q: %w", ip, err)
}

return nil
},
retry.Context(connectCtx),
)
if err != nil {
return nil, err
}

return &vpn{Conn: conn, server: s, tempDir: tempDir, TargetIP: ip}, err
}()
if err != nil {
errs = append(errs, err)
continue
}

return vpn, nil
}

if err := lc.Logout(context.Background()); err != nil {
errs = append(errs, fmt.Errorf("unable to logout with tailscale server: %w", err))
}

return nil, fmt.Errorf("not able to dial to any of the found peer ips: %w", errors.Join(errs...))
}

// Close all open connections after vpn was used.
Expand Down