-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[vnet] linux support #63664
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] linux support #63664
Changes from all commits
c4aa90e
7807d29
fea6ed0
c86be2e
ec7bca2
7d06ab6
3e32eb4
3d21612
d529c8c
e1bc30b
eff094f
06a8c22
ff7dee8
d026ba8
dd0eebb
ed0c82d
aea5805
bb18843
706f21d
f192f39
2579159
d8b7d0c
9a1753c
214f135
9dbf3f8
2cdddd3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| } | ||
|
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) | ||
| } | ||
| 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") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we intentionally returning an error for a clean exit?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||
|
tangyatsu marked this conversation as resolved.
|
||
| } | ||
| return dev, name, nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.