Skip to content

Commit

Permalink
[vnet] add windows tsh cli commands (#50935)
Browse files Browse the repository at this point in the history
* [vnet] add windows tsh cli commands

This PR refactors the VNet tsh CLI commands and adds stubs for the
commands that are going to be added for Windows VNet support.

* fix linux build

* update copyright year

* use context.AfterFunc

* fix typo
  • Loading branch information
nklaassen authored and mvbrock committed Jan 18, 2025
1 parent 82b3836 commit 032df73
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 143 deletions.
27 changes: 18 additions & 9 deletions tool/tsh/common/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -1258,9 +1258,12 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error {

workloadIdentityCmd := newSVIDCommands(app)

vnetCmd := newVnetCommand(app)
vnetAdminSetupCmd := newVnetAdminSetupCommand(app)
vnetDaemonCmd := newVnetDaemonCommand(app)
vnetCommand := newVnetCommand(app)
vnetAdminSetupCommand := newVnetAdminSetupCommand(app)
vnetDaemonCommand := newVnetDaemonCommand(app)
vnetInstallServiceCommand := newVnetInstallServiceCommand(app)
vnetUninstallServiceCommand := newVnetUninstallServiceCommand(app)
vnetServiceCommand := newVnetServiceCommand(app)

gitCmd := newGitCommands(app)

Expand Down Expand Up @@ -1638,12 +1641,18 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error {
err = onHeadlessApprove(&cf)
case workloadIdentityCmd.issue.FullCommand():
err = workloadIdentityCmd.issue.run(&cf)
case vnetCmd.FullCommand():
err = vnetCmd.run(&cf)
case vnetAdminSetupCmd.FullCommand():
err = vnetAdminSetupCmd.run(&cf)
case vnetDaemonCmd.FullCommand():
err = vnetDaemonCmd.run(&cf)
case vnetCommand.FullCommand():
err = vnetCommand.run(&cf)
case vnetAdminSetupCommand.FullCommand():
err = vnetAdminSetupCommand.run(&cf)
case vnetDaemonCommand.FullCommand():
err = vnetDaemonCommand.run(&cf)
case vnetInstallServiceCommand.FullCommand():
err = vnetInstallServiceCommand.run(&cf)
case vnetUninstallServiceCommand.FullCommand():
err = vnetUninstallServiceCommand.run(&cf)
case vnetServiceCommand.FullCommand():
err = vnetServiceCommand.run(&cf)
case gitCmd.list.FullCommand():
err = gitCmd.list.run(&cf)
case gitCmd.login.FullCommand():
Expand Down
92 changes: 92 additions & 0 deletions tool/tsh/common/vnet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package common

import (
"context"
"fmt"

"github.com/alecthomas/kingpin/v2"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/lib/vnet"
)

type vnetCLICommand interface {
// FullCommand matches the signature of kingpin.CmdClause.FullCommand, which
// most commands should embed.
FullCommand() string
// run should be called iff FullCommand() matches the CLI parameters.
run(cf *CLIConf) error
}

// vnetCommand implements the `tsh vnet` command to run VNet.
type vnetCommand struct {
*kingpin.CmdClause
}

func newVnetCommand(app *kingpin.Application) *vnetCommand {
cmd := &vnetCommand{
CmdClause: app.Command("vnet", "Start Teleport VNet, a virtual network for TCP application access."),
}
return cmd
}

func (c *vnetCommand) run(cf *CLIConf) error {
appProvider, err := newVnetAppProvider(cf)
if err != nil {
return trace.Wrap(err)
}
processManager, err := vnet.Run(cf.Context, &vnet.RunConfig{AppProvider: appProvider})
if err != nil {
return trace.Wrap(err)
}
fmt.Println("VNet is ready.")
context.AfterFunc(cf.Context, processManager.Close)
return trace.Wrap(processManager.Wait())
}

func newVnetAdminSetupCommand(app *kingpin.Application) vnetCLICommand {
return newPlatformVnetAdminSetupCommand(app)
}

func newVnetDaemonCommand(app *kingpin.Application) vnetCLICommand {
return newPlatformVnetDaemonCommand(app)
}

func newVnetInstallServiceCommand(app *kingpin.Application) vnetCLICommand {
return newPlatformVnetInstallServiceCommand(app)
}

func newVnetUninstallServiceCommand(app *kingpin.Application) vnetCLICommand {
return newPlatformVnetUninstallServiceCommand(app)
}

func newVnetServiceCommand(app *kingpin.Application) vnetCLICommand {
return newPlatformVnetServiceCommand(app)
}

// vnetCommandNotSupported implements vnetCLICommand, it is returned when a specific
// command is not implemented for a certain platform or environment.
type vnetCommandNotSupported struct{}

func (vnetCommandNotSupported) FullCommand() string {
return ""
}
func (vnetCommandNotSupported) run(*CLIConf) error {
panic("vnetCommandNotSupported.run should never be called, this is a bug")
}
4 changes: 3 additions & 1 deletion tool/tsh/common/vnet_daemon_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ const (
vnetDaemonSubCommand = "vnet-daemon"
)

// vnetDaemonCommand implements the vnet-daemon subcommand to run the VNet MacOS
// daemon.
type vnetDaemonCommand struct {
*kingpin.CmdClause
// Launch daemons added through SMAppService are launched from a static .plist file, hence
// why this command does not accept any arguments.
// Instead, the daemon expects the arguments to be sent over XPC from an unprivileged process.
}

func newVnetDaemonCommand(app *kingpin.Application) *vnetDaemonCommand {
func newPlatformVnetDaemonCommand(app *kingpin.Application) *vnetDaemonCommand {
return &vnetDaemonCommand{
CmdClause: app.Command(vnetDaemonSubCommand, "Start the VNet daemon").Hidden(),
}
Expand Down
50 changes: 16 additions & 34 deletions tool/tsh/common/vnet_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package common

import (
"fmt"
"os"

"github.com/alecthomas/kingpin/v2"
Expand All @@ -29,38 +28,6 @@ import (
"github.com/gravitational/teleport/lib/vnet/daemon"
)

type vnetCommand struct {
*kingpin.CmdClause
}

func newVnetCommand(app *kingpin.Application) *vnetCommand {
cmd := &vnetCommand{
CmdClause: app.Command("vnet", "Start Teleport VNet, a virtual network for TCP application access."),
}
return cmd
}

func (c *vnetCommand) run(cf *CLIConf) error {
appProvider, err := newVnetAppProvider(cf)
if err != nil {
return trace.Wrap(err)
}

processManager, err := vnet.Run(cf.Context, &vnet.RunConfig{AppProvider: appProvider})
if err != nil {
return trace.Wrap(err)
}

go func() {
<-cf.Context.Done()
processManager.Close()
}()

fmt.Println("VNet is ready.")

return trace.Wrap(processManager.Wait())
}

// vnetAdminSetupCommand is the fallback command ran as root when tsh wasn't compiled with the
// vnetdaemon build tag. This is typically the case when running tsh in development where it's not
// signed and bundled in tsh.app.
Expand All @@ -83,7 +50,7 @@ type vnetAdminSetupCommand struct {
euid int
}

func newVnetAdminSetupCommand(app *kingpin.Application) *vnetAdminSetupCommand {
func newPlatformVnetAdminSetupCommand(app *kingpin.Application) *vnetAdminSetupCommand {
cmd := &vnetAdminSetupCommand{
CmdClause: app.Command(teleport.VnetAdminSetupSubCommand, "Start the VNet admin subprocess.").Hidden(),
}
Expand Down Expand Up @@ -116,3 +83,18 @@ func (c *vnetAdminSetupCommand) run(cf *CLIConf) error {

return trace.Wrap(vnet.RunAdminProcess(cf.Context, config))
}

// the vnet-install-service command is only supported on windows.
func newPlatformVnetInstallServiceCommand(app *kingpin.Application) vnetCommandNotSupported {
return vnetCommandNotSupported{}
}

// the vnet-uninstall-service command is only supported on windows.
func newPlatformVnetUninstallServiceCommand(app *kingpin.Application) vnetCommandNotSupported {
return vnetCommandNotSupported{}
}

// the vnet-service command is only supported on windows.
func newPlatformVnetServiceCommand(app *kingpin.Application) vnetCommandNotSupported {
return vnetCommandNotSupported{}
}
15 changes: 3 additions & 12 deletions tool/tsh/common/vnet_nodaemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,9 @@ package common

import (
"github.com/alecthomas/kingpin/v2"
"github.com/gravitational/trace"
)

func newVnetDaemonCommand(app *kingpin.Application) vnetDaemonNotSupported {
return vnetDaemonNotSupported{}
}

type vnetDaemonNotSupported struct{}

func (vnetDaemonNotSupported) FullCommand() string {
return ""
}
func (vnetDaemonNotSupported) run(*CLIConf) error {
return trace.NotImplemented("tsh was built without support for VNet daemon")
// The vnet-daemon command is only supported with the vnetdaemon tag on darwin.
func newPlatformVnetDaemonCommand(app *kingpin.Application) vnetCommandNotSupported {
return vnetCommandNotSupported{}
}
37 changes: 15 additions & 22 deletions tool/tsh/common/vnet_other.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build !darwin && !windows
// +build !darwin,!windows

// Teleport
// Copyright (C) 2024 Gravitational, Inc.
//
Expand All @@ -17,34 +14,30 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

//go:build !darwin && !windows
// +build !darwin,!windows

package common

import (
"github.com/alecthomas/kingpin/v2"
"github.com/gravitational/trace"

"github.com/gravitational/teleport/lib/vnet"
)

func newVnetCommand(app *kingpin.Application) vnetNotSupported {
return vnetNotSupported{}
}
// Satisfy unused linter.
var _ = newVnetAppProvider

func newVnetAdminSetupCommand(app *kingpin.Application) vnetNotSupported {
return vnetNotSupported{}
func newPlatformVnetAdminSetupCommand(app *kingpin.Application) vnetCLICommand {
return vnetCommandNotSupported{}
}

type vnetNotSupported struct{}

func (vnetNotSupported) FullCommand() string {
return ""
func newPlatformVnetInstallServiceCommand(app *kingpin.Application) vnetCLICommand {
return vnetCommandNotSupported{}
}
func (vnetNotSupported) run(*CLIConf) error {
return trace.Wrap(vnet.ErrVnetNotImplemented)

func newPlatformVnetUninstallServiceCommand(app *kingpin.Application) vnetCLICommand {
return vnetCommandNotSupported{}
}

var (
// Satisfy unused linter.
_ = (*vnetAppProvider)(nil)
_ = newVnetAppProvider
)
func newPlatformVnetServiceCommand(app *kingpin.Application) vnetCLICommand {
return vnetCommandNotSupported{}
}
Loading

0 comments on commit 032df73

Please sign in to comment.