mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-20 11:58:39 +00:00
cmd/tailscale: define CLI tools to manipulate macOS network and system extensions (#14727)
Updates tailscale/corp#25278 Adds definitions for new CLI commands getting added in v1.80. Refactors some pre-existing CLI commands within the `configure` tree to clean up code. Signed-off-by: Andrea Gottardo <andrea@gottardo.me>
This commit is contained in:
parent
0fa7b4a236
commit
3dabea0fc2
@ -190,7 +190,7 @@ change in the future.
|
||||
loginCmd,
|
||||
logoutCmd,
|
||||
switchCmd,
|
||||
configureCmd,
|
||||
configureCmd(),
|
||||
syspolicyCmd,
|
||||
netcheckCmd,
|
||||
ipCmd,
|
||||
@ -216,6 +216,7 @@ change in the future.
|
||||
driveCmd,
|
||||
idTokenCmd,
|
||||
advertiseCmd(),
|
||||
configureHostCmd(),
|
||||
),
|
||||
FlagSet: rootfs,
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
@ -226,10 +227,6 @@ change in the future.
|
||||
},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
|
||||
rootCmd.Subcommands = append(rootCmd.Subcommands, configureHostCmd)
|
||||
}
|
||||
|
||||
walkCommands(rootCmd, func(w cmdWalk) bool {
|
||||
if w.UsageFunc == nil {
|
||||
w.UsageFunc = usageFunc
|
||||
|
@ -20,33 +20,31 @@ import (
|
||||
"tailscale.com/version"
|
||||
)
|
||||
|
||||
func init() {
|
||||
configureCmd.Subcommands = append(configureCmd.Subcommands, configureKubeconfigCmd)
|
||||
}
|
||||
|
||||
var configureKubeconfigCmd = &ffcli.Command{
|
||||
Name: "kubeconfig",
|
||||
ShortHelp: "[ALPHA] Connect to a Kubernetes cluster using a Tailscale Auth Proxy",
|
||||
ShortUsage: "tailscale configure kubeconfig <hostname-or-fqdn>",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
func configureKubeconfigCmd() *ffcli.Command {
|
||||
return &ffcli.Command{
|
||||
Name: "kubeconfig",
|
||||
ShortHelp: "[ALPHA] Connect to a Kubernetes cluster using a Tailscale Auth Proxy",
|
||||
ShortUsage: "tailscale configure kubeconfig <hostname-or-fqdn>",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
Run this command to configure kubectl to connect to a Kubernetes cluster over Tailscale.
|
||||
|
||||
The hostname argument should be set to the Tailscale hostname of the peer running as an auth proxy in the cluster.
|
||||
|
||||
See: https://tailscale.com/s/k8s-auth-proxy
|
||||
`),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("kubeconfig")
|
||||
return fs
|
||||
})(),
|
||||
Exec: runConfigureKubeconfig,
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("kubeconfig")
|
||||
return fs
|
||||
})(),
|
||||
Exec: runConfigureKubeconfig,
|
||||
}
|
||||
}
|
||||
|
||||
// kubeconfigPath returns the path to the kubeconfig file for the current user.
|
||||
func kubeconfigPath() (string, error) {
|
||||
if kubeconfig := os.Getenv("KUBECONFIG"); kubeconfig != "" {
|
||||
if version.IsSandboxedMacOS() {
|
||||
return "", errors.New("$KUBECONFIG is incompatible with the App Store version")
|
||||
return "", errors.New("cannot read $KUBECONFIG on GUI builds of the macOS client: this requires the open-source tailscaled distribution")
|
||||
}
|
||||
var out string
|
||||
for _, out = range filepath.SplitList(kubeconfig) {
|
||||
|
13
cmd/tailscale/cli/configure-kube_omit.go
Normal file
13
cmd/tailscale/cli/configure-kube_omit.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build ts_omit_kube
|
||||
|
||||
package cli
|
||||
|
||||
import "github.com/peterbourgon/ff/v3/ffcli"
|
||||
|
||||
func configureKubeconfigCmd() *ffcli.Command {
|
||||
// omitted from the build when the ts_omit_kube build tag is set
|
||||
return nil
|
||||
}
|
@ -22,22 +22,27 @@ import (
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
var synologyConfigureCertCmd = &ffcli.Command{
|
||||
Name: "synology-cert",
|
||||
Exec: runConfigureSynologyCert,
|
||||
ShortHelp: "Configure Synology with a TLS certificate for your tailnet",
|
||||
ShortUsage: "synology-cert [--domain <domain>]",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
func synologyConfigureCertCmd() *ffcli.Command {
|
||||
if runtime.GOOS != "linux" || distro.Get() != distro.Synology {
|
||||
return nil
|
||||
}
|
||||
return &ffcli.Command{
|
||||
Name: "synology-cert",
|
||||
Exec: runConfigureSynologyCert,
|
||||
ShortHelp: "Configure Synology with a TLS certificate for your tailnet",
|
||||
ShortUsage: "synology-cert [--domain <domain>]",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
This command is intended to run periodically as root on a Synology device to
|
||||
create or refresh the TLS certificate for the tailnet domain.
|
||||
|
||||
See: https://tailscale.com/kb/1153/enabling-https
|
||||
`),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("synology-cert")
|
||||
fs.StringVar(&synologyConfigureCertArgs.domain, "domain", "", "Tailnet domain to create or refresh certificates for. Ignored if only one domain exists.")
|
||||
return fs
|
||||
})(),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("synology-cert")
|
||||
fs.StringVar(&synologyConfigureCertArgs.domain, "domain", "", "Tailnet domain to create or refresh certificates for. Ignored if only one domain exists.")
|
||||
return fs
|
||||
})(),
|
||||
}
|
||||
}
|
||||
|
||||
var synologyConfigureCertArgs struct {
|
||||
|
@ -21,34 +21,49 @@ import (
|
||||
// configureHostCmd is the "tailscale configure-host" command which was once
|
||||
// used to configure Synology devices, but is now a compatibility alias to
|
||||
// "tailscale configure synology".
|
||||
var configureHostCmd = &ffcli.Command{
|
||||
Name: "configure-host",
|
||||
Exec: runConfigureSynology,
|
||||
ShortUsage: "tailscale configure-host\n" + synologyConfigureCmd.ShortUsage,
|
||||
ShortHelp: synologyConfigureCmd.ShortHelp,
|
||||
LongHelp: hidden + synologyConfigureCmd.LongHelp,
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("configure-host")
|
||||
return fs
|
||||
})(),
|
||||
//
|
||||
// It returns nil if the actual "tailscale configure synology" command is not
|
||||
// available.
|
||||
func configureHostCmd() *ffcli.Command {
|
||||
synologyConfigureCmd := synologyConfigureCmd()
|
||||
if synologyConfigureCmd == nil {
|
||||
// No need to offer this compatibility alias if the actual command is not available.
|
||||
return nil
|
||||
}
|
||||
return &ffcli.Command{
|
||||
Name: "configure-host",
|
||||
Exec: runConfigureSynology,
|
||||
ShortUsage: "tailscale configure-host\n" + synologyConfigureCmd.ShortUsage,
|
||||
ShortHelp: synologyConfigureCmd.ShortHelp,
|
||||
LongHelp: hidden + synologyConfigureCmd.LongHelp,
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("configure-host")
|
||||
return fs
|
||||
})(),
|
||||
}
|
||||
}
|
||||
|
||||
var synologyConfigureCmd = &ffcli.Command{
|
||||
Name: "synology",
|
||||
Exec: runConfigureSynology,
|
||||
ShortUsage: "tailscale configure synology",
|
||||
ShortHelp: "Configure Synology to enable outbound connections",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
func synologyConfigureCmd() *ffcli.Command {
|
||||
if runtime.GOOS != "linux" || distro.Get() != distro.Synology {
|
||||
return nil
|
||||
}
|
||||
return &ffcli.Command{
|
||||
Name: "synology",
|
||||
Exec: runConfigureSynology,
|
||||
ShortUsage: "tailscale configure synology",
|
||||
ShortHelp: "Configure Synology to enable outbound connections",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
This command is intended to run at boot as root on a Synology device to
|
||||
create the /dev/net/tun device and give the tailscaled binary permission
|
||||
to use it.
|
||||
|
||||
See: https://tailscale.com/s/synology-outbound
|
||||
`),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("synology")
|
||||
return fs
|
||||
})(),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("synology")
|
||||
return fs
|
||||
})(),
|
||||
}
|
||||
}
|
||||
|
||||
func runConfigureSynology(ctx context.Context, args []string) error {
|
||||
|
@ -5,32 +5,41 @@ package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
var configureCmd = &ffcli.Command{
|
||||
Name: "configure",
|
||||
ShortUsage: "tailscale configure <subcommand>",
|
||||
ShortHelp: "[ALPHA] Configure the host to enable more Tailscale features",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
func configureCmd() *ffcli.Command {
|
||||
return &ffcli.Command{
|
||||
Name: "configure",
|
||||
ShortUsage: "tailscale configure <subcommand>",
|
||||
ShortHelp: "Configure the host to enable more Tailscale features",
|
||||
LongHelp: strings.TrimSpace(`
|
||||
The 'configure' set of commands are intended to provide a way to enable different
|
||||
services on the host to use Tailscale in more ways.
|
||||
`),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("configure")
|
||||
return fs
|
||||
})(),
|
||||
Subcommands: configureSubcommands(),
|
||||
FlagSet: (func() *flag.FlagSet {
|
||||
fs := newFlagSet("configure")
|
||||
return fs
|
||||
})(),
|
||||
Subcommands: nonNilCmds(
|
||||
configureKubeconfigCmd(),
|
||||
synologyConfigureCmd(),
|
||||
synologyConfigureCertCmd(),
|
||||
ccall(maybeSysExtCmd),
|
||||
ccall(maybeVPNConfigCmd),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func configureSubcommands() (out []*ffcli.Command) {
|
||||
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
|
||||
out = append(out, synologyConfigureCmd)
|
||||
out = append(out, synologyConfigureCertCmd)
|
||||
// ccall calls the function f if it is non-nil, and returns its result.
|
||||
//
|
||||
// It returns the zero value of the type T if f is nil.
|
||||
func ccall[T any](f func() T) T {
|
||||
var zero T
|
||||
if f == nil {
|
||||
return zero
|
||||
}
|
||||
return out
|
||||
return f()
|
||||
}
|
||||
|
11
cmd/tailscale/cli/configure_apple-all.go
Normal file
11
cmd/tailscale/cli/configure_apple-all.go
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cli
|
||||
|
||||
import "github.com/peterbourgon/ff/v3/ffcli"
|
||||
|
||||
var (
|
||||
maybeSysExtCmd func() *ffcli.Command // non-nil only on macOS, see configure_apple.go
|
||||
maybeVPNConfigCmd func() *ffcli.Command // non-nil only on macOS, see configure_apple.go
|
||||
)
|
97
cmd/tailscale/cli/configure_apple.go
Normal file
97
cmd/tailscale/cli/configure_apple.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build darwin
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
)
|
||||
|
||||
func init() {
|
||||
maybeSysExtCmd = sysExtCmd
|
||||
maybeVPNConfigCmd = vpnConfigCmd
|
||||
}
|
||||
|
||||
// Functions in this file provide a dummy Exec function that only prints an error message for users of the open-source
|
||||
// tailscaled distribution. On GUI builds, the Swift code in the macOS client handles these commands by not passing the
|
||||
// flow of execution to the CLI.
|
||||
|
||||
// sysExtCmd returns a command for managing the Tailscale system extension on macOS
|
||||
// (for the Standalone variant of the client only).
|
||||
func sysExtCmd() *ffcli.Command {
|
||||
return &ffcli.Command{
|
||||
Name: "sysext",
|
||||
ShortUsage: "tailscale configure sysext [activate|deactivate|status]",
|
||||
ShortHelp: "Manages the system extension for macOS (Standalone variant)",
|
||||
LongHelp: "The sysext set of commands provides a way to activate, deactivate, or manage the state of the Tailscale system extension on macOS. " +
|
||||
"This is only relevant if you are running the Standalone variant of the Tailscale client for macOS. " +
|
||||
"To access more detailed information about system extensions installed on this Mac, run 'systemextensionsctl list'.",
|
||||
Subcommands: []*ffcli.Command{
|
||||
{
|
||||
Name: "activate",
|
||||
ShortUsage: "tailscale sysext activate",
|
||||
ShortHelp: "Register the Tailscale system extension with macOS.",
|
||||
LongHelp: "This command registers the Tailscale system extension with macOS. To run Tailscale, you'll also need to install the VPN configuration separately (run `tailscale configure vpn-config install`). After running this command, you need to approve the extension in System Settings > Login Items and Extensions > Network Extensions.",
|
||||
Exec: requiresStandalone,
|
||||
},
|
||||
{
|
||||
Name: "deactivate",
|
||||
ShortUsage: "tailscale sysext deactivate",
|
||||
ShortHelp: "Deactivate the Tailscale system extension on macOS",
|
||||
LongHelp: "This command deactivates the Tailscale system extension on macOS. To completely remove Tailscale, you'll also need to delete the VPN configuration separately (use `tailscale configure vpn-config uninstall`).",
|
||||
Exec: requiresStandalone,
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
ShortUsage: "tailscale sysext status",
|
||||
ShortHelp: "Print the enablement status of the Tailscale system extension",
|
||||
LongHelp: "This command prints the enablement status of the Tailscale system extension. If the extension is not enabled, run `tailscale sysext activate` to enable it.",
|
||||
Exec: requiresStandalone,
|
||||
},
|
||||
},
|
||||
Exec: requiresStandalone,
|
||||
}
|
||||
}
|
||||
|
||||
// vpnConfigCmd returns a command for managing the Tailscale VPN configuration on macOS
|
||||
// (the entry that appears in System Settings > VPN).
|
||||
func vpnConfigCmd() *ffcli.Command {
|
||||
return &ffcli.Command{
|
||||
Name: "mac-vpn",
|
||||
ShortUsage: "tailscale configure mac-vpn [install|uninstall]",
|
||||
ShortHelp: "Manage the VPN configuration on macOS (App Store and Standalone variants)",
|
||||
LongHelp: "The vpn-config set of commands provides a way to add or remove the Tailscale VPN configuration from the macOS settings. This is the entry that appears in System Settings > VPN.",
|
||||
Subcommands: []*ffcli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
ShortUsage: "tailscale mac-vpn install",
|
||||
ShortHelp: "Write the Tailscale VPN configuration to the macOS settings",
|
||||
LongHelp: "This command writes the Tailscale VPN configuration to the macOS settings. This is the entry that appears in System Settings > VPN. If you are running the Standalone variant of the client, you'll also need to install the system extension separately (run `tailscale configure sysext activate`).",
|
||||
Exec: requiresGUI,
|
||||
},
|
||||
{
|
||||
Name: "uninstall",
|
||||
ShortUsage: "tailscale mac-vpn uninstall",
|
||||
ShortHelp: "Delete the Tailscale VPN configuration from the macOS settings",
|
||||
LongHelp: "This command removes the Tailscale VPN configuration from the macOS settings. This is the entry that appears in System Settings > VPN. If you are running the Standalone variant of the client, you'll also need to deactivate the system extension separately (run `tailscale configure sysext deactivate`).",
|
||||
Exec: requiresGUI,
|
||||
},
|
||||
},
|
||||
Exec: func(ctx context.Context, args []string) error {
|
||||
return errors.New("unsupported command: requires a GUI build of the macOS client")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func requiresStandalone(ctx context.Context, args []string) error {
|
||||
return errors.New("unsupported command: requires the Standalone (.pkg installer) GUI build of the client")
|
||||
}
|
||||
|
||||
func requiresGUI(ctx context.Context, args []string) error {
|
||||
return errors.New("unsupported command: requires a GUI build of the macOS client")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user