diff --git a/pkg/agent/flannel/setup.go b/pkg/agent/flannel/setup.go index 6ca5108077..95669f8369 100644 --- a/pkg/agent/flannel/setup.go +++ b/pkg/agent/flannel/setup.go @@ -76,7 +76,7 @@ const ( tailscaledBackend = `{ "Type": "extension", - "PostStartupCommand": "tailscale up --accept-routes --advertise-routes=%Routes%", + "PostStartupCommand": "tailscale set --accept-routes --advertise-routes=%Routes%", "ShutdownCommand": "tailscale down" }` diff --git a/pkg/cli/cmds/agent.go b/pkg/cli/cmds/agent.go index 543b8c8c42..06753cee4e 100644 --- a/pkg/cli/cmds/agent.go +++ b/pkg/cli/cmds/agent.go @@ -155,13 +155,13 @@ var ( } VPNAuth = &cli.StringFlag{ Name: "vpn-auth", - Usage: "(agent/networking) (experimental) Credentials for the VPN provider. It must include the provider name and join key in the format name=,joinKey=", + Usage: "(agent/networking) (experimental) Credentials for the VPN provider. It must include the provider name and join key in the format name=,joinKey=[,controlServerURL=]", EnvVar: version.ProgramUpper + "_VPN_AUTH", Destination: &AgentConfig.VPNAuth, } VPNAuthFile = &cli.StringFlag{ Name: "vpn-auth-file", - Usage: "(agent/networking) (experimental) File containing credentials for the VPN provider. It must include the provider name and join key in the format name=,joinKey=", + Usage: "(agent/networking) (experimental) File containing credentials for the VPN provider. It must include the provider name and join key in the format name=,joinKey=[,controlServerURL=]", EnvVar: version.ProgramUpper + "_VPN_AUTH_FILE", Destination: &AgentConfig.VPNAuthFile, } diff --git a/pkg/vpn/vpn.go b/pkg/vpn/vpn.go index 466b145590..3b3a5c8ff7 100644 --- a/pkg/vpn/vpn.go +++ b/pkg/vpn/vpn.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net" + "net/url" "strings" "github.com/k3s-io/k3s/pkg/util" @@ -31,8 +32,9 @@ type VPNInfo struct { // vpnCliAuthInfo includes auth information of the VPN. It is a general struct in case we want to add more vpn integrations type vpnCliAuthInfo struct { - Name string - JoinKey string + Name string + JoinKey string + ControlServerURL string } // StartVPN starts the VPN interface. General function in case we want to add more vpn integrations @@ -45,7 +47,13 @@ func StartVPN(vpnAuthConfigFile string) error { logrus.Infof("Starting VPN: %s", authInfo.Name) switch authInfo.Name { case "tailscale": - output, err := util.ExecCommand("tailscale", []string{"up", "--authkey", authInfo.JoinKey, "--reset"}) + args := []string{ + "up", "--authkey", authInfo.JoinKey, "--timeout=30s", "--reset", + } + if authInfo.ControlServerURL != "" { + args = append(args, "--login-server", authInfo.ControlServerURL) + } + output, err := util.ExecCommand("tailscale", args) if err != nil { return errors.Wrap(err, "tailscale up failed: "+output) } @@ -80,6 +88,8 @@ func getVPNAuthInfo(vpnAuth string) (vpnCliAuthInfo, error) { authInfo.Name = vpnKeyValue[1] case "joinKey": authInfo.JoinKey = vpnKeyValue[1] + case "controlServerURL": + authInfo.ControlServerURL = vpnKeyValue[1] default: return vpnCliAuthInfo{}, fmt.Errorf("VPN Error. The passed VPN auth info includes an unknown parameter: %v", vpnKeyValue[0]) } @@ -97,6 +107,11 @@ func isVPNConfigOK(authInfo vpnCliAuthInfo) error { if authInfo.JoinKey == "" { return errors.New("VPN Error. Tailscale requires a JoinKey") } + if authInfo.ControlServerURL != "" { + if _, err := url.Parse(authInfo.ControlServerURL); err != nil { + return fmt.Errorf("VPN Error. Invalid control server URL for Tailscale: %w", err) + } + } return nil }