Skip to content

Commit 95e7c3b

Browse files
committed
[vnet] run networking stack on Windows
1 parent 9cd96ef commit 95e7c3b

File tree

6 files changed

+168
-22
lines changed

6 files changed

+168
-22
lines changed

lib/vnet/admin_process_windows.go

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,34 @@ package vnet
1818

1919
import (
2020
"context"
21+
"time"
2122

2223
"github.com/gravitational/trace"
24+
"github.com/jonboulle/clockwork"
2325
"golang.zx2c4.com/wireguard/tun"
2426
)
2527

28+
type windowsAdminProcessConfig struct {
29+
clientApplicationServiceAddr string
30+
}
31+
2632
// runWindowsAdminProcess must run as administrator. It creates and sets up a TUN
2733
// device, runs the VNet networking stack, and handles OS configuration. It will
2834
// continue to run until [ctx] is canceled or encountering an unrecoverable
2935
// error.
30-
func runWindowsAdminProcess(ctx context.Context) error {
36+
func runWindowsAdminProcess(ctx context.Context, cfg *windowsAdminProcessConfig) error {
37+
log.InfoContext(ctx, "Running VNet admin process")
38+
39+
clt, err := newClientApplicationServiceClient(ctx, cfg.clientApplicationServiceAddr)
40+
if err != nil {
41+
return trace.Wrap(err, "creating user process client")
42+
}
43+
defer clt.close()
44+
45+
if err := authenticateUserProcess(ctx, clt); err != nil {
46+
return trace.Wrap(err, "authenticating user process")
47+
}
48+
3149
device, err := tun.CreateTUN("TeleportVNet", mtu)
3250
if err != nil {
3351
return trace.Wrap(err, "creating TUN device")
@@ -38,8 +56,58 @@ func runWindowsAdminProcess(ctx context.Context) error {
3856
return trace.Wrap(err, "getting TUN device name")
3957
}
4058
log.InfoContext(ctx, "Created TUN interface", "tun", tunName)
41-
// TODO(nklaassen): actually run VNet. For now, just stay alive until the
42-
// context is canceled.
43-
<-ctx.Done()
44-
return trace.Wrap(ctx.Err())
59+
60+
networkStackConfig, err := newWindowsNetworkStackConfig(device, clt)
61+
if err != nil {
62+
return trace.Wrap(err, "creating network stack config")
63+
}
64+
networkStack, err := newNetworkStack(networkStackConfig)
65+
if err != nil {
66+
return trace.Wrap(err, "creating network stack")
67+
}
68+
69+
ctx, cancel := context.WithCancel(ctx)
70+
defer cancel()
71+
errCh := make(chan error)
72+
go func() {
73+
errCh <- trace.Wrap(networkStack.run(ctx), "running network stack")
74+
}()
75+
loop:
76+
for {
77+
select {
78+
case <-time.After(time.Second):
79+
if err := clt.Ping(ctx); err != nil {
80+
log.InfoContext(ctx, "Failed to ping client application, it may have exited, shutting down", "error", err)
81+
break loop
82+
}
83+
case <-ctx.Done():
84+
log.InfoContext(ctx, "Context canceled, shutting down", "error", err)
85+
break loop
86+
}
87+
}
88+
// Cancel the context and wait for networkStack.run to terminate.
89+
cancel()
90+
err = <-errCh
91+
return trace.Wrap(err, "running VNet network stack")
92+
}
93+
94+
func newWindowsNetworkStackConfig(tun tunDevice, clt *clientApplicationServiceClient) (*networkStackConfig, error) {
95+
appProvider := newRemoteAppProvider(clt)
96+
appResolver := newTCPAppResolver(appProvider, clockwork.NewRealClock())
97+
ipv6Prefix, err := NewIPv6Prefix()
98+
if err != nil {
99+
return nil, trace.Wrap(err, "creating new IPv6 prefix")
100+
}
101+
dnsIPv6 := ipv6WithSuffix(ipv6Prefix, []byte{2})
102+
return &networkStackConfig{
103+
tunDevice: tun,
104+
ipv6Prefix: ipv6Prefix,
105+
dnsIPv6: dnsIPv6,
106+
tcpHandlerResolver: appResolver,
107+
}, nil
108+
}
109+
110+
func authenticateUserProcess(ctx context.Context, clt *clientApplicationServiceClient) error {
111+
// TODO(nklaassen): implement process authentication.
112+
return nil
45113
}

lib/vnet/client_application_service_client.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type clientApplicationServiceClient struct {
3737
}
3838

3939
func newClientApplicationServiceClient(ctx context.Context, addr string) (*clientApplicationServiceClient, error) {
40+
// TODO(nklaassen): add mTLS credentials for client application service.
4041
conn, err := grpc.NewClient(addr,
4142
grpc.WithTransportCredentials(insecure.NewCredentials()),
4243
grpc.WithUnaryInterceptor(interceptors.GRPCClientUnaryErrorInterceptor),
@@ -58,7 +59,7 @@ func (c *clientApplicationServiceClient) close() error {
5859
// Ping pings the client application.
5960
func (c *clientApplicationServiceClient) Ping(ctx context.Context) error {
6061
if _, err := c.clt.Ping(ctx, &vnetv1.PingRequest{}); err != nil {
61-
return trace.Wrap(err, "pinging client application")
62+
return trace.Wrap(err, "calling Ping rpc")
6263
}
6364
return nil
6465
}
@@ -70,7 +71,7 @@ func (c *clientApplicationServiceClient) AuthenticateProcess(ctx context.Context
7071
PipePath: pipePath,
7172
})
7273
if err != nil {
73-
return trace.Wrap(err, "authenticating process")
74+
return trace.Wrap(err, "calling AuthenticateProcess rpc")
7475
}
7576
if resp.Version != api.Version {
7677
return trace.BadParameter("version mismatch, user process version is %s, admin process version is %s",
@@ -86,7 +87,7 @@ func (c *clientApplicationServiceClient) ResolveAppInfo(ctx context.Context, fqd
8687
Fqdn: fqdn,
8788
})
8889
if err != nil {
89-
return nil, trace.Wrap(err, "resolving app info")
90+
return nil, trace.Wrap(err, "calling ResolveAppInfo rpc")
9091
}
9192
return resp.GetAppInfo(), nil
9293
}
@@ -98,7 +99,7 @@ func (c *clientApplicationServiceClient) ReissueAppCert(ctx context.Context, app
9899
TargetPort: uint32(targetPort),
99100
})
100101
if err != nil {
101-
return nil, trace.Wrap(err, "reissuing app cert")
102+
return nil, trace.Wrap(err, "calling ReissueAppCert rpc")
102103
}
103104
return resp.GetCert(), nil
104105
}
@@ -108,7 +109,7 @@ func (c *clientApplicationServiceClient) ReissueAppCert(ctx context.Context, app
108109
func (c *clientApplicationServiceClient) SignForApp(ctx context.Context, req *vnetv1.SignForAppRequest) ([]byte, error) {
109110
resp, err := c.clt.SignForApp(ctx, req)
110111
if err != nil {
111-
return nil, trace.Wrap(err, "signing for app")
112+
return nil, trace.Wrap(err, "calling SignForApp rpc")
112113
}
113114
return resp.GetSignature(), nil
114115
}
@@ -119,7 +120,7 @@ func (c *clientApplicationServiceClient) OnNewConnection(ctx context.Context, ap
119120
AppKey: appKey,
120121
})
121122
if err != nil {
122-
return trace.Wrap(err)
123+
return trace.Wrap(err, "calling OnNewConnection rpc")
123124
}
124125
return nil
125126
}
@@ -132,7 +133,7 @@ func (c *clientApplicationServiceClient) OnInvalidLocalPort(ctx context.Context,
132133
TargetPort: uint32(targetPort),
133134
})
134135
if err != nil {
135-
return trace.Wrap(err)
136+
return trace.Wrap(err, "calling OnInvalidLocalPort rpc")
136137
}
137138
return nil
138139
}

lib/vnet/dns/osnameservers_other.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
//go:build !darwin
18-
// +build !darwin
17+
//go:build !darwin && !windows
18+
// +build !darwin,!windows
1919

2020
package dns
2121

lib/vnet/dns/osnameservers_windows.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Teleport
2+
// Copyright (C) 2024 Gravitational, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package dns
18+
19+
import "context"
20+
21+
type OSUpstreamNameserverSource struct{}
22+
23+
func NewOSUpstreamNameserverSource() (*OSUpstreamNameserverSource, error) {
24+
return &OSUpstreamNameserverSource{}, nil
25+
}
26+
27+
func (s *OSUpstreamNameserverSource) UpstreamNameservers(ctx context.Context) ([]string, error) {
28+
// TODO(nklaassen): implement UpstreamNameservers on windows.
29+
return nil, nil
30+
}

lib/vnet/service_windows.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"syscall"
2525
"time"
2626

27+
"github.com/alecthomas/kingpin/v2"
2728
"github.com/gravitational/trace"
2829
"golang.org/x/sys/windows"
2930
"golang.org/x/sys/windows/svc"
@@ -40,8 +41,8 @@ const (
4041
// runService is called from the normal user process to run the VNet Windows in
4142
// the background and wait for it to exit. It will terminate the service and
4243
// return immediately if [ctx] is canceled.
43-
func runService(ctx context.Context) error {
44-
service, err := startService(ctx)
44+
func runService(ctx context.Context, cfg *windowsAdminProcessConfig) error {
45+
service, err := startService(ctx, cfg)
4546
if err != nil {
4647
return trace.Wrap(err)
4748
}
@@ -69,7 +70,7 @@ func runService(ctx context.Context) error {
6970
}
7071

7172
// startService starts the Windows VNet admin service in the background.
72-
func startService(ctx context.Context) (*mgr.Service, error) {
73+
func startService(ctx context.Context, cfg *windowsAdminProcessConfig) (*mgr.Service, error) {
7374
// Avoid [mgr.Connect] because it requests elevated permissions.
7475
scManager, err := windows.OpenSCManager(nil /*machine*/, nil /*database*/, windows.SC_MANAGER_CONNECT)
7576
if err != nil {
@@ -88,7 +89,7 @@ func startService(ctx context.Context) (*mgr.Service, error) {
8889
Name: serviceName,
8990
Handle: serviceHandle,
9091
}
91-
if err := service.Start(ServiceCommand); err != nil {
92+
if err := service.Start(ServiceCommand, "--addr", cfg.clientApplicationServiceAddr); err != nil {
9293
return nil, trace.Wrap(err, "starting Windows service %s", serviceName)
9394
}
9495
return service, nil
@@ -157,7 +158,20 @@ loop:
157158
}
158159

159160
func (s *windowsService) run(ctx context.Context, args []string) error {
160-
if err := runWindowsAdminProcess(ctx); err != nil {
161+
var clientApplicationServiceAddr string
162+
app := kingpin.New(serviceName, "Teleport Windows Service")
163+
serviceCmd := app.Command("vnet-service", "Start the VNet service.")
164+
serviceCmd.Flag("addr", "client application service address").Required().StringVar(&clientApplicationServiceAddr)
165+
cmd, err := app.Parse(args[1:])
166+
if err != nil {
167+
return trace.Wrap(err, "parsing runtime arguments to Windows service")
168+
}
169+
if cmd != serviceCmd.FullCommand() {
170+
return trace.BadParameter("Windows service runtime arguments did not match \"vnet-service\", args: %v", args[1:])
171+
}
172+
if err := runWindowsAdminProcess(ctx, &windowsAdminProcessConfig{
173+
clientApplicationServiceAddr: clientApplicationServiceAddr,
174+
}); err != nil {
161175
return trace.Wrap(err, "running admin process")
162176
}
163177
return nil

lib/vnet/user_process_windows.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@ package vnet
1818

1919
import (
2020
"context"
21+
"net"
2122

2223
"github.com/gravitational/trace"
24+
"github.com/jonboulle/clockwork"
25+
"google.golang.org/grpc"
26+
"google.golang.org/grpc/credentials/insecure"
27+
28+
"github.com/gravitational/teleport/api/utils/grpc/interceptors"
29+
vnetv1 "github.com/gravitational/teleport/gen/proto/go/teleport/lib/vnet/v1"
2330
)
2431

2532
// runPlatformUserProcess launches a Windows service in the background that will
@@ -35,10 +42,36 @@ func runPlatformUserProcess(ctx context.Context, config *UserProcessConfig) (pm
3542
}
3643
}()
3744

45+
listener, err := net.Listen("tcp", ":0")
46+
if err != nil {
47+
return nil, trace.Wrap(err, "listening on tcp socket")
48+
}
3849
pm, processCtx := newProcessManager()
39-
pm.AddCriticalBackgroundTask("VNet Windows service", func() error {
40-
return trace.Wrap(runService(processCtx), "running VNet Windows service in the background")
50+
pm.AddCriticalBackgroundTask("tcp socket closer", func() error {
51+
<-processCtx.Done()
52+
return trace.Wrap(listener.Close())
53+
})
54+
pm.AddCriticalBackgroundTask("admin process", func() error {
55+
return trace.Wrap(runService(processCtx, &windowsAdminProcessConfig{
56+
clientApplicationServiceAddr: listener.Addr().String(),
57+
}))
58+
})
59+
pm.AddCriticalBackgroundTask("gRPC service", func() error {
60+
log.InfoContext(processCtx, "Starting gRPC service", "addr", listener.Addr().String())
61+
// TODO(nklaassen): add mTLS credentials for client application service.
62+
grpcServer := grpc.NewServer(
63+
grpc.Creds(insecure.NewCredentials()),
64+
grpc.UnaryInterceptor(interceptors.GRPCServerUnaryErrorInterceptor),
65+
grpc.StreamInterceptor(interceptors.GRPCServerStreamErrorInterceptor),
66+
)
67+
clock := clockwork.NewRealClock()
68+
appProvider := newLocalAppProvider(config.ClientApplication, clock)
69+
svc := newClientApplicationService(appProvider)
70+
vnetv1.RegisterClientApplicationServiceServer(grpcServer, svc)
71+
if err := grpcServer.Serve(listener); err != nil {
72+
return trace.Wrap(err, "serving VNet user process gRPC service")
73+
}
74+
return nil
4175
})
42-
// TODO(nklaassen): run user process gRPC service.
4376
return pm, nil
4477
}

0 commit comments

Comments
 (0)