Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vnet] windows service stub #50468

Merged
merged 3 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/teleterm/vnet/service.go
codingllama marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (s *Service) Start(ctx context.Context, req *api.StartRequest) (*api.StartR
}

s.clusterConfigCache = vnet.NewClusterConfigCache(s.cfg.Clock)
processManager, err := vnet.Run(ctx, &vnet.RunConfig{
processManager, err := vnet.RunUserProcess(ctx, &vnet.UserProcessConfig{
AppProvider: appProvider,
ClusterConfigCache: s.clusterConfigCache,
})
Expand Down
25 changes: 12 additions & 13 deletions lib/vnet/admin_process.go → lib/vnet/admin_process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,17 @@ import (
"github.com/gravitational/teleport/lib/vnet/daemon"
)

// RunAdminProcess must run as root. It creates and sets up a TUN device and passes
// the file descriptor for that device over the unix socket found at config.socketPath.
// RunDarwinAdminProcess must run as root. It creates and sets up a TUN device
// and passes the file descriptor for that device over the unix socket found at
// config.socketPath.
//
// It also handles host OS configuration that must run as root, and stays alive to keep the host configuration
// up to date. It will stay running until the socket at config.socketPath is deleted or until encountering an
// unrecoverable error.
//
// OS configuration is updated every [osConfigurationInterval]. During the update, it temporarily
// changes egid and euid of the process to that of the client connecting to the daemon.
func RunAdminProcess(ctx context.Context, config daemon.Config) error {
// It also handles host OS configuration that must run as root, and stays alive
// to keep the host configuration up to date. It will stay running until the
// socket at config.socketPath is deleted, ctx is canceled, or until
// encountering an unrecoverable error.
func RunDarwinAdminProcess(ctx context.Context, config daemon.Config) error {
if err := config.CheckAndSetDefaults(); err != nil {
return trace.Wrap(err)
return trace.Wrap(err, "checking daemon process config")
}

ctx, cancel := context.WithCancel(ctx)
Expand Down Expand Up @@ -74,7 +73,7 @@ func RunAdminProcess(ctx context.Context, config daemon.Config) error {
}

// createAndSendTUNDevice creates a virtual network TUN device and sends the open file descriptor on
// [socketPath]. It returns the name of the TUN device or an error.
// socketPath. It returns the name of the TUN device or an error.
func createAndSendTUNDevice(ctx context.Context, socketPath string) (string, error) {
tun, tunName, err := createTUNDevice(ctx)
if err != nil {
Expand Down Expand Up @@ -107,7 +106,7 @@ func createTUNDevice(ctx context.Context) (tun.Device, string, error) {
return dev, name, nil
}

// osConfigurationLoop will keep running until [ctx] is canceled or an unrecoverable error is encountered, in
// osConfigurationLoop will keep running until ctx] is canceled or an unrecoverable error is encountered, in
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
// order to keep the host OS configuration up to date.
func osConfigurationLoop(ctx context.Context, tunName, ipv6Prefix, dnsAddr, homePath string, clientCred daemon.ClientCred) error {
osConfigurator, err := newOSConfigurator(tunName, ipv6Prefix, dnsAddr, homePath, clientCred)
Expand All @@ -128,7 +127,7 @@ func osConfigurationLoop(ctx context.Context, tunName, ipv6Prefix, dnsAddr, home
}

defer func() {
// Shutting down, deconfigure OS. Pass context.Background because [ctx] has likely been canceled
// Shutting down, deconfigure OS. Pass context.Background because ctx has likely been canceled
// already but we still need to clean up.
if err := osConfigurator.deconfigureOS(context.Background()); err != nil {
log.ErrorContext(ctx, "Error deconfiguring host OS before shutting down.", "error", err)
Expand Down
40 changes: 20 additions & 20 deletions lib/vnet/socket_other.go → lib/vnet/admin_process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,32 @@
// 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 vnet

import (
"os"
"context"

"github.com/gravitational/trace"
"golang.zx2c4.com/wireguard/tun"
)

func createSocket() (*noSocket, string, error) {
return nil, "", trace.Wrap(ErrVnetNotImplemented)
}

func sendTUNNameAndFd(socketPath, tunName string, tunFile *os.File) error {
return trace.Wrap(ErrVnetNotImplemented)
}

func receiveTUNDevice(_ *noSocket) (tun.Device, error) {
return nil, trace.Wrap(ErrVnetNotImplemented)
}

type noSocket struct{}

func (_ noSocket) Close() error {
return trace.Wrap(ErrVnetNotImplemented)
// runWindowsAdminProcess must run as administrator. It creates and sets up a TUN
// device, runs the VNet networking stack, and handles OS configuration. It will
// continue to run until [ctx] is canceled or encountering an unrecoverable
// error.
func runWindowsAdminProcess(ctx context.Context) error {
device, err := tun.CreateTUN("TeleportVNet", mtu)
if err != nil {
return trace.Wrap(err, "creating TUN device")
}
defer device.Close()
tunName, err := device.Name()
if err != nil {
return trace.Wrap(err, "getting TUN device name")
}
log.InfoContext(ctx, "Created TUN interface", "tun", tunName)
// TODO(nklaassen): actually run VNet. For now, just stay alive until the
// context is canceled.
<-ctx.Done()
return trace.Wrap(ctx.Err())
}
2 changes: 1 addition & 1 deletion lib/vnet/escalate_daemon_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ func execAdminProcess(ctx context.Context, config daemon.Config) error {

// DaemonSubcommand runs the VNet daemon process.
func DaemonSubcommand(ctx context.Context) error {
return trace.Wrap(daemon.Start(ctx, RunAdminProcess))
return trace.Wrap(daemon.Start(ctx, RunDarwinAdminProcess))
codingllama marked this conversation as resolved.
Show resolved Hide resolved
}
40 changes: 0 additions & 40 deletions lib/vnet/escalate_other.go

This file was deleted.

40 changes: 0 additions & 40 deletions lib/vnet/escalate_windows.go
nklaassen marked this conversation as resolved.
Outdated
Show resolved Hide resolved

This file was deleted.

3 changes: 3 additions & 0 deletions lib/vnet/network_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ import (
"gvisor.dev/gvisor/pkg/waiter"

"github.com/gravitational/teleport"
logutils "github.com/gravitational/teleport/lib/utils/log"
"github.com/gravitational/teleport/lib/vnet/dns"
)

var log = logutils.NewPackageLogger(teleport.ComponentKey, "vnet")

const (
nicID = 1
mtu = 1500
Expand Down
5 changes: 5 additions & 0 deletions lib/vnet/osconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
// 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/>.

// TODO(nklaassen): refactor OS configuration so this file isn't
// platform-specific.
//go:build darwin
// +build darwin

package vnet

import (
Expand Down
36 changes: 0 additions & 36 deletions lib/vnet/osconfig_windows.go

This file was deleted.

90 changes: 90 additions & 0 deletions lib/vnet/process_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Teleport
// Copyright (C) 2024 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 vnet

import (
"context"
"errors"
"fmt"
"sync"

"github.com/gravitational/trace"
"golang.org/x/sync/errgroup"
)

func newProcessManager() (*ProcessManager, context.Context) {
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
pm := &ProcessManager{
g: g,
cancel: cancel,
closed: make(chan struct{}),
}
pm.closeOnce = sync.OnceFunc(func() {
close(pm.closed)
})
return pm, ctx
}

// ProcessManager handles background tasks needed to run VNet.
// Its semantics are similar to an error group with a context, but it cancels the context whenever
// any task returns prematurely, that is, a task exits while the context was not canceled.
type ProcessManager struct {
codingllama marked this conversation as resolved.
Show resolved Hide resolved
g *errgroup.Group
cancel context.CancelFunc
closed chan struct{}
closeOnce func()
}

// AddCriticalBackgroundTask adds a function to the error group. [task] is expected to block until
// the context returned by [newProcessManager] gets canceled. The context gets canceled either by
// calling Close on [ProcessManager] or if any task returns.
func (pm *ProcessManager) AddCriticalBackgroundTask(name string, task func() error) {
pm.g.Go(func() error {
err := task()
if err == nil {
// Make sure to always return an error so that the errgroup context is canceled.
err = fmt.Errorf("critical task %q exited prematurely", name)
}
return trace.Wrap(err)
})
}

// Wait blocks and waits for the background tasks to finish, which typically happens when another
// goroutine calls Close on the process manager.
func (pm *ProcessManager) Wait() error {
err := pm.g.Wait()
select {
case <-pm.closed:
// Errors are expected after the process manager has been closed,
nklaassen marked this conversation as resolved.
Show resolved Hide resolved
// usually due to context cancellation, but other error types may be
// returned. Log unexpected errors at debug level but return nil.
if err != nil && !errors.Is(err, context.Canceled) {
log.DebugContext(context.Background(), "ProcessManager exited with error after being closed", "error", err)
}
return nil
default:
return trace.Wrap(err)
}
}

// Close stops any active background tasks by canceling the underlying context,
// and waits for all tasks to terminate.
func (pm *ProcessManager) Close() {
pm.closeOnce()
pm.cancel()
}
4 changes: 1 addition & 3 deletions lib/vnet/process_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ func TestProcessManager_Close(t *testing.T) {
})

pm.Close()

err := pm.Wait()
require.ErrorIs(t, err, context.Canceled)
require.ErrorIs(t, err, context.Cause(pmCtx))
require.NoError(t, err)
}
Loading
Loading