From 3530ab5915802dcdb94db61d80267916db61afa7 Mon Sep 17 00:00:00 2001 From: Roberto Bonafiglia Date: Tue, 28 Oct 2025 16:13:27 +0100 Subject: [PATCH] Fix tailscale setup in case of an already running configuration Signed-off-by: Roberto Bonafiglia --- pkg/agent/flannel/setup.go | 7 +++++++ pkg/vpn/vpn.go | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/pkg/agent/flannel/setup.go b/pkg/agent/flannel/setup.go index ed4c97cfdee..86d9033e04f 100644 --- a/pkg/agent/flannel/setup.go +++ b/pkg/agent/flannel/setup.go @@ -13,6 +13,7 @@ import ( agentutil "github.com/k3s-io/k3s/pkg/agent/util" "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/signals" + "github.com/k3s-io/k3s/pkg/vpn" "github.com/k3s-io/k3s/pkg/util" pkgerrors "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -232,6 +233,12 @@ func createFlannelConf(nodeConfig *config.Node) error { if len(routes) == 0 { return fmt.Errorf("incorrect netMode for flannel tailscale backend") } + advertisedRoutes, err := vpn.GetAdvertisedRoutes() + if err == nil && advertisedRoutes != nil { + for _, advertisedRoute := range advertisedRoutes { + routes = append(routes, advertisedRoute.String()) + } + } backendConf = strings.ReplaceAll(tailscaledBackend, "%Routes%", strings.Join(routes, ",")) case config.FlannelBackendWireguardNative: backendConf = wireguardNativeBackend diff --git a/pkg/vpn/vpn.go b/pkg/vpn/vpn.go index 06e73591dfe..39d2611bf6b 100644 --- a/pkg/vpn/vpn.go +++ b/pkg/vpn/vpn.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net" + "net/netip" "net/url" "strings" @@ -20,10 +21,16 @@ const ( type TailscaleOutput struct { TailscaleIPs []string `json:"TailscaleIPs"` + BackendState string `json:"BackendState"` +} + +type TailscalePrefsOutput struct { + AdvertiseRoutes []netip.Prefix `json:"AdvertiseRoutes"` } // VPNInfo includes node information of the VPN. It is a general struct in case we want to add more vpn integrations type VPNInfo struct { + BackendState string IPv4Address net.IP IPv6Address net.IP NodeID string @@ -49,6 +56,11 @@ func StartVPN(vpnAuthConfigFile string) error { logrus.Infof("Starting VPN: %s", authInfo.Name) switch authInfo.Name { case "tailscale": + vpnInfo, err := getTailscaleInfo() + if err == nil && vpnInfo.BackendState == "Running" { + logrus.Debugf("Tailscale is already running, skipping tailscale up") + return nil + } args := []string{ "up", "--authkey", authInfo.JoinKey, "--timeout=30s", "--reset", } @@ -148,7 +160,26 @@ func getTailscaleInfo() (VPNInfo, error) { ipv4Address, _ := util.GetFirst4String(tailscaleOutput.TailscaleIPs) ipv6Address, _ := util.GetFirst6String(tailscaleOutput.TailscaleIPs) - return VPNInfo{IPv4Address: net.ParseIP(ipv4Address), IPv6Address: net.ParseIP(ipv6Address), NodeID: "", ProviderName: "tailscale", VPNInterface: tailscaleIf}, nil + return VPNInfo{BackendState: tailscaleOutput.BackendState, IPv4Address: net.ParseIP(ipv4Address), IPv6Address: net.ParseIP(ipv6Address), NodeID: "", ProviderName: "tailscale", VPNInterface: tailscaleIf}, nil +} + +// get Tailscale advertised route list +func GetAdvertisedRoutes() ([]netip.Prefix, error) { + output, err := util.ExecCommand("tailscale", []string{"debug", "prefs"}) + if err != nil { + return nil, fmt.Errorf("failed to run tailscale debug prefs: %v", err) + } + + logrus.Debugf("Output from tailscale debug prefs: %v", output) + + var tailscaleOutput TailscalePrefsOutput + err = json.Unmarshal([]byte(output), &tailscaleOutput) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal tailscale output: %v", err) + } + + return tailscaleOutput.AdvertiseRoutes, nil + } // processCLIArgs separates the extraArgs part from the command.