Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c4aa90e
first build
nklaassen Jun 3, 2025
7807d29
add d-bus daemon for vnet
tangyatsu Jan 27, 2026
fea6ed0
set up dns server via systemd-resolved d-bus api
tangyatsu Feb 9, 2026
c86be2e
get DNS upstreams via systemd-resolved API
tangyatsu Feb 13, 2026
ec7bca2
never forward DNS requests to self
tangyatsu Feb 13, 2026
7d06ab6
refactor: move all systemd-resolved logic to a separate package
tangyatsu Feb 13, 2026
3e32eb4
refactor: move all polkit logic to a separate package
tangyatsu Feb 13, 2026
3d21612
filter systemd-resolved loopback upstreams
tangyatsu Feb 19, 2026
d529c8c
add scope for link-local IPv6 adresses for upstreams
tangyatsu Feb 19, 2026
e1bc30b
comment updates, small fixes, and typo cleanup
tangyatsu Feb 20, 2026
eff094f
return uid from D-Bus authorize func
tangyatsu Feb 20, 2026
06a8c22
add typed constants for systemd unit states
tangyatsu Feb 20, 2026
ff7dee8
avoid using canceled context for stopping VNet daemon
tangyatsu Feb 20, 2026
d026ba8
refactor: do not store ctx in dbusDaemon
tangyatsu Mar 11, 2026
dd0eebb
Improve dbusDaemon lifecycle handling
ravicious Mar 12, 2026
ed0c82d
Log errors from `dbusDaemon.Wait`
ravicious Mar 12, 2026
aea5805
refactor: reuse shared darwin/linux code for admin process
tangyatsu Mar 12, 2026
bb18843
reintroduce isDiagSupported
tangyatsu Mar 12, 2026
706f21d
refactor logging errors from dbusDaemon.Wait
tangyatsu Mar 12, 2026
f192f39
do not return error for missing TUN during deconfigure
tangyatsu Mar 12, 2026
2579159
use d.conn.Context()
tangyatsu Mar 13, 2026
d8b7d0c
avoid using bare slog logger
tangyatsu Mar 13, 2026
9a1753c
avoid mutating cached upstream nameserver slices
tangyatsu Mar 13, 2026
214f135
fix race between dbus Start and Stop
tangyatsu Mar 13, 2026
9dbf3f8
small fixes
tangyatsu Mar 13, 2026
2cdddd3
some refactoring
tangyatsu Mar 16, 2026
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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ require (
github.com/go-webauthn/webauthn v0.11.2
github.com/gobwas/ws v1.4.0
github.com/gocql/gocql v1.7.0
github.com/godbus/dbus/v5 v5.2.2
github.com/gofrs/flock v0.13.0
github.com/gogo/protobuf v1.3.2 // replaced
github.com/golang-jwt/jwt/v4 v4.5.2
Expand Down Expand Up @@ -427,7 +428,6 @@ require (
github.com/gobwas/pool v0.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/godbus/dbus/v5 v5.2.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
Expand Down
108 changes: 5 additions & 103 deletions lib/vnet/admin_process_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@ package vnet

import (
"context"
"errors"
"os"
"time"

"github.com/gravitational/trace"
"golang.org/x/sync/errgroup"
"golang.zx2c4.com/wireguard/tun"

vnetv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/vnet/v1"
"github.com/gravitational/teleport/lib/vnet/daemon"
)

const (
tunInterfaceName = "utun"
)

// 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.
Expand All @@ -54,101 +52,5 @@ func RunDarwinAdminProcess(ctx context.Context, config daemon.Config) error {
}
defer clt.close()

tun, tunName, err := createTUNDevice(ctx)
if err != nil {
return trace.Wrap(err)
}
defer tun.Close()

networkStackConfig, err := newNetworkStackConfig(ctx, tun, clt)
if err != nil {
return trace.Wrap(err, "creating network stack config")
}
networkStack, err := newNetworkStack(networkStackConfig)
if err != nil {
return trace.Wrap(err, "creating network stack")
}

if err := clt.ReportNetworkStackInfo(ctx, &vnetv1.NetworkStackInfo{
InterfaceName: tunName,
Ipv6Prefix: networkStackConfig.ipv6Prefix.String(),
}); err != nil {
return trace.Wrap(err, "reporting network stack info to client application")
}

osConfigProvider, err := newOSConfigProvider(osConfigProviderConfig{
clt: clt,
tunName: tunName,
ipv6Prefix: networkStackConfig.ipv6Prefix.String(),
dnsIPv6: networkStackConfig.dnsIPv6.String(),
addDNSAddress: networkStack.addDNSAddress,
})
if err != nil {
return trace.Wrap(err, "creating OS config provider")
}
osConfigurator := newOSConfigurator(osConfigProvider)

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
defer log.InfoContext(ctx, "Network stack terminated.")
if err := networkStack.run(ctx); err != nil {
return trace.Wrap(err, "running network stack")
}
return errors.New("network stack terminated")
})
g.Go(func() error {
defer log.InfoContext(ctx, "OS configuration loop exited.")
if err := osConfigurator.runOSConfigurationLoop(ctx); err != nil {
return trace.Wrap(err, "running OS configuration loop")
}
return errors.New("OS configuration loop terminated")
})
g.Go(func() error {
defer log.InfoContext(ctx, "Ping loop exited.")
tick := time.Tick(time.Second)
for {
select {
case <-tick:
if err := clt.Ping(ctx); err != nil {
return trace.Wrap(err, "failed to ping client application, it may have exited, shutting down")
}
case <-ctx.Done():
return ctx.Err()
}
}
})

done := make(chan error)
go func() {
done <- g.Wait()
}()

select {
case err := <-done:
return trace.Wrap(err, "running VNet admin process")
case <-ctx.Done():
}

select {
case err := <-done:
// network stack exited cleanly within timeout
return trace.Wrap(err, "running VNet admin process")
case <-time.After(10 * time.Second):
log.ErrorContext(ctx, "VNet admin process did not exit within 10 seconds, forcing shutdown.")
os.Exit(1)
return nil
}
}

func createTUNDevice(ctx context.Context) (tun.Device, string, error) {
log.DebugContext(ctx, "Creating TUN device.")
dev, err := tun.CreateTUN("utun", mtu)
if err != nil {
return nil, "", trace.Wrap(err, "creating TUN device")
}
name, err := dev.Name()
if err != nil {
return nil, "", trace.Wrap(err, "getting TUN device name")
}
return dev, name, nil
return runUnixAdminProcess(ctx, clt, tunInterfaceName)
}
55 changes: 55 additions & 0 deletions lib/vnet/admin_process_linux.go
Comment thread
tangyatsu marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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"

"github.com/gravitational/trace"
)

const (
tunInterfaceName = "TeleportVNet"
)

// LinuxAdminProcessConfig configures RunLinuxAdminProcess.
type LinuxAdminProcessConfig struct {
// ClientApplicationServiceAddr is the address of the client application
// service the admin process connects to.
ClientApplicationServiceAddr string
// ServiceCredentialPath is the path to IPC credentials used to authenticate
// with the client application service.
ServiceCredentialPath string
}
Comment thread
tangyatsu marked this conversation as resolved.

// RunLinuxAdminProcess must run as root.
func RunLinuxAdminProcess(ctx context.Context, config LinuxAdminProcessConfig) error {
log.InfoContext(ctx, "Running VNet admin process")

serviceCreds, err := readCredentials(config.ServiceCredentialPath)
if err != nil {
return trace.Wrap(err, "reading service IPC credentials")
}
// TODO(tangyatsu): change to gRPC client over unix socket instead of TCP.
clt, err := newClientApplicationServiceClient(ctx, serviceCreds, config.ClientApplicationServiceAddr)
if err != nil {
return trace.Wrap(err, "creating user process client")
}
defer clt.close()

return runUnixAdminProcess(ctx, clt, tunInterfaceName)
}
133 changes: 133 additions & 0 deletions lib/vnet/admin_process_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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/>.

//go:build darwin || linux

package vnet

import (
"context"
"errors"
"os"
"time"

"github.com/gravitational/trace"
"golang.org/x/sync/errgroup"
"golang.zx2c4.com/wireguard/tun"

vnetv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/vnet/v1"
)

func runUnixAdminProcess(ctx context.Context, clt *clientApplicationServiceClient, tunInterfaceName string) error {
tun, tunName, err := createTUNDevice(ctx, tunInterfaceName)
if err != nil {
return trace.Wrap(err)
}
defer tun.Close()

networkStackConfig, err := newNetworkStackConfig(ctx, tun, clt)
if err != nil {
return trace.Wrap(err, "creating network stack config")
}
networkStack, err := newNetworkStack(networkStackConfig)
if err != nil {
return trace.Wrap(err, "creating network stack")
}

if err := clt.ReportNetworkStackInfo(ctx, &vnetv1.NetworkStackInfo{
InterfaceName: tunName,
Ipv6Prefix: networkStackConfig.ipv6Prefix.String(),
}); err != nil {
return trace.Wrap(err, "reporting network stack info to client application")
}

osConfigProvider, err := newOSConfigProvider(osConfigProviderConfig{
clt: clt,
tunName: tunName,
ipv6Prefix: networkStackConfig.ipv6Prefix.String(),
dnsIPv6: networkStackConfig.dnsIPv6.String(),
addDNSAddress: networkStack.addDNSAddress,
})
if err != nil {
return trace.Wrap(err, "creating OS config provider")
}
osConfigurator := newOSConfigurator(osConfigProvider)

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
defer log.InfoContext(ctx, "Network stack terminated.")
if err := networkStack.run(ctx); err != nil {
return trace.Wrap(err, "running network stack")
}
return errors.New("network stack terminated")
})
g.Go(func() error {
defer log.InfoContext(ctx, "OS configuration loop exited.")
if err := osConfigurator.runOSConfigurationLoop(ctx); err != nil {
return trace.Wrap(err, "running OS configuration loop")
}
return errors.New("OS configuration loop terminated")
})
g.Go(func() error {
defer log.InfoContext(ctx, "Ping loop exited.")
tick := time.Tick(time.Second)
for {
select {
case <-tick:
if err := clt.Ping(ctx); err != nil {
return trace.Wrap(err, "failed to ping client application, it may have exited, shutting down")
}
case <-ctx.Done():
return ctx.Err()
}
}
})

done := make(chan error)
go func() {
done <- g.Wait()
}()

select {
case err := <-done:
return trace.Wrap(err, "running VNet admin process")
case <-ctx.Done():
}

select {
case err := <-done:
// network stack exited cleanly within timeout
return trace.Wrap(err, "running VNet admin process")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we intentionally returning an error for a clean exit?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code was only in darwin version previously. my understanding is there was a bug where the network stack could hang on shutdown #58298, so “clean exit” here means it exited within the timeout window, regardless of whether it returned an error

case <-time.After(10 * time.Second):
log.ErrorContext(ctx, "VNet admin process did not exit within 10 seconds, forcing shutdown.")
os.Exit(1)
return nil
}
}

func createTUNDevice(ctx context.Context, interfaceName string) (tun.Device, string, error) {
log.DebugContext(ctx, "Creating TUN device.")
dev, err := tun.CreateTUN(interfaceName, mtu)
if err != nil {
return nil, "", trace.Wrap(err, "creating TUN device")
}
name, err := dev.Name()
if err != nil {
dev.Close()
return nil, "", trace.Wrap(err, "getting TUN device name")
Comment thread
tangyatsu marked this conversation as resolved.
}
return dev, name, nil
}
Loading
Loading