diff --git a/pkg/cli/cmds/agent.go b/pkg/cli/cmds/agent.go index 06753cee4e..433f0162cf 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=[,controlServerURL=]", + Usage: "(agent/networking) (experimental) Credentials for the VPN provider. It must include the provider name and join key in the format name=,joinKey=[,controlServerURL=][,extraArgs=]", 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=[,controlServerURL=]", + 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=][,extraArgs=]", EnvVar: version.ProgramUpper + "_VPN_AUTH_FILE", Destination: &AgentConfig.VPNAuthFile, } diff --git a/pkg/vpn/vpn.go b/pkg/vpn/vpn.go index 3b3a5c8ff7..3d3e25e34d 100644 --- a/pkg/vpn/vpn.go +++ b/pkg/vpn/vpn.go @@ -35,6 +35,7 @@ type vpnCliAuthInfo struct { Name string JoinKey string ControlServerURL string + ExtraCLIFlags []string } // StartVPN starts the VPN interface. General function in case we want to add more vpn integrations @@ -53,6 +54,10 @@ func StartVPN(vpnAuthConfigFile string) error { if authInfo.ControlServerURL != "" { args = append(args, "--login-server", authInfo.ControlServerURL) } + if len(authInfo.ExtraCLIFlags) > 0 { + args = append(args, authInfo.ExtraCLIFlags...) + } + logrus.Debugf("Flags passed to tailscale up: %v", args) output, err := util.ExecCommand("tailscale", args) if err != nil { return errors.Wrap(err, "tailscale up failed: "+output) @@ -80,7 +85,12 @@ func GetVPNInfo(vpnAuth string) (VPNInfo, error) { // getVPNAuthInfo returns the required authInfo object func getVPNAuthInfo(vpnAuth string) (vpnCliAuthInfo, error) { var authInfo vpnCliAuthInfo - vpnParameters := strings.Split(vpnAuth, ",") + + // Separate extraArgs which will be passed directly to the vpn binary command + vpnCommand, extraArgs := processCLIArgs(vpnAuth) + authInfo.ExtraCLIFlags = extraArgs + + vpnParameters := strings.Split(vpnCommand, ",") for _, vpnKeyValues := range vpnParameters { vpnKeyValue := strings.Split(vpnKeyValues, "=") switch vpnKeyValue[0] { @@ -139,3 +149,13 @@ func getTailscaleInfo() (VPNInfo, error) { return VPNInfo{IPv4Address: net.ParseIP(ipv4Address), IPv6Address: net.ParseIP(ipv6Address), NodeID: "", ProviderName: "tailscale", VPNInterface: tailscaleIf}, nil } + +// processCLIArgs separates the extraArgs part from the command. +// Note that tailscale flags of type list are comma separated and don't accept spaces, thus we can use strings.Fields to separate flags +func processCLIArgs(command string) (string, []string) { + subCommands := strings.Split(command, ",extraArgs=") + if len(subCommands) > 1 { + return subCommands[0], strings.Fields(subCommands[1]) + } + return subCommands[0], []string{} +}