Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
76aa49a
first build
nklaassen Jun 3, 2025
ee2eec0
add d-bus daemon for vnet
tangyatsu Jan 27, 2026
a4716d0
set up dns server via systemd-resolved d-bus api
tangyatsu Feb 9, 2026
079f75b
get DNS upstreams via systemd-resolved API
tangyatsu Feb 13, 2026
99b3494
never forward DNS requests to self
tangyatsu Feb 13, 2026
e07e87c
refactor: move all systemd-resolved logic to a separate package
tangyatsu Feb 13, 2026
f77a93d
refactor: move all polkit logic to a separate package
tangyatsu Feb 13, 2026
63f3b06
filter systemd-resolved loopback upstreams
tangyatsu Feb 19, 2026
ca2ab53
add scope for link-local IPv6 adresses for upstreams
tangyatsu Feb 19, 2026
3633e70
comment updates, small fixes, and typo cleanup
tangyatsu Feb 20, 2026
5edefdd
return uid from D-Bus authorize func
tangyatsu Feb 20, 2026
7733d30
add typed constants for systemd unit states
tangyatsu Feb 20, 2026
32c3268
avoid using canceled context for stopping VNet daemon
tangyatsu Feb 20, 2026
a6e3a2a
refactor: do not store ctx in dbusDaemon
tangyatsu Mar 11, 2026
5d3f9ba
Improve dbusDaemon lifecycle handling
ravicious Mar 12, 2026
61d39af
Log errors from `dbusDaemon.Wait`
ravicious Mar 12, 2026
a9af730
refactor: reuse shared darwin/linux code for admin process
tangyatsu Mar 12, 2026
8c8ba11
reintroduce isDiagSupported
tangyatsu Mar 12, 2026
bf2465e
refactor logging errors from dbusDaemon.Wait
tangyatsu Mar 12, 2026
27c0c4b
do not return error for missing TUN during deconfigure
tangyatsu Mar 12, 2026
1f8bad4
use d.conn.Context()
tangyatsu Mar 13, 2026
81cc965
avoid using bare slog logger
tangyatsu Mar 13, 2026
71d10af
avoid mutating cached upstream nameserver slices
tangyatsu Mar 13, 2026
b6ac1b6
fix race between dbus Start and Stop
tangyatsu Mar 13, 2026
05fef4f
small fixes
tangyatsu Mar 13, 2026
2255d4f
some refactoring
tangyatsu Mar 16, 2026
3e1f7fb
fix go mod tidy
tangyatsu Mar 17, 2026
edef658
Replace TCP socket with unix socket for VNet user-admin IPC for Linux…
tangyatsu Apr 1, 2026
9074652
Merge remote-tracking branch 'origin/branch/v18' into tangyatsu/vnet-…
tangyatsu Apr 17, 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 @@ -133,6 +133,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 @@ -428,7 +429,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)
}
47 changes: 47 additions & 0 deletions lib/vnet/admin_process_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 {
// ClientApplicationServiceSocketPath is the unix socket path of the client
// application service.
ClientApplicationServiceSocketPath string
}

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

clt, err := newUnixClientApplicationServiceClient(ctx, config.ClientApplicationServiceSocketPath)
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")
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")
}
return dev, name, nil
}
2 changes: 2 additions & 0 deletions lib/vnet/client_application_service_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type clientApplicationServiceClient struct {
conn *grpc.ClientConn
}

// newClientApplicationServiceClient creates a gRPC client over a TCP
// socket using mTLS credentials.
func newClientApplicationServiceClient(ctx context.Context, creds *credentials, addr string) (*clientApplicationServiceClient, error) {
tlsConfig, err := creds.clientTLSConfig()
if err != nil {
Expand Down
49 changes: 49 additions & 0 deletions lib/vnet/client_application_service_client_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Teleport
// Copyright (C) 2026 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"
"fmt"

"github.com/gravitational/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/gravitational/teleport/api/utils/grpc/interceptors"
vnetv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/vnet/v1"
)

// newUnixClientApplicationServiceClient creates a gRPC client over a Unix
// socket without TLS.
func newUnixClientApplicationServiceClient(_ context.Context, socketPath string) (*clientApplicationServiceClient, error) {
if socketPath == "" {
return nil, trace.BadParameter("missing unix socket path")
}
conn, err := grpc.NewClient(fmt.Sprintf("unix://%s", socketPath),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(interceptors.GRPCClientUnaryErrorInterceptor),
grpc.WithStreamInterceptor(interceptors.GRPCClientStreamErrorInterceptor),
)
if err != nil {
return nil, trace.Wrap(err, "creating user process gRPC client")
}
return &clientApplicationServiceClient{
clt: vnetv1.NewClientApplicationServiceClient(conn),
conn: conn,
}, nil
}
Loading
Loading